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