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., 51 Franklin Street, Fifth Floor, 00028 * Boston, MA 02110-1301, 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 <tqapplication.h> 00040 #include <tqlabel.h> 00041 #include <tqlineedit.h> 00042 #include <tqsize.h> 00043 #include <tqslider.h> 00044 #include <tqspinbox.h> 00045 #include <tqstyle.h> 00046 00047 #include <tdeglobal.h> 00048 #include <tdelocale.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(TQWidget* parent, const char* name) 00063 : TQWidget(parent, name) 00064 { 00065 init(); 00066 } 00067 00068 KNumInput::KNumInput(KNumInput* below, TQWidget* parent, const char* name) 00069 : TQWidget(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 TQString & 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 TQLabel(label, this, "KNumInput::TQLabel"); 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 TQString KNumInput::label() const 00123 { 00124 if (m_label) return m_label->text(); 00125 return TQString::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() : TQSize(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() : TQSize(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 = TQMAX(w1, p->m_colw1); 00156 w2 = TQMAX(w2, p->m_colw2); 00157 p = p->m_prev; 00158 } 00159 00160 p = m_next; 00161 while(p) { 00162 p->doLayout(); 00163 w1 = TQMAX(w1, p->m_colw1); 00164 w2 = TQMAX(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 TQSizePolicy KNumInput::sizePolicy() const 00186 { 00187 return TQSizePolicy( TQSizePolicy::Minimum, TQSizePolicy::Fixed ); 00188 } 00189 00190 TQSize 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(TQWidget *parent, const char *name) 00205 : TQSpinBox(0, 99, 1, parent, name) 00206 { 00207 editor()->setAlignment(AlignRight); 00208 val_base = 10; 00209 setValidator(new KIntValidator(this, val_base)); 00210 setValue(0); 00211 } 00212 00213 KIntSpinBox::~KIntSpinBox() 00214 { 00215 } 00216 00217 KIntSpinBox::KIntSpinBox(int lower, int upper, int step, int value, int base, 00218 TQWidget* parent, const char* name) 00219 : TQSpinBox(lower, upper, step, parent, name) 00220 { 00221 editor()->setAlignment(AlignRight); 00222 val_base = base; 00223 setValidator(new KIntValidator(this, val_base)); 00224 setValue(value); 00225 } 00226 00227 void KIntSpinBox::setBase(int base) 00228 { 00229 const KIntValidator* kvalidator = dynamic_cast<const KIntValidator*>(validator()); 00230 if (kvalidator) { 00231 const_cast<KIntValidator*>(kvalidator)->setBase(base); 00232 } 00233 val_base = base; 00234 } 00235 00236 00237 int KIntSpinBox::base() const 00238 { 00239 return val_base; 00240 } 00241 00242 TQString KIntSpinBox::mapValueToText(int v) 00243 { 00244 return TQString::number(v, val_base); 00245 } 00246 00247 int KIntSpinBox::mapTextToValue(bool* ok) 00248 { 00249 return cleanText().toInt(ok, val_base); 00250 } 00251 00252 void KIntSpinBox::setEditFocus(bool mark) 00253 { 00254 editor()->setFocus(); 00255 if(mark) 00256 editor()->selectAll(); 00257 } 00258 00259 00260 // ---------------------------------------------------------------------------- 00261 00262 class KIntNumInput::KIntNumInputPrivate { 00263 public: 00264 int referencePoint; 00265 short blockRelative; 00266 KIntNumInputPrivate( int r ) 00267 : referencePoint( r ), 00268 blockRelative( 0 ) {} 00269 }; 00270 00271 00272 KIntNumInput::KIntNumInput(KNumInput* below, int val, TQWidget* parent, 00273 int _base, const char* name) 00274 : KNumInput(below, parent, name) 00275 { 00276 init(val, _base); 00277 } 00278 00279 KIntNumInput::KIntNumInput(TQWidget *parent, const char *name) 00280 : KNumInput(parent, name) 00281 { 00282 init(0, 10); 00283 } 00284 00285 KIntNumInput::KIntNumInput(int val, TQWidget *parent, int _base, const char *name) 00286 : KNumInput(parent, name) 00287 { 00288 init(val, _base); 00289 00290 } 00291 00292 void KIntNumInput::init(int val, int _base) 00293 { 00294 d = new KIntNumInputPrivate( val ); 00295 m_spin = new KIntSpinBox(INT_MIN, INT_MAX, 1, val, _base, this, "KIntNumInput::KIntSpinBox"); 00296 // the KIntValidator is broken beyond believe for 00297 // spinboxes which have suffix or prefix texts, so 00298 // better don't use it unless absolutely necessary 00299 if (_base != 10) 00300 m_spin->setValidator(new KIntValidator(this, _base, "KNumInput::KIntValidtr")); 00301 00302 connect(m_spin, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(spinValueChanged(int))); 00303 connect(this, TQT_SIGNAL(valueChanged(int)), 00304 TQT_SLOT(slotEmitRelativeValueChanged(int))); 00305 00306 setFocusProxy(m_spin); 00307 layout(true); 00308 } 00309 00310 void KIntNumInput::setReferencePoint( int ref ) { 00311 // clip to valid range: 00312 ref = kMin( maxValue(), kMax( minValue(), ref ) ); 00313 d->referencePoint = ref; 00314 } 00315 00316 int KIntNumInput::referencePoint() const { 00317 return d->referencePoint; 00318 } 00319 00320 void KIntNumInput::spinValueChanged(int val) 00321 { 00322 if(m_slider) 00323 m_slider->setValue(val); 00324 00325 emit valueChanged(val); 00326 } 00327 00328 void KIntNumInput::slotEmitRelativeValueChanged( int value ) { 00329 if ( d->blockRelative || !d->referencePoint ) return; 00330 emit relativeValueChanged( double( value ) / double( d->referencePoint ) ); 00331 } 00332 00333 void KIntNumInput::setRange(int lower, int upper, int step, bool slider) 00334 { 00335 upper = kMax(upper, lower); 00336 lower = kMin(upper, lower); 00337 m_spin->setMinValue(lower); 00338 m_spin->setMaxValue(upper); 00339 m_spin->setLineStep(step); 00340 00341 step = m_spin->lineStep(); // maybe TQRangeControl didn't like out lineStep? 00342 00343 if(slider) { 00344 if (m_slider) 00345 m_slider->setRange(lower, upper); 00346 else { 00347 m_slider = new TQSlider(lower, upper, step, m_spin->value(), 00348 Qt::Horizontal, this); 00349 m_slider->setTickmarks(TQSlider::Below); 00350 connect(m_slider, TQT_SIGNAL(valueChanged(int)), 00351 m_spin, TQT_SLOT(setValue(int))); 00352 } 00353 00354 // calculate (upper-lower)/10 without overflowing int's: 00355 int major = calcDiffByTen( upper, lower ); 00356 if ( major==0 ) major = step; // #### workaround Qt bug in 2.1-beta4 00357 00358 m_slider->setSteps(step, major); 00359 m_slider->setTickInterval(major); 00360 } 00361 else { 00362 delete m_slider; 00363 m_slider = 0; 00364 } 00365 00366 // check that reference point is still inside valid range: 00367 setReferencePoint( referencePoint() ); 00368 00369 layout(true); 00370 } 00371 00372 void KIntNumInput::setMinValue(int min) 00373 { 00374 setRange(min, m_spin->maxValue(), m_spin->lineStep(), m_slider); 00375 } 00376 00377 int KIntNumInput::minValue() const 00378 { 00379 return m_spin->minValue(); 00380 } 00381 00382 void KIntNumInput::setMaxValue(int max) 00383 { 00384 setRange(m_spin->minValue(), max, m_spin->lineStep(), m_slider); 00385 } 00386 00387 int KIntNumInput::maxValue() const 00388 { 00389 return m_spin->maxValue(); 00390 } 00391 00392 void KIntNumInput::setSuffix(const TQString &suffix) 00393 { 00394 m_spin->setSuffix(suffix); 00395 00396 layout(true); 00397 } 00398 00399 TQString KIntNumInput::suffix() const 00400 { 00401 return m_spin->suffix(); 00402 } 00403 00404 void KIntNumInput::setPrefix(const TQString &prefix) 00405 { 00406 m_spin->setPrefix(prefix); 00407 00408 layout(true); 00409 } 00410 00411 TQString KIntNumInput::prefix() const 00412 { 00413 return m_spin->prefix(); 00414 } 00415 00416 void KIntNumInput::setEditFocus(bool mark) 00417 { 00418 m_spin->setEditFocus(mark); 00419 } 00420 00421 TQSize KIntNumInput::minimumSizeHint() const 00422 { 00423 constPolish(); 00424 00425 int w; 00426 int h; 00427 00428 h = 2 + TQMAX(m_sizeSpin.height(), m_sizeSlider.height()); 00429 00430 // if in extra row, then count it here 00431 if(m_label && (m_alignment & (AlignBottom|AlignTop))) 00432 h += 4 + m_sizeLabel.height(); 00433 else 00434 // label is in the same row as the other widgets 00435 h = TQMAX(h, m_sizeLabel.height() + 2); 00436 00437 w = m_slider ? m_slider->sizeHint().width() + 8 : 0; 00438 w += m_colw1 + m_colw2; 00439 00440 if(m_alignment & (AlignTop|AlignBottom)) 00441 w = TQMAX(w, m_sizeLabel.width() + 4); 00442 00443 return TQSize(w, h); 00444 } 00445 00446 void KIntNumInput::doLayout() 00447 { 00448 m_sizeSpin = m_spin->sizeHint(); 00449 m_colw2 = m_sizeSpin.width(); 00450 00451 if (m_label) 00452 m_label->setBuddy(m_spin); 00453 } 00454 00455 void KIntNumInput::resizeEvent(TQResizeEvent* e) 00456 { 00457 int w = m_colw1; 00458 int h = 0; 00459 00460 if(m_label && (m_alignment & AlignTop)) { 00461 m_label->setGeometry(0, 0, e->size().width(), m_sizeLabel.height()); 00462 h += m_sizeLabel.height() + KDialog::spacingHint(); 00463 } 00464 00465 if(m_label && (m_alignment & AlignVCenter)) 00466 m_label->setGeometry(0, 0, w, m_sizeSpin.height()); 00467 00468 if (tqApp->reverseLayout()) 00469 { 00470 m_spin->setGeometry(w, h, m_slider ? m_colw2 : TQMAX(m_colw2, e->size().width() - w), m_sizeSpin.height()); 00471 w += m_colw2 + 8; 00472 00473 if(m_slider) 00474 m_slider->setGeometry(w, h, e->size().width() - w, m_sizeSpin.height()); 00475 } 00476 else if(m_slider) { 00477 m_slider->setGeometry(w, h, e->size().width() - (w + m_colw2 + KDialog::spacingHint()), m_sizeSpin.height()); 00478 m_spin->setGeometry(w + m_slider->size().width() + KDialog::spacingHint(), h, m_colw2, m_sizeSpin.height()); 00479 } 00480 else { 00481 m_spin->setGeometry(w, h, TQMAX(m_colw2, e->size().width() - w), m_sizeSpin.height()); 00482 } 00483 00484 h += m_sizeSpin.height() + 2; 00485 00486 if(m_label && (m_alignment & AlignBottom)) 00487 m_label->setGeometry(0, h, m_sizeLabel.width(), m_sizeLabel.height()); 00488 } 00489 00490 KIntNumInput::~KIntNumInput() 00491 { 00492 delete d; 00493 } 00494 00495 void KIntNumInput::setValue(int val) 00496 { 00497 m_spin->setValue(val); 00498 // slider value is changed by spinValueChanged 00499 } 00500 00501 void KIntNumInput::setRelativeValue( double r ) { 00502 if ( !d->referencePoint ) return; 00503 ++d->blockRelative; 00504 setValue( int( d->referencePoint * r + 0.5 ) ); 00505 --d->blockRelative; 00506 } 00507 00508 double KIntNumInput::relativeValue() const { 00509 if ( !d->referencePoint ) return 0; 00510 return double( value() ) / double ( d->referencePoint ); 00511 } 00512 00513 int KIntNumInput::value() const 00514 { 00515 return m_spin->value(); 00516 } 00517 00518 void KIntNumInput::setSpecialValueText(const TQString& text) 00519 { 00520 m_spin->setSpecialValueText(text); 00521 layout(true); 00522 } 00523 00524 TQString KIntNumInput::specialValueText() const 00525 { 00526 return m_spin->specialValueText(); 00527 } 00528 00529 void KIntNumInput::setLabel(const TQString & label, int a) 00530 { 00531 KNumInput::setLabel(label, a); 00532 00533 if(m_label) 00534 m_label->setBuddy(m_spin); 00535 } 00536 00537 // ---------------------------------------------------------------------------- 00538 00539 class KDoubleNumInput::KDoubleNumInputPrivate { 00540 public: 00541 KDoubleNumInputPrivate( double r ) 00542 : spin( 0 ), 00543 referencePoint( r ), 00544 blockRelative ( 0 ) {} 00545 KDoubleSpinBox * spin; 00546 double referencePoint; 00547 short blockRelative; 00548 }; 00549 00550 KDoubleNumInput::KDoubleNumInput(TQWidget *parent, const char *name) 00551 : KNumInput(parent, name) 00552 { 00553 init(0.0, 0.0, 9999.0, 0.01, 2); 00554 } 00555 00556 KDoubleNumInput::KDoubleNumInput(double lower, double upper, double value, 00557 double step, int precision, TQWidget* parent, 00558 const char *name) 00559 : KNumInput(parent, name) 00560 { 00561 init(value, lower, upper, step, precision); 00562 } 00563 00564 KDoubleNumInput::KDoubleNumInput(KNumInput *below, 00565 double lower, double upper, double value, 00566 double step, int precision, TQWidget* parent, 00567 const char *name) 00568 : KNumInput(below, parent, name) 00569 { 00570 init(value, lower, upper, step, precision); 00571 } 00572 00573 KDoubleNumInput::KDoubleNumInput(double value, TQWidget *parent, const char *name) 00574 : KNumInput(parent, name) 00575 { 00576 init(value, kMin(0.0, value), kMax(0.0, value), 0.01, 2 ); 00577 } 00578 00579 KDoubleNumInput::KDoubleNumInput(KNumInput* below, double value, TQWidget* parent, 00580 const char* name) 00581 : KNumInput(below, parent, name) 00582 { 00583 init( value, kMin(0.0, value), kMax(0.0, value), 0.01, 2 ); 00584 } 00585 00586 KDoubleNumInput::~KDoubleNumInput() 00587 { 00588 delete d; 00589 } 00590 00591 // ### remove when BIC changes are allowed again: 00592 00593 bool KDoubleNumInput::eventFilter( TQObject * o, TQEvent * e ) { 00594 return KNumInput::eventFilter( o, e ); 00595 } 00596 00597 void KDoubleNumInput::resetEditBox() { 00598 00599 } 00600 00601 // ### end stuff to remove when BIC changes are allowed again 00602 00603 00604 00605 void KDoubleNumInput::init(double value, double lower, double upper, 00606 double step, int precision ) 00607 { 00608 // ### init no longer used members: 00609 edit = 0; 00610 m_range = true; 00611 m_value = 0.0; 00612 m_precision = 2; 00613 // ### end 00614 00615 d = new KDoubleNumInputPrivate( value ); 00616 00617 d->spin = new KDoubleSpinBox( lower, upper, step, value, precision, 00618 this, "KDoubleNumInput::d->spin" ); 00619 setFocusProxy(d->spin); 00620 connect( d->spin, TQT_SIGNAL(valueChanged(double)), 00621 this, TQT_SIGNAL(valueChanged(double)) ); 00622 connect( this, TQT_SIGNAL(valueChanged(double)), 00623 this, TQT_SLOT(slotEmitRelativeValueChanged(double)) ); 00624 00625 updateLegacyMembers(); 00626 00627 layout(true); 00628 } 00629 00630 void KDoubleNumInput::updateLegacyMembers() { 00631 // ### update legacy members that are either not private or for 00632 // which an inlined getter exists: 00633 m_lower = minValue(); 00634 m_upper = maxValue(); 00635 m_step = d->spin->lineStep(); 00636 m_specialvalue = specialValueText(); 00637 } 00638 00639 00640 double KDoubleNumInput::mapSliderToSpin( int val ) const 00641 { 00642 // map [slidemin,slidemax] to [spinmin,spinmax] 00643 double spinmin = d->spin->minValue(); 00644 double spinmax = d->spin->maxValue(); 00645 double slidemin = m_slider->minValue(); // cast int to double to avoid 00646 double slidemax = m_slider->maxValue(); // overflow in rel denominator 00647 double rel = ( double(val) - slidemin ) / ( slidemax - slidemin ); 00648 return spinmin + rel * ( spinmax - spinmin ); 00649 } 00650 00651 void KDoubleNumInput::sliderMoved(int val) 00652 { 00653 d->spin->setValue( mapSliderToSpin( val ) ); 00654 } 00655 00656 void KDoubleNumInput::slotEmitRelativeValueChanged( double value ) 00657 { 00658 if ( !d->referencePoint ) return; 00659 emit relativeValueChanged( value / d->referencePoint ); 00660 } 00661 00662 TQSize KDoubleNumInput::minimumSizeHint() const 00663 { 00664 constPolish(); 00665 00666 int w; 00667 int h; 00668 00669 h = 2 + TQMAX(m_sizeEdit.height(), m_sizeSlider.height()); 00670 00671 // if in extra row, then count it here 00672 if(m_label && (m_alignment & (AlignBottom|AlignTop))) 00673 h += 4 + m_sizeLabel.height(); 00674 else 00675 // label is in the same row as the other widgets 00676 h = TQMAX(h, m_sizeLabel.height() + 2); 00677 00678 w = m_slider ? m_slider->sizeHint().width() + 8 : 0; 00679 w += m_colw1 + m_colw2; 00680 00681 if(m_alignment & (AlignTop|AlignBottom)) 00682 w = TQMAX(w, m_sizeLabel.width() + 4); 00683 00684 return TQSize(w, h); 00685 } 00686 00687 void KDoubleNumInput::resizeEvent(TQResizeEvent* e) 00688 { 00689 int w = m_colw1; 00690 int h = 0; 00691 00692 if(m_label && (m_alignment & AlignTop)) { 00693 m_label->setGeometry(0, 0, e->size().width(), m_sizeLabel.height()); 00694 h += m_sizeLabel.height() + 4; 00695 } 00696 00697 if(m_label && (m_alignment & AlignVCenter)) 00698 m_label->setGeometry(0, 0, w, m_sizeEdit.height()); 00699 00700 if (tqApp->reverseLayout()) 00701 { 00702 d->spin->setGeometry(w, h, m_slider ? m_colw2 00703 : e->size().width() - w, m_sizeEdit.height()); 00704 w += m_colw2 + KDialog::spacingHint(); 00705 00706 if(m_slider) 00707 m_slider->setGeometry(w, h, e->size().width() - w, m_sizeEdit.height()); 00708 } 00709 else if(m_slider) { 00710 m_slider->setGeometry(w, h, e->size().width() - 00711 (m_colw1 + m_colw2 + KDialog::spacingHint()), 00712 m_sizeEdit.height()); 00713 d->spin->setGeometry(w + m_slider->width() + KDialog::spacingHint(), h, 00714 m_colw2, m_sizeEdit.height()); 00715 } 00716 else { 00717 d->spin->setGeometry(w, h, e->size().width() - w, m_sizeEdit.height()); 00718 } 00719 00720 h += m_sizeEdit.height() + 2; 00721 00722 if(m_label && (m_alignment & AlignBottom)) 00723 m_label->setGeometry(0, h, m_sizeLabel.width(), m_sizeLabel.height()); 00724 } 00725 00726 void KDoubleNumInput::doLayout() 00727 { 00728 m_sizeEdit = d->spin->sizeHint(); 00729 m_colw2 = m_sizeEdit.width(); 00730 } 00731 00732 void KDoubleNumInput::setValue(double val) 00733 { 00734 d->spin->setValue( val ); 00735 } 00736 00737 void KDoubleNumInput::setRelativeValue( double r ) 00738 { 00739 if ( !d->referencePoint ) return; 00740 ++d->blockRelative; 00741 setValue( r * d->referencePoint ); 00742 --d->blockRelative; 00743 } 00744 00745 void KDoubleNumInput::setReferencePoint( double ref ) 00746 { 00747 // clip to valid range: 00748 ref = kMin( maxValue(), kMax( minValue(), ref ) ); 00749 d->referencePoint = ref; 00750 } 00751 00752 void KDoubleNumInput::setRange(double lower, double upper, double step, 00753 bool slider) 00754 { 00755 if( m_slider ) { 00756 // don't update the slider to avoid an endless recursion 00757 TQSpinBox * spin = d->spin; 00758 disconnect(spin, TQT_SIGNAL(valueChanged(int)), 00759 m_slider, TQT_SLOT(setValue(int)) ); 00760 } 00761 d->spin->setRange( lower, upper, step, d->spin->precision() ); 00762 00763 if(slider) { 00764 // upcast to base type to get the min/maxValue in int form: 00765 TQSpinBox * spin = d->spin; 00766 int slmax = spin->maxValue(); 00767 int slmin = spin->minValue(); 00768 int slvalue = spin->value(); 00769 int slstep = spin->lineStep(); 00770 if (m_slider) { 00771 m_slider->setRange(slmin, slmax); 00772 m_slider->setValue(slvalue); 00773 } else { 00774 m_slider = new TQSlider(slmin, slmax, slstep, slvalue, 00775 Qt::Horizontal, this); 00776 m_slider->setTickmarks(TQSlider::Below); 00777 // feedback line: when one moves, the other moves, too: 00778 connect(m_slider, TQT_SIGNAL(valueChanged(int)), 00779 TQT_SLOT(sliderMoved(int)) ); 00780 } 00781 connect(spin, TQT_SIGNAL(valueChanged(int)), 00782 m_slider, TQT_SLOT(setValue(int)) ); 00783 // calculate ( slmax - slmin ) / 10 without overflowing ints: 00784 int major = calcDiffByTen( slmax, slmin ); 00785 if ( !major ) major = slstep; // ### needed? 00786 m_slider->setSteps(slstep, major); 00787 m_slider->setTickInterval(major); 00788 } else { 00789 delete m_slider; 00790 m_slider = 0; 00791 } 00792 00793 setReferencePoint( referencePoint() ); 00794 00795 layout(true); 00796 updateLegacyMembers(); 00797 } 00798 00799 void KDoubleNumInput::setMinValue(double min) 00800 { 00801 setRange(min, maxValue(), d->spin->lineStep(), m_slider); 00802 } 00803 00804 double KDoubleNumInput::minValue() const 00805 { 00806 return d->spin->minValue(); 00807 } 00808 00809 void KDoubleNumInput::setMaxValue(double max) 00810 { 00811 setRange(minValue(), max, d->spin->lineStep(), m_slider); 00812 } 00813 00814 double KDoubleNumInput::maxValue() const 00815 { 00816 return d->spin->maxValue(); 00817 } 00818 00819 double KDoubleNumInput::value() const 00820 { 00821 return d->spin->value(); 00822 } 00823 00824 double KDoubleNumInput::relativeValue() const 00825 { 00826 if ( !d->referencePoint ) return 0; 00827 return value() / d->referencePoint; 00828 } 00829 00830 double KDoubleNumInput::referencePoint() const 00831 { 00832 return d->referencePoint; 00833 } 00834 00835 TQString KDoubleNumInput::suffix() const 00836 { 00837 return d->spin->suffix(); 00838 } 00839 00840 TQString KDoubleNumInput::prefix() const 00841 { 00842 return d->spin->prefix(); 00843 } 00844 00845 void KDoubleNumInput::setSuffix(const TQString &suffix) 00846 { 00847 d->spin->setSuffix( suffix ); 00848 00849 layout(true); 00850 } 00851 00852 void KDoubleNumInput::setPrefix(const TQString &prefix) 00853 { 00854 d->spin->setPrefix( prefix ); 00855 00856 layout(true); 00857 } 00858 00859 void KDoubleNumInput::setPrecision(int precision) 00860 { 00861 d->spin->setPrecision( precision ); 00862 if(m_slider) { 00863 // upcast to base type to get the min/maxValue in int form: 00864 TQSpinBox * spin = d->spin; 00865 m_slider->setRange(spin->minValue(), spin->maxValue()); 00866 m_slider->setValue(spin->value()); 00867 int major = calcDiffByTen(spin->maxValue(), spin->minValue()); 00868 if ( !major ) major = spin->lineStep(); 00869 m_slider->setSteps(spin->lineStep(), major); 00870 m_slider->setTickInterval(major); 00871 } 00872 00873 layout(true); 00874 } 00875 00876 int KDoubleNumInput::precision() const 00877 { 00878 return d->spin->precision(); 00879 } 00880 00881 void KDoubleNumInput::setSpecialValueText(const TQString& text) 00882 { 00883 d->spin->setSpecialValueText( text ); 00884 00885 layout(true); 00886 updateLegacyMembers(); 00887 } 00888 00889 void KDoubleNumInput::setLabel(const TQString & label, int a) 00890 { 00891 KNumInput::setLabel(label, a); 00892 00893 if(m_label) 00894 m_label->setBuddy(d->spin); 00895 00896 } 00897 00898 // ---------------------------------------------------------------------------- 00899 00900 00901 class KDoubleSpinBoxValidator : public KDoubleValidator 00902 { 00903 public: 00904 KDoubleSpinBoxValidator( double bottom, double top, int decimals, KDoubleSpinBox* sb, const char *name ) 00905 : KDoubleValidator( bottom, top, decimals, TQT_TQOBJECT(sb), name ), spinBox( sb ) { } 00906 00907 virtual State validate( TQString& str, int& pos ) const; 00908 00909 private: 00910 KDoubleSpinBox *spinBox; 00911 }; 00912 00913 TQValidator::State KDoubleSpinBoxValidator::validate( TQString& str, int& pos ) const 00914 { 00915 TQString pref = spinBox->prefix(); 00916 TQString suff = spinBox->suffix(); 00917 TQString suffStriped = suff.stripWhiteSpace(); 00918 uint overhead = pref.length() + suff.length(); 00919 State state = Invalid; 00920 00921 if ( overhead == 0 ) { 00922 state = KDoubleValidator::validate( str, pos ); 00923 } else { 00924 bool stripedVersion = false; 00925 if ( str.length() >= overhead && str.startsWith(pref) 00926 && (str.endsWith(suff) 00927 || (stripedVersion = str.endsWith(suffStriped))) ) { 00928 if ( stripedVersion ) 00929 overhead = pref.length() + suffStriped.length(); 00930 TQString core = str.mid( pref.length(), str.length() - overhead ); 00931 int corePos = pos - pref.length(); 00932 state = KDoubleValidator::validate( core, corePos ); 00933 pos = corePos + pref.length(); 00934 str.replace( pref.length(), str.length() - overhead, core ); 00935 } else { 00936 state = KDoubleValidator::validate( str, pos ); 00937 if ( state == Invalid ) { 00938 // stripWhiteSpace(), cf. TQSpinBox::interpretText() 00939 TQString special = spinBox->specialValueText().stripWhiteSpace(); 00940 TQString candidate = str.stripWhiteSpace(); 00941 00942 if ( special.startsWith(candidate) ) { 00943 if ( candidate.length() == special.length() ) { 00944 state = Acceptable; 00945 } else { 00946 state = Intermediate; 00947 } 00948 } 00949 } 00950 } 00951 } 00952 return state; 00953 } 00954 00955 // We use a kind of fixed-point arithmetic to represent the range of 00956 // doubles [mLower,mUpper] in steps of 10^(-mPrecision). Thus, the 00957 // following relations hold: 00958 // 00959 // 1. factor = 10^mPrecision 00960 // 2. basicStep = 1/factor = 10^(-mPrecision); 00961 // 3. lowerInt = lower * factor; 00962 // 4. upperInt = upper * factor; 00963 // 5. lower = lowerInt * basicStep; 00964 // 6. upper = upperInt * basicStep; 00965 class KDoubleSpinBox::Private { 00966 public: 00967 Private( int precision=1 ) 00968 : mPrecision( precision ), 00969 mValidator( 0 ) 00970 { 00971 } 00972 00973 int factor() const { 00974 int f = 1; 00975 for ( int i = 0 ; i < mPrecision ; ++i ) f *= 10; 00976 return f; 00977 } 00978 00979 double basicStep() const { 00980 return 1.0/double(factor()); 00981 } 00982 00983 int mapToInt( double value, bool * ok ) const { 00984 assert( ok ); 00985 const double f = factor(); 00986 if ( value > double(INT_MAX) / f ) { 00987 kdWarning() << "KDoubleSpinBox: can't represent value " << value 00988 << "in terms of fixed-point numbers with precision " 00989 << mPrecision << endl; 00990 *ok = false; 00991 return INT_MAX; 00992 } else if ( value < double(INT_MIN) / f ) { 00993 kdWarning() << "KDoubleSpinBox: can't represent value " << value 00994 << "in terms of fixed-point numbers with precision " 00995 << mPrecision << endl; 00996 *ok = false; 00997 return INT_MIN; 00998 } else { 00999 *ok = true; 01000 return int( value * f + ( value < 0 ? -0.5 : 0.5 ) ); 01001 } 01002 } 01003 01004 double mapToDouble( int value ) const { 01005 return double(value) * basicStep(); 01006 } 01007 01008 int mPrecision; 01009 KDoubleSpinBoxValidator * mValidator; 01010 }; 01011 01012 KDoubleSpinBox::KDoubleSpinBox( TQWidget * parent, const char * name ) 01013 : TQSpinBox( parent, name ) 01014 { 01015 editor()->setAlignment( Qt::AlignRight ); 01016 d = new Private(); 01017 updateValidator(); 01018 connect( this, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(slotValueChanged(int)) ); 01019 } 01020 01021 KDoubleSpinBox::KDoubleSpinBox( double lower, double upper, double step, 01022 double value, int precision, 01023 TQWidget * parent, const char * name ) 01024 : TQSpinBox( parent, name ) 01025 { 01026 editor()->setAlignment( Qt::AlignRight ); 01027 d = new Private(); 01028 setRange( lower, upper, step, precision ); 01029 setValue( value ); 01030 connect( this, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(slotValueChanged(int)) ); 01031 } 01032 01033 KDoubleSpinBox::~KDoubleSpinBox() { 01034 delete d; d = 0; 01035 } 01036 01037 bool KDoubleSpinBox::acceptLocalizedNumbers() const { 01038 if ( !d->mValidator ) return true; // we'll set one that does; 01039 // can't do it now, since we're const 01040 return d->mValidator->acceptLocalizedNumbers(); 01041 } 01042 01043 void KDoubleSpinBox::setAcceptLocalizedNumbers( bool accept ) { 01044 if ( !d->mValidator ) updateValidator(); 01045 d->mValidator->setAcceptLocalizedNumbers( accept ); 01046 } 01047 01048 void KDoubleSpinBox::setRange( double lower, double upper, double step, 01049 int precision ) { 01050 lower = kMin(upper, lower); 01051 upper = kMax(upper, lower); 01052 setPrecision( precision, true ); // disable bounds checking, since 01053 setMinValue( lower ); // it's done in set{Min,Max}Value 01054 setMaxValue( upper ); // anyway and we want lower, upper 01055 setLineStep( step ); // and step to have the right precision 01056 } 01057 01058 int KDoubleSpinBox::precision() const { 01059 return d->mPrecision; 01060 } 01061 01062 void KDoubleSpinBox::setPrecision( int precision ) { 01063 setPrecision( precision, false ); 01064 } 01065 01066 void KDoubleSpinBox::setPrecision( int precision, bool force ) { 01067 if ( precision < 0 ) return; 01068 if ( !force ) { 01069 int maxPrec = maxPrecision(); 01070 if ( precision > maxPrec ) 01071 { 01072 precision = maxPrec; 01073 } 01074 } 01075 // Update minValue, maxValue, value and lineStep to match the precision change 01076 int oldPrecision = d->mPrecision; 01077 double oldValue = value(); 01078 double oldMinValue = minValue(); 01079 double oldMaxValue = maxValue(); 01080 double oldLineStep = lineStep(); 01081 d->mPrecision = precision; 01082 if (precision != oldPrecision) 01083 { 01084 setMinValue(oldMinValue); 01085 setMaxValue(oldMaxValue); 01086 setValue(oldValue); 01087 setLineStep(oldLineStep); 01088 } 01089 updateValidator(); 01090 } 01091 01092 int KDoubleSpinBox::maxPrecision() const { 01093 // INT_MAX must be > maxAbsValue * 10^precision 01094 // ==> 10^precision < INT_MAX / maxAbsValue 01095 // ==> precision < log10 ( INT_MAX / maxAbsValue ) 01096 // ==> maxPrecision = floor( log10 ( INT_MAX / maxAbsValue ) ); 01097 double maxAbsValue = kMax( fabs(minValue()), fabs(maxValue()) ); 01098 if ( maxAbsValue == 0 ) return 6; // return arbitrary value to avoid dbz... 01099 01100 return int( floor( log10( double(INT_MAX) / maxAbsValue ) ) ); 01101 } 01102 01103 double KDoubleSpinBox::value() const { 01104 return d->mapToDouble( base::value() ); 01105 } 01106 01107 void KDoubleSpinBox::setValue( double value ) { 01108 if ( value == this->value() ) return; 01109 if ( value < minValue() ) 01110 base::setValue( base::minValue() ); 01111 else if ( value > maxValue() ) 01112 base::setValue( base::maxValue() ); 01113 else { 01114 bool ok = false; 01115 base::setValue( d->mapToInt( value, &ok ) ); 01116 assert( ok ); 01117 } 01118 } 01119 01120 double KDoubleSpinBox::minValue() const { 01121 return d->mapToDouble( base::minValue() ); 01122 } 01123 01124 void KDoubleSpinBox::setMinValue( double value ) { 01125 bool ok = false; 01126 int min = d->mapToInt( value, &ok ); 01127 base::setMinValue( min ); 01128 updateValidator(); 01129 } 01130 01131 01132 double KDoubleSpinBox::maxValue() const { 01133 return d->mapToDouble( base::maxValue() ); 01134 } 01135 01136 void KDoubleSpinBox::setMaxValue( double value ) { 01137 bool ok = false; 01138 int max = d->mapToInt( value, &ok ); 01139 base::setMaxValue( max ); 01140 updateValidator(); 01141 } 01142 01143 double KDoubleSpinBox::lineStep() const { 01144 return d->mapToDouble( base::lineStep() ); 01145 } 01146 01147 void KDoubleSpinBox::setLineStep( double step ) { 01148 bool ok = false; 01149 if ( step > maxValue() - minValue() ) 01150 base::setLineStep( 1 ); 01151 else 01152 base::setLineStep( kMax( d->mapToInt( step, &ok ), 1 ) ); 01153 } 01154 01155 TQString KDoubleSpinBox::mapValueToText( int value ) { 01156 if ( acceptLocalizedNumbers() ) 01157 return TDEGlobal::locale() 01158 ->formatNumber( d->mapToDouble( value ), d->mPrecision ); 01159 else 01160 return TQString().setNum( d->mapToDouble( value ), 'f', d->mPrecision ); 01161 } 01162 01163 int KDoubleSpinBox::mapTextToValue( bool * ok ) { 01164 double value; 01165 if ( acceptLocalizedNumbers() ) 01166 value = TDEGlobal::locale()->readNumber( cleanText(), ok ); 01167 else 01168 value = cleanText().toDouble( ok ); 01169 if ( !*ok ) return 0; 01170 if ( value > maxValue() ) 01171 value = maxValue(); 01172 else if ( value < minValue() ) 01173 value = minValue(); 01174 return d->mapToInt( value, ok ); 01175 } 01176 01177 void KDoubleSpinBox::setValidator( const TQValidator * ) { 01178 // silently discard the new validator. We don't want another one ;-) 01179 } 01180 01181 void KDoubleSpinBox::slotValueChanged( int value ) { 01182 emit valueChanged( d->mapToDouble( value ) ); 01183 } 01184 01185 void KDoubleSpinBox::updateValidator() { 01186 if ( !d->mValidator ) { 01187 d->mValidator = new KDoubleSpinBoxValidator( minValue(), maxValue(), precision(), 01188 this, "d->mValidator" ); 01189 base::setValidator( d->mValidator ); 01190 } else 01191 d->mValidator->setRange( minValue(), maxValue(), precision() ); 01192 } 01193 01194 void KNumInput::virtual_hook( int, void* ) 01195 { /*BASE::virtual_hook( id, data );*/ } 01196 01197 void KIntNumInput::virtual_hook( int id, void* data ) 01198 { KNumInput::virtual_hook( id, data ); } 01199 01200 void KDoubleNumInput::virtual_hook( int id, void* data ) 01201 { KNumInput::virtual_hook( id, data ); } 01202 01203 void KIntSpinBox::virtual_hook( int, void* ) 01204 { /*BASE::virtual_hook( id, data );*/ } 01205 01206 void KDoubleSpinBox::virtual_hook( int, void* ) 01207 { /*BASE::virtual_hook( id, data );*/ } 01208 01209 #include "knuminput.moc"