kdeui Library API Documentation

knuminput.cpp

00001 // -*- c-basic-offset: 4 -*- 00002 /* 00003 * knuminput.cpp 00004 * 00005 * Initial implementation: 00006 * Copyright (c) 1997 Patrick Dowler <dowler@morgul.fsh.uvic.ca> 00007 * Rewritten and maintained by: 00008 * Copyright (c) 2000 Dirk A. Mueller <mueller@kde.org> 00009 * KDoubleSpinBox: 00010 * Copyright (c) 2002 Marc Mutz <mutz@kde.org> 00011 * 00012 * Requires the Qt widget libraries, available at no cost at 00013 * http://www.troll.no/ 00014 * 00015 * This library is free software; you can redistribute it and/or 00016 * modify it under the terms of the GNU Library General Public 00017 * License as published by the Free Software Foundation; either 00018 * version 2 of the License, or (at your option) any later version. 00019 * 00020 * This library is distributed in the hope that it will be useful, 00021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00023 * Library General Public License for more details. 00024 * 00025 * You should have received a copy of the GNU Library General Public License 00026 * along with this library; see the file COPYING.LIB. If not, write to 00027 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00028 * Boston, MA 02111-1307, USA. 00029 */ 00030 00031 #include <config.h> 00032 #ifdef HAVE_LIMITS_H 00033 #include <limits.h> 00034 #endif 00035 #include <assert.h> 00036 #include <math.h> 00037 #include <algorithm> 00038 00039 #include <qapplication.h> 00040 #include <qlabel.h> 00041 #include <qlineedit.h> 00042 #include <qsize.h> 00043 #include <qslider.h> 00044 #include <qspinbox.h> 00045 #include <qstyle.h> 00046 00047 #include <kglobal.h> 00048 #include <klocale.h> 00049 #include <kdebug.h> 00050 00051 #include "kdialog.h" 00052 #include "knumvalidator.h" 00053 #include "knuminput.h" 00054 00055 static inline int calcDiffByTen( int x, int y ) { 00056 // calculate ( x - y ) / 10 without overflowing ints: 00057 return ( x / 10 ) - ( y / 10 ) + ( x % 10 - y % 10 ) / 10; 00058 } 00059 00060 // ---------------------------------------------------------------------------- 00061 00062 KNumInput::KNumInput(QWidget* parent, const char* name) 00063 : QWidget(parent, name) 00064 { 00065 init(); 00066 } 00067 00068 KNumInput::KNumInput(KNumInput* below, QWidget* parent, const char* name) 00069 : QWidget(parent, name) 00070 { 00071 init(); 00072 00073 if(below) { 00074 m_next = below->m_next; 00075 m_prev = below; 00076 below->m_next = this; 00077 if(m_next) 00078 m_next->m_prev = this; 00079 } 00080 } 00081 00082 void KNumInput::init() 00083 { 00084 m_prev = m_next = 0; 00085 m_colw1 = m_colw2 = 0; 00086 00087 m_label = 0; 00088 m_slider = 0; 00089 m_alignment = 0; 00090 } 00091 00092 KNumInput::~KNumInput() 00093 { 00094 if(m_prev) 00095 m_prev->m_next = m_next; 00096 00097 if(m_next) 00098 m_next->m_prev = m_prev; 00099 } 00100 00101 void KNumInput::setLabel(const QString & label, int a) 00102 { 00103 if(label.isEmpty()) { 00104 delete m_label; 00105 m_label = 0; 00106 m_alignment = 0; 00107 } 00108 else { 00109 if (m_label) m_label->setText(label); 00110 else m_label = new QLabel(label, this, "KNumInput::QLabel"); 00111 m_label->setAlignment((a & (~(AlignTop|AlignBottom|AlignVCenter))) 00112 | AlignVCenter); 00113 // if no vertical alignment set, use Top alignment 00114 if(!(a & (AlignTop|AlignBottom|AlignVCenter))) 00115 a |= AlignTop; 00116 m_alignment = a; 00117 } 00118 00119 layout(true); 00120 } 00121 00122 QString KNumInput::label() const 00123 { 00124 if (m_label) return m_label->text(); 00125 return QString::null; 00126 } 00127 00128 void KNumInput::layout(bool deep) 00129 { 00130 int w1 = m_colw1; 00131 int w2 = m_colw2; 00132 00133 // label sizeHint 00134 m_sizeLabel = (m_label ? m_label->sizeHint() : QSize(0,0)); 00135 00136 if(m_label && (m_alignment & AlignVCenter)) 00137 m_colw1 = m_sizeLabel.width() + 4; 00138 else 00139 m_colw1 = 0; 00140 00141 // slider sizeHint 00142 m_sizeSlider = (m_slider ? m_slider->sizeHint() : QSize(0, 0)); 00143 00144 doLayout(); 00145 00146 if(!deep) { 00147 m_colw1 = w1; 00148 m_colw2 = w2; 00149 return; 00150 } 00151 00152 KNumInput* p = this; 00153 while(p) { 00154 p->doLayout(); 00155 w1 = QMAX(w1, p->m_colw1); 00156 w2 = QMAX(w2, p->m_colw2); 00157 p = p->m_prev; 00158 } 00159 00160 p = m_next; 00161 while(p) { 00162 p->doLayout(); 00163 w1 = QMAX(w1, p->m_colw1); 00164 w2 = QMAX(w2, p->m_colw2); 00165 p = p->m_next; 00166 } 00167 00168 p = this; 00169 while(p) { 00170 p->m_colw1 = w1; 00171 p->m_colw2 = w2; 00172 p = p->m_prev; 00173 } 00174 00175 p = m_next; 00176 while(p) { 00177 p->m_colw1 = w1; 00178 p->m_colw2 = w2; 00179 p = p->m_next; 00180 } 00181 00182 // kdDebug() << "w1 " << w1 << " w2 " << w2 << endl; 00183 } 00184 00185 QSizePolicy KNumInput::sizePolicy() const 00186 { 00187 return QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ); 00188 } 00189 00190 QSize KNumInput::sizeHint() const 00191 { 00192 return minimumSizeHint(); 00193 } 00194 00195 void KNumInput::setSteps(int minor, int major) 00196 { 00197 if(m_slider) 00198 m_slider->setSteps( minor, major ); 00199 } 00200 00201 00202 // ---------------------------------------------------------------------------- 00203 00204 KIntSpinBox::KIntSpinBox(QWidget *parent, const char *name) 00205 : QSpinBox(0, 99, 1, parent, name) 00206 { 00207 editor()->setAlignment(AlignRight); 00208 val_base = 10; 00209 setValue(0); 00210 } 00211 00212 KIntSpinBox::~KIntSpinBox() 00213 { 00214 } 00215 00216 KIntSpinBox::KIntSpinBox(int lower, int upper, int step, int value, int base, 00217 QWidget* parent, const char* name) 00218 : QSpinBox(lower, upper, step, parent, name) 00219 { 00220 editor()->setAlignment(AlignRight); 00221 val_base = base; 00222 setValue(value); 00223 } 00224 00225 void KIntSpinBox::setBase(int base) 00226 { 00227 val_base = base; 00228 } 00229 00230 00231 int KIntSpinBox::base() const 00232 { 00233 return val_base; 00234 } 00235 00236 QString KIntSpinBox::mapValueToText(int v) 00237 { 00238 return QString::number(v, val_base); 00239 } 00240 00241 int KIntSpinBox::mapTextToValue(bool* ok) 00242 { 00243 return cleanText().toInt(ok, val_base); 00244 } 00245 00246 void KIntSpinBox::setEditFocus(bool mark) 00247 { 00248 editor()->setFocus(); 00249 if(mark) 00250 editor()->selectAll(); 00251 } 00252 00253 00254 // ---------------------------------------------------------------------------- 00255 00256 class KIntNumInput::KIntNumInputPrivate { 00257 public: 00258 int referencePoint; 00259 short blockRelative; 00260 KIntNumInputPrivate( int r ) 00261 : referencePoint( r ), 00262 blockRelative( 0 ) {} 00263 }; 00264 00265 00266 KIntNumInput::KIntNumInput(KNumInput* below, int val, QWidget* parent, 00267 int _base, const char* name) 00268 : KNumInput(below, parent, name) 00269 { 00270 init(val, _base); 00271 } 00272 00273 KIntNumInput::KIntNumInput(QWidget *parent, const char *name) 00274 : KNumInput(parent, name) 00275 { 00276 init(0, 10); 00277 } 00278 00279 KIntNumInput::KIntNumInput(int val, QWidget *parent, int _base, const char *name) 00280 : KNumInput(parent, name) 00281 { 00282 init(val, _base); 00283 00284 } 00285 00286 void KIntNumInput::init(int val, int _base) 00287 { 00288 d = new KIntNumInputPrivate( val ); 00289 m_spin = new KIntSpinBox(INT_MIN, INT_MAX, 1, val, _base, this, "KIntNumInput::KIntSpinBox"); 00290 // the KIntValidator is broken beyond believe for 00291 // spinboxes which have suffix or prefix texts, so 00292 // better don't use it unless absolutely necessary 00293 if (_base != 10) 00294 m_spin->setValidator(new KIntValidator(this, _base, "KNumInput::KIntValidtr")); 00295 00296 connect(m_spin, SIGNAL(valueChanged(int)), SLOT(spinValueChanged(int))); 00297 connect(this, SIGNAL(valueChanged(int)), 00298 SLOT(slotEmitRelativeValueChanged(int))); 00299 00300 setFocusProxy(m_spin); 00301 layout(true); 00302 } 00303 00304 void KIntNumInput::setReferencePoint( int ref ) { 00305 // clip to valid range: 00306 ref = kMin( maxValue(), kMax( minValue(), ref ) ); 00307 d->referencePoint = ref; 00308 } 00309 00310 int KIntNumInput::referencePoint() const { 00311 return d->referencePoint; 00312 } 00313 00314 void KIntNumInput::spinValueChanged(int val) 00315 { 00316 if(m_slider) 00317 m_slider->setValue(val); 00318 00319 emit valueChanged(val); 00320 } 00321 00322 void KIntNumInput::slotEmitRelativeValueChanged( int value ) { 00323 if ( d->blockRelative || !d->referencePoint ) return; 00324 emit relativeValueChanged( double( value ) / double( d->referencePoint ) ); 00325 } 00326 00327 void KIntNumInput::setRange(int lower, int upper, int step, bool slider) 00328 { 00329 upper = kMax(upper, lower); 00330 lower = kMin(upper, lower); 00331 m_spin->setMinValue(lower); 00332 m_spin->setMaxValue(upper); 00333 m_spin->setLineStep(step); 00334 00335 step = m_spin->lineStep(); // maybe QRangeControl didn't like out lineStep? 00336 00337 if(slider) { 00338 if (m_slider) 00339 m_slider->setRange(lower, upper); 00340 else { 00341 m_slider = new QSlider(lower, upper, step, m_spin->value(), 00342 QSlider::Horizontal, this); 00343 m_slider->setTickmarks(QSlider::Below); 00344 connect(m_slider, SIGNAL(valueChanged(int)), 00345 m_spin, SLOT(setValue(int))); 00346 } 00347 00348 // calculate (upper-lower)/10 without overflowing int's: 00349 int major = calcDiffByTen( upper, lower ); 00350 if ( major==0 ) major = step; // #### workaround Qt bug in 2.1-beta4 00351 00352 m_slider->setSteps(step, major); 00353 m_slider->setTickInterval(major); 00354 } 00355 else { 00356 delete m_slider; 00357 m_slider = 0; 00358 } 00359 00360 // check that reference point is still inside valid range: 00361 setReferencePoint( referencePoint() ); 00362 00363 layout(true); 00364 } 00365 00366 void KIntNumInput::setMinValue(int min) 00367 { 00368 setRange(min, m_spin->maxValue(), m_spin->lineStep(), m_slider); 00369 } 00370 00371 int KIntNumInput::minValue() const 00372 { 00373 return m_spin->minValue(); 00374 } 00375 00376 void KIntNumInput::setMaxValue(int max) 00377 { 00378 setRange(m_spin->minValue(), max, m_spin->lineStep(), m_slider); 00379 } 00380 00381 int KIntNumInput::maxValue() const 00382 { 00383 return m_spin->maxValue(); 00384 } 00385 00386 void KIntNumInput::setSuffix(const QString &suffix) 00387 { 00388 m_spin->setSuffix(suffix); 00389 00390 layout(true); 00391 } 00392 00393 QString KIntNumInput::suffix() const 00394 { 00395 return m_spin->suffix(); 00396 } 00397 00398 void KIntNumInput::setPrefix(const QString &prefix) 00399 { 00400 m_spin->setPrefix(prefix); 00401 00402 layout(true); 00403 } 00404 00405 QString KIntNumInput::prefix() const 00406 { 00407 return m_spin->prefix(); 00408 } 00409 00410 void KIntNumInput::setEditFocus(bool mark) 00411 { 00412 m_spin->setEditFocus(mark); 00413 } 00414 00415 QSize KIntNumInput::minimumSizeHint() const 00416 { 00417 constPolish(); 00418 00419 int w; 00420 int h; 00421 00422 h = 2 + QMAX(m_sizeSpin.height(), m_sizeSlider.height()); 00423 00424 // if in extra row, then count it here 00425 if(m_label && (m_alignment & (AlignBottom|AlignTop))) 00426 h += 4 + m_sizeLabel.height(); 00427 else 00428 // label is in the same row as the other widgets 00429 h = QMAX(h, m_sizeLabel.height() + 2); 00430 00431 w = m_slider ? m_slider->sizeHint().width() + 8 : 0; 00432 w += m_colw1 + m_colw2; 00433 00434 if(m_alignment & (AlignTop|AlignBottom)) 00435 w = QMAX(w, m_sizeLabel.width() + 4); 00436 00437 return QSize(w, h); 00438 } 00439 00440 void KIntNumInput::doLayout() 00441 { 00442 m_sizeSpin = m_spin->sizeHint(); 00443 m_colw2 = m_sizeSpin.width(); 00444 00445 if (m_label) 00446 m_label->setBuddy(m_spin); 00447 } 00448 00449 void KIntNumInput::resizeEvent(QResizeEvent* e) 00450 { 00451 int w = m_colw1; 00452 int h = 0; 00453 00454 if(m_label && (m_alignment & AlignTop)) { 00455 m_label->setGeometry(0, 0, e->size().width(), m_sizeLabel.height()); 00456 h += m_sizeLabel.height() + KDialog::spacingHint(); 00457 } 00458 00459 if(m_label && (m_alignment & AlignVCenter)) 00460 m_label->setGeometry(0, 0, w, m_sizeSpin.height()); 00461 00462 if (qApp->reverseLayout()) 00463 { 00464 m_spin->setGeometry(w, h, m_slider ? m_colw2 : QMAX(m_colw2, e->size().width() - w), m_sizeSpin.height()); 00465 w += m_colw2 + 8; 00466 00467 if(m_slider) 00468 m_slider->setGeometry(w, h, e->size().width() - w, m_sizeSpin.height()); 00469 } 00470 else if(m_slider) { 00471 m_slider->setGeometry(w, h, e->size().width() - (w + m_colw2 + KDialog::spacingHint()), m_sizeSpin.height()); 00472 m_spin->setGeometry(w + m_slider->size().width() + KDialog::spacingHint(), h, m_colw2, m_sizeSpin.height()); 00473 } 00474 else { 00475 m_spin->setGeometry(w, h, QMAX(m_colw2, e->size().width() - w), m_sizeSpin.height()); 00476 } 00477 00478 h += m_sizeSpin.height() + 2; 00479 00480 if(m_label && (m_alignment & AlignBottom)) 00481 m_label->setGeometry(0, h, m_sizeLabel.width(), m_sizeLabel.height()); 00482 } 00483 00484 KIntNumInput::~KIntNumInput() 00485 { 00486 delete d; 00487 } 00488 00489 void KIntNumInput::setValue(int val) 00490 { 00491 m_spin->setValue(val); 00492 // slider value is changed by spinValueChanged 00493 } 00494 00495 void KIntNumInput::setRelativeValue( double r ) { 00496 if ( !d->referencePoint ) return; 00497 ++d->blockRelative; 00498 setValue( int( d->referencePoint * r + 0.5 ) ); 00499 --d->blockRelative; 00500 } 00501 00502 double KIntNumInput::relativeValue() const { 00503 if ( !d->referencePoint ) return 0; 00504 return double( value() ) / double ( d->referencePoint ); 00505 } 00506 00507 int KIntNumInput::value() const 00508 { 00509 return m_spin->value(); 00510 } 00511 00512 void KIntNumInput::setSpecialValueText(const QString& text) 00513 { 00514 m_spin->setSpecialValueText(text); 00515 layout(true); 00516 } 00517 00518 QString KIntNumInput::specialValueText() const 00519 { 00520 return m_spin->specialValueText(); 00521 } 00522 00523 void KIntNumInput::setLabel(const QString & label, int a) 00524 { 00525 KNumInput::setLabel(label, a); 00526 00527 if(m_label) 00528 m_label->setBuddy(m_spin); 00529 } 00530 00531 // ---------------------------------------------------------------------------- 00532 00533 class KDoubleNumInput::KDoubleNumInputPrivate { 00534 public: 00535 KDoubleNumInputPrivate( double r ) 00536 : spin( 0 ), 00537 referencePoint( r ), 00538 blockRelative ( 0 ) {} 00539 KDoubleSpinBox * spin; 00540 double referencePoint; 00541 short blockRelative; 00542 }; 00543 00544 KDoubleNumInput::KDoubleNumInput(QWidget *parent, const char *name) 00545 : KNumInput(parent, name) 00546 { 00547 init(0.0, 0.0, 9999.0, 0.01, 2); 00548 } 00549 00550 KDoubleNumInput::KDoubleNumInput(double lower, double upper, double value, 00551 double step, int precision, QWidget* parent, 00552 const char *name) 00553 : KNumInput(parent, name) 00554 { 00555 init(value, lower, upper, step, precision); 00556 } 00557 00558 KDoubleNumInput::KDoubleNumInput(KNumInput *below, 00559 double lower, double upper, double value, 00560 double step, int precision, QWidget* parent, 00561 const char *name) 00562 : KNumInput(below, parent, name) 00563 { 00564 init(value, lower, upper, step, precision); 00565 } 00566 00567 KDoubleNumInput::KDoubleNumInput(double value, QWidget *parent, const char *name) 00568 : KNumInput(parent, name) 00569 { 00570 init(value, kMin(0.0, value), kMax(0.0, value), 0.01, 2 ); 00571 } 00572 00573 KDoubleNumInput::KDoubleNumInput(KNumInput* below, double value, QWidget* parent, 00574 const char* name) 00575 : KNumInput(below, parent, name) 00576 { 00577 init( value, kMin(0.0, value), kMax(0.0, value), 0.01, 2 ); 00578 } 00579 00580 KDoubleNumInput::~KDoubleNumInput() 00581 { 00582 delete d; 00583 } 00584 00585 // ### remove when BIC changes are allowed again: 00586 00587 bool KDoubleNumInput::eventFilter( QObject * o, QEvent * e ) { 00588 return KNumInput::eventFilter( o, e ); 00589 } 00590 00591 void KDoubleNumInput::resetEditBox() { 00592 00593 } 00594 00595 // ### end stuff to remove when BIC changes are allowed again 00596 00597 00598 00599 void KDoubleNumInput::init(double value, double lower, double upper, 00600 double step, int precision ) 00601 { 00602 // ### init no longer used members: 00603 edit = 0; 00604 m_range = true; 00605 m_value = 0.0; 00606 m_precision = 2; 00607 // ### end 00608 00609 d = new KDoubleNumInputPrivate( value ); 00610 00611 d->spin = new KDoubleSpinBox( lower, upper, step, value, precision, 00612 this, "KDoubleNumInput::d->spin" ); 00613 setFocusProxy(d->spin); 00614 connect( d->spin, SIGNAL(valueChanged(double)), 00615 this, SIGNAL(valueChanged(double)) ); 00616 connect( this, SIGNAL(valueChanged(double)), 00617 this, SLOT(slotEmitRelativeValueChanged(double)) ); 00618 00619 updateLegacyMembers(); 00620 00621 layout(true); 00622 } 00623 00624 void KDoubleNumInput::updateLegacyMembers() { 00625 // ### update legacy members that are either not private or for 00626 // which an inlined getter exists: 00627 m_lower = minValue(); 00628 m_upper = maxValue(); 00629 m_step = d->spin->lineStep(); 00630 m_specialvalue = specialValueText(); 00631 } 00632 00633 00634 double KDoubleNumInput::mapSliderToSpin( int val ) const 00635 { 00636 // map [slidemin,slidemax] to [spinmin,spinmax] 00637 double spinmin = d->spin->minValue(); 00638 double spinmax = d->spin->maxValue(); 00639 double slidemin = m_slider->minValue(); // cast int to double to avoid 00640 double slidemax = m_slider->maxValue(); // overflow in rel denominator 00641 double rel = ( double(val) - slidemin ) / ( slidemax - slidemin ); 00642 return spinmin + rel * ( spinmax - spinmin ); 00643 } 00644 00645 void KDoubleNumInput::sliderMoved(int val) 00646 { 00647 d->spin->setValue( mapSliderToSpin( val ) ); 00648 } 00649 00650 void KDoubleNumInput::slotEmitRelativeValueChanged( double value ) 00651 { 00652 if ( !d->referencePoint ) return; 00653 emit relativeValueChanged( value / d->referencePoint ); 00654 } 00655 00656 QSize KDoubleNumInput::minimumSizeHint() const 00657 { 00658 constPolish(); 00659 00660 int w; 00661 int h; 00662 00663 h = 2 + QMAX(m_sizeEdit.height(), m_sizeSlider.height()); 00664 00665 // if in extra row, then count it here 00666 if(m_label && (m_alignment & (AlignBottom|AlignTop))) 00667 h += 4 + m_sizeLabel.height(); 00668 else 00669 // label is in the same row as the other widgets 00670 h = QMAX(h, m_sizeLabel.height() + 2); 00671 00672 w = m_slider ? m_slider->sizeHint().width() + 8 : 0; 00673 w += m_colw1 + m_colw2; 00674 00675 if(m_alignment & (AlignTop|AlignBottom)) 00676 w = QMAX(w, m_sizeLabel.width() + 4); 00677 00678 return QSize(w, h); 00679 } 00680 00681 void KDoubleNumInput::resizeEvent(QResizeEvent* e) 00682 { 00683 int w = m_colw1; 00684 int h = 0; 00685 00686 if(m_label && (m_alignment & AlignTop)) { 00687 m_label->setGeometry(0, 0, e->size().width(), m_sizeLabel.height()); 00688 h += m_sizeLabel.height() + 4; 00689 } 00690 00691 if(m_label && (m_alignment & AlignVCenter)) 00692 m_label->setGeometry(0, 0, w, m_sizeEdit.height()); 00693 00694 if (qApp->reverseLayout()) 00695 { 00696 d->spin->setGeometry(w, h, m_slider ? m_colw2 00697 : e->size().width() - w, m_sizeEdit.height()); 00698 w += m_colw2 + KDialog::spacingHint(); 00699 00700 if(m_slider) 00701 m_slider->setGeometry(w, h, e->size().width() - w, m_sizeEdit.height()); 00702 } 00703 else if(m_slider) { 00704 m_slider->setGeometry(w, h, e->size().width() - 00705 (m_colw1 + m_colw2 + KDialog::spacingHint()), 00706 m_sizeEdit.height()); 00707 d->spin->setGeometry(w + m_slider->width() + KDialog::spacingHint(), h, 00708 m_colw2, m_sizeEdit.height()); 00709 } 00710 else { 00711 d->spin->setGeometry(w, h, e->size().width() - w, m_sizeEdit.height()); 00712 } 00713 00714 h += m_sizeEdit.height() + 2; 00715 00716 if(m_label && (m_alignment & AlignBottom)) 00717 m_label->setGeometry(0, h, m_sizeLabel.width(), m_sizeLabel.height()); 00718 } 00719 00720 void KDoubleNumInput::doLayout() 00721 { 00722 m_sizeEdit = d->spin->sizeHint(); 00723 m_colw2 = m_sizeEdit.width(); 00724 } 00725 00726 void KDoubleNumInput::setValue(double val) 00727 { 00728 d->spin->setValue( val ); 00729 } 00730 00731 void KDoubleNumInput::setRelativeValue( double r ) 00732 { 00733 if ( !d->referencePoint ) return; 00734 ++d->blockRelative; 00735 setValue( r * d->referencePoint ); 00736 --d->blockRelative; 00737 } 00738 00739 void KDoubleNumInput::setReferencePoint( double ref ) 00740 { 00741 // clip to valid range: 00742 ref = kMin( maxValue(), kMax( minValue(), ref ) ); 00743 d->referencePoint = ref; 00744 } 00745 00746 void KDoubleNumInput::setRange(double lower, double upper, double step, 00747 bool slider) 00748 { 00749 if( m_slider ) { 00750 // don't update the slider to avoid an endless recursion 00751 QSpinBox * spin = d->spin; 00752 disconnect(spin, SIGNAL(valueChanged(int)), 00753 m_slider, SLOT(setValue(int)) ); 00754 } 00755 d->spin->setRange( lower, upper, step, d->spin->precision() ); 00756 00757 if(slider) { 00758 // upcast to base type to get the min/maxValue in int form: 00759 QSpinBox * spin = d->spin; 00760 int slmax = spin->maxValue(); 00761 int slmin = spin->minValue(); 00762 int slvalue = spin->value(); 00763 int slstep = spin->lineStep(); 00764 if (m_slider) { 00765 m_slider->setRange(slmin, slmax); 00766 m_slider->setLineStep(slstep); 00767 m_slider->setValue(slvalue); 00768 } else { 00769 m_slider = new QSlider(slmin, slmax, slstep, slvalue, 00770 QSlider::Horizontal, this); 00771 m_slider->setTickmarks(QSlider::Below); 00772 // feedback line: when one moves, the other moves, too: 00773 connect(m_slider, SIGNAL(valueChanged(int)), 00774 SLOT(sliderMoved(int)) ); 00775 } 00776 connect(spin, SIGNAL(valueChanged(int)), 00777 m_slider, SLOT(setValue(int)) ); 00778 // calculate ( slmax - slmin ) / 10 without overflowing ints: 00779 int major = calcDiffByTen( slmax, slmin ); 00780 if ( !major ) major = slstep; // ### needed? 00781 m_slider->setTickInterval(major); 00782 } else { 00783 delete m_slider; 00784 m_slider = 0; 00785 } 00786 00787 setReferencePoint( referencePoint() ); 00788 00789 layout(true); 00790 updateLegacyMembers(); 00791 } 00792 00793 void KDoubleNumInput::setMinValue(double min) 00794 { 00795 setRange(min, maxValue(), d->spin->lineStep(), m_slider); 00796 } 00797 00798 double KDoubleNumInput::minValue() const 00799 { 00800 return d->spin->minValue(); 00801 } 00802 00803 void KDoubleNumInput::setMaxValue(double max) 00804 { 00805 setRange(minValue(), max, d->spin->lineStep(), m_slider); 00806 } 00807 00808 double KDoubleNumInput::maxValue() const 00809 { 00810 return d->spin->maxValue(); 00811 } 00812 00813 double KDoubleNumInput::value() const 00814 { 00815 return d->spin->value(); 00816 } 00817 00818 double KDoubleNumInput::relativeValue() const 00819 { 00820 if ( !d->referencePoint ) return 0; 00821 return value() / d->referencePoint; 00822 } 00823 00824 double KDoubleNumInput::referencePoint() const 00825 { 00826 return d->referencePoint; 00827 } 00828 00829 QString KDoubleNumInput::suffix() const 00830 { 00831 return d->spin->suffix(); 00832 } 00833 00834 QString KDoubleNumInput::prefix() const 00835 { 00836 return d->spin->prefix(); 00837 } 00838 00839 void KDoubleNumInput::setSuffix(const QString &suffix) 00840 { 00841 d->spin->setSuffix( suffix ); 00842 00843 layout(true); 00844 } 00845 00846 void KDoubleNumInput::setPrefix(const QString &prefix) 00847 { 00848 d->spin->setPrefix( prefix ); 00849 00850 layout(true); 00851 } 00852 00853 void KDoubleNumInput::setPrecision(int precision) 00854 { 00855 d->spin->setPrecision( precision ); 00856 00857 layout(true); 00858 } 00859 00860 int KDoubleNumInput::precision() const 00861 { 00862 return d->spin->precision(); 00863 } 00864 00865 void KDoubleNumInput::setSpecialValueText(const QString& text) 00866 { 00867 d->spin->setSpecialValueText( text ); 00868 00869 layout(true); 00870 updateLegacyMembers(); 00871 } 00872 00873 void KDoubleNumInput::setLabel(const QString & label, int a) 00874 { 00875 KNumInput::setLabel(label, a); 00876 00877 if(m_label) 00878 m_label->setBuddy(d->spin); 00879 00880 } 00881 00882 // ---------------------------------------------------------------------------- 00883 00884 00885 class KDoubleSpinBoxValidator : public KDoubleValidator 00886 { 00887 public: 00888 KDoubleSpinBoxValidator( double bottom, double top, int decimals, KDoubleSpinBox* sb, const char *name ) 00889 : KDoubleValidator( bottom, top, decimals, sb, name ), spinBox( sb ) { } 00890 00891 virtual State validate( QString& str, int& pos ) const; 00892 00893 private: 00894 KDoubleSpinBox *spinBox; 00895 }; 00896 00897 QValidator::State KDoubleSpinBoxValidator::validate( QString& str, int& pos ) const 00898 { 00899 QString pref = spinBox->prefix(); 00900 QString suff = spinBox->suffix(); 00901 QString suffStriped = suff.stripWhiteSpace(); 00902 uint overhead = pref.length() + suff.length(); 00903 State state = Invalid; 00904 00905 if ( overhead == 0 ) { 00906 state = KDoubleValidator::validate( str, pos ); 00907 } else { 00908 bool stripedVersion = false; 00909 if ( str.length() >= overhead && str.startsWith(pref) 00910 && (str.endsWith(suff) 00911 || (stripedVersion = str.endsWith(suffStriped))) ) { 00912 if ( stripedVersion ) 00913 overhead = pref.length() + suffStriped.length(); 00914 QString core = str.mid( pref.length(), str.length() - overhead ); 00915 int corePos = pos - pref.length(); 00916 state = KDoubleValidator::validate( core, corePos ); 00917 pos = corePos + pref.length(); 00918 str.replace( pref.length(), str.length() - overhead, core ); 00919 } else { 00920 state = KDoubleValidator::validate( str, pos ); 00921 if ( state == Invalid ) { 00922 // stripWhiteSpace(), cf. QSpinBox::interpretText() 00923 QString special = spinBox->specialValueText().stripWhiteSpace(); 00924 QString candidate = str.stripWhiteSpace(); 00925 00926 if ( special.startsWith(candidate) ) { 00927 if ( candidate.length() == special.length() ) { 00928 state = Acceptable; 00929 } else { 00930 state = Intermediate; 00931 } 00932 } 00933 } 00934 } 00935 } 00936 return state; 00937 } 00938 00939 // We use a kind of fixed-point arithmetic to represent the range of 00940 // doubles [mLower,mUpper] in steps of 10^(-mPrecision). Thus, the 00941 // following relations hold: 00942 // 00943 // 1. factor = 10^mPrecision 00944 // 2. basicStep = 1/factor = 10^(-mPrecision); 00945 // 3. lowerInt = lower * factor; 00946 // 4. upperInt = upper * factor; 00947 // 5. lower = lowerInt * basicStep; 00948 // 6. upper = upperInt * basicStep; 00949 class KDoubleSpinBox::Private { 00950 public: 00951 Private( int precision=1 ) 00952 : mPrecision( precision ), 00953 mValidator( 0 ) 00954 { 00955 } 00956 00957 int factor() const { 00958 int f = 1; 00959 for ( int i = 0 ; i < mPrecision ; ++i ) f *= 10; 00960 return f; 00961 } 00962 00963 double basicStep() const { 00964 return 1.0/double(factor()); 00965 } 00966 00967 int mapToInt( double value, bool * ok ) const { 00968 assert( ok ); 00969 const double f = factor(); 00970 if ( value > double(INT_MAX) / f ) { 00971 kdWarning() << "KDoubleSpinBox: can't represent value " << value 00972 << "in terms of fixed-point numbers with precision " 00973 << mPrecision << endl; 00974 *ok = false; 00975 return INT_MAX; 00976 } else if ( value < double(INT_MIN) / f ) { 00977 kdWarning() << "KDoubleSpinBox: can't represent value " << value 00978 << "in terms of fixed-point numbers with precision " 00979 << mPrecision << endl; 00980 *ok = false; 00981 return INT_MIN; 00982 } else { 00983 *ok = true; 00984 return int( value * f + ( value < 0 ? -0.5 : 0.5 ) ); 00985 } 00986 } 00987 00988 double mapToDouble( int value ) const { 00989 return double(value) * basicStep(); 00990 } 00991 00992 int mPrecision; 00993 KDoubleSpinBoxValidator * mValidator; 00994 }; 00995 00996 KDoubleSpinBox::KDoubleSpinBox( QWidget * parent, const char * name ) 00997 : QSpinBox( parent, name ) 00998 { 00999 editor()->setAlignment( Qt::AlignRight ); 01000 d = new Private(); 01001 updateValidator(); 01002 } 01003 01004 KDoubleSpinBox::KDoubleSpinBox( double lower, double upper, double step, 01005 double value, int precision, 01006 QWidget * parent, const char * name ) 01007 : QSpinBox( parent, name ) 01008 { 01009 editor()->setAlignment( Qt::AlignRight ); 01010 d = new Private(); 01011 setRange( lower, upper, step, precision ); 01012 setValue( value ); 01013 connect( this, SIGNAL(valueChanged(int)), SLOT(slotValueChanged(int)) ); 01014 } 01015 01016 KDoubleSpinBox::~KDoubleSpinBox() { 01017 delete d; d = 0; 01018 } 01019 01020 bool KDoubleSpinBox::acceptLocalizedNumbers() const { 01021 if ( !d->mValidator ) return true; // we'll set one that does; 01022 // can't do it now, since we're const 01023 return d->mValidator->acceptLocalizedNumbers(); 01024 } 01025 01026 void KDoubleSpinBox::setAcceptLocalizedNumbers( bool accept ) { 01027 if ( !d->mValidator ) updateValidator(); 01028 d->mValidator->setAcceptLocalizedNumbers( accept ); 01029 } 01030 01031 void KDoubleSpinBox::setRange( double lower, double upper, double step, 01032 int precision ) { 01033 lower = kMin(upper, lower); 01034 upper = kMax(upper, lower); 01035 setPrecision( precision, true ); // disable bounds checking, since 01036 setMinValue( lower ); // it's done in set{Min,Max}Value 01037 setMaxValue( upper ); // anyway and we want lower, upper 01038 setLineStep( step ); // and step to have the right precision 01039 } 01040 01041 int KDoubleSpinBox::precision() const { 01042 return d->mPrecision; 01043 } 01044 01045 void KDoubleSpinBox::setPrecision( int precision ) { 01046 setPrecision( precision, false ); 01047 } 01048 01049 void KDoubleSpinBox::setPrecision( int precision, bool force ) { 01050 if ( precision < 1 ) return; 01051 if ( !force ) { 01052 int maxPrec = maxPrecision(); 01053 if ( precision > maxPrec ) 01054 precision = maxPrec; 01055 } 01056 d->mPrecision = precision; 01057 updateValidator(); 01058 } 01059 01060 int KDoubleSpinBox::maxPrecision() const { 01061 // INT_MAX must be > maxAbsValue * 10^precision 01062 // ==> 10^precision < INT_MAX / maxAbsValue 01063 // ==> precision < log10 ( INT_MAX / maxAbsValue ) 01064 // ==> maxPrecision = floor( log10 ( INT_MAX / maxAbsValue ) ); 01065 double maxAbsValue = kMax( fabs(minValue()), fabs(maxValue()) ); 01066 if ( maxAbsValue == 0 ) return 6; // return arbitrary value to avoid dbz... 01067 01068 return int( floor( log10( double(INT_MAX) / maxAbsValue ) ) ); 01069 } 01070 01071 double KDoubleSpinBox::value() const { 01072 return d->mapToDouble( base::value() ); 01073 } 01074 01075 void KDoubleSpinBox::setValue( double value ) { 01076 if ( value == this->value() ) return; 01077 if ( value < minValue() ) 01078 base::setValue( base::minValue() ); 01079 else if ( value > maxValue() ) 01080 base::setValue( base::maxValue() ); 01081 else { 01082 bool ok = false; 01083 base::setValue( d->mapToInt( value, &ok ) ); 01084 assert( ok ); 01085 } 01086 } 01087 01088 double KDoubleSpinBox::minValue() const { 01089 return d->mapToDouble( base::minValue() ); 01090 } 01091 01092 void KDoubleSpinBox::setMinValue( double value ) { 01093 bool ok = false; 01094 int min = d->mapToInt( value, &ok ); 01095 if ( !ok ) return; 01096 base::setMinValue( min ); 01097 updateValidator(); 01098 } 01099 01100 01101 double KDoubleSpinBox::maxValue() const { 01102 return d->mapToDouble( base::maxValue() ); 01103 } 01104 01105 void KDoubleSpinBox::setMaxValue( double value ) { 01106 bool ok = false; 01107 int max = d->mapToInt( value, &ok ); 01108 if ( !ok ) return; 01109 base::setMaxValue( max ); 01110 updateValidator(); 01111 } 01112 01113 double KDoubleSpinBox::lineStep() const { 01114 return d->mapToDouble( base::lineStep() ); 01115 } 01116 01117 void KDoubleSpinBox::setLineStep( double step ) { 01118 bool ok = false; 01119 if ( step > maxValue() - minValue() ) 01120 base::setLineStep( 1 ); 01121 else 01122 base::setLineStep( kMax( d->mapToInt( step, &ok ), 1 ) ); 01123 } 01124 01125 QString KDoubleSpinBox::mapValueToText( int value ) { 01126 if ( acceptLocalizedNumbers() ) 01127 return KGlobal::locale() 01128 ->formatNumber( d->mapToDouble( value ), d->mPrecision ); 01129 else 01130 return QString().setNum( d->mapToDouble( value ), 'f', d->mPrecision ); 01131 } 01132 01133 int KDoubleSpinBox::mapTextToValue( bool * ok ) { 01134 double value; 01135 if ( acceptLocalizedNumbers() ) 01136 value = KGlobal::locale()->readNumber( cleanText(), ok ); 01137 else 01138 value = cleanText().toDouble( ok ); 01139 if ( !*ok ) return 0; 01140 if ( value > maxValue() ) 01141 value = maxValue(); 01142 else if ( value < minValue() ) 01143 value = minValue(); 01144 return d->mapToInt( value, ok ); 01145 } 01146 01147 void KDoubleSpinBox::setValidator( const QValidator * ) { 01148 // silently discard the new validator. We don't want another one ;-) 01149 } 01150 01151 void KDoubleSpinBox::slotValueChanged( int value ) { 01152 emit valueChanged( d->mapToDouble( value ) ); 01153 } 01154 01155 void KDoubleSpinBox::updateValidator() { 01156 if ( !d->mValidator ) { 01157 d->mValidator = new KDoubleSpinBoxValidator( minValue(), maxValue(), precision(), 01158 this, "d->mValidator" ); 01159 base::setValidator( d->mValidator ); 01160 } else 01161 d->mValidator->setRange( minValue(), maxValue(), precision() ); 01162 } 01163 01164 void KNumInput::virtual_hook( int, void* ) 01165 { /*BASE::virtual_hook( id, data );*/ } 01166 01167 void KIntNumInput::virtual_hook( int id, void* data ) 01168 { KNumInput::virtual_hook( id, data ); } 01169 01170 void KDoubleNumInput::virtual_hook( int id, void* data ) 01171 { KNumInput::virtual_hook( id, data ); } 01172 01173 void KIntSpinBox::virtual_hook( int, void* ) 01174 { /*BASE::virtual_hook( id, data );*/ } 01175 01176 void KDoubleSpinBox::virtual_hook( int, void* ) 01177 { /*BASE::virtual_hook( id, data );*/ } 01178 01179 #include "knuminput.moc"
KDE Logo
This file is part of the documentation for kdeui Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Sep 29 09:40:34 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003