File indexing completed on 2024-04-21 14:55:56
0001 /* This file is part of the KDE libraries 0002 * Initial implementation: 0003 * Copyright (c) 1997 Patrick Dowler <dowler@morgul.fsh.uvic.ca> 0004 * Rewritten and maintained by: 0005 * Copyright (c) 2000 Dirk Mueller <mueller@kde.org> 0006 * 0007 * This library is free software; you can redistribute it and/or 0008 * modify it under the terms of the GNU Library General Public 0009 * License as published by the Free Software Foundation; either 0010 * version 2 of the License, or (at your option) any later version. 0011 * 0012 * This library is distributed in the hope that it will be useful, 0013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0015 * Library General Public License for more details. 0016 * 0017 * You should have received a copy of the GNU Library General Public License 0018 * along with this library; see the file COPYING.LIB. If not, write to 0019 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0020 * Boston, MA 02110-1301, USA. 0021 */ 0022 0023 #include "knuminput.h" 0024 0025 #include <config-kdelibs4support.h> 0026 #if HAVE_LIMITS_H 0027 #include <limits.h> 0028 #endif 0029 0030 #include <cmath> 0031 0032 #include <QApplication> 0033 #include <QLabel> 0034 #include <QLineEdit> 0035 #include <QResizeEvent> 0036 #include <QSlider> 0037 #include <QStyle> 0038 0039 #include <kconfigdialogmanager.h> 0040 #include <kdebug.h> 0041 #include <klocalizedstring.h> 0042 0043 static inline int calcDiffByTen(int x, int y) 0044 { 0045 // calculate ( x - y ) / 10 without overflowing ints: 0046 return (x / 10) - (y / 10) + (x % 10 - y % 10) / 10; 0047 } 0048 0049 // ---------------------------------------------------------------------------- 0050 0051 class KNumInputPrivate 0052 { 0053 public: 0054 KNumInputPrivate(KNumInput *q, KNumInput *below = nullptr) : 0055 q(q), 0056 previousNumInput(nullptr), 0057 nextNumInput(nullptr), 0058 column1Width(0), 0059 column2Width(0), 0060 label(nullptr), 0061 slider(nullptr), 0062 labelAlignment() 0063 { 0064 if (below) { 0065 nextNumInput = below->d->nextNumInput; 0066 previousNumInput = below; 0067 below->d->nextNumInput = q; 0068 if (nextNumInput) { 0069 nextNumInput->d->previousNumInput = q; 0070 } 0071 } 0072 } 0073 0074 static KNumInputPrivate *get(const KNumInput *i) 0075 { 0076 return i->d; 0077 } 0078 0079 KNumInput *q; 0080 KNumInput *previousNumInput, *nextNumInput; 0081 int column1Width, column2Width; 0082 0083 QLabel *label; 0084 QSlider *slider; 0085 QSize sliderSize, labelSize; 0086 0087 Qt::Alignment labelAlignment; 0088 }; 0089 0090 #define K_USING_KNUMINPUT_P(_d) KNumInputPrivate *_d = KNumInputPrivate::get(this) 0091 0092 KNumInput::KNumInput(QWidget *parent) 0093 : QWidget(parent), d(new KNumInputPrivate(this)) 0094 { 0095 setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); 0096 setFocusPolicy(Qt::StrongFocus); 0097 KConfigDialogManager::changedMap()->insert("KIntNumInput", SIGNAL(valueChanged(int))); 0098 KConfigDialogManager::changedMap()->insert("KIntSpinBox", SIGNAL(valueChanged(int))); 0099 KConfigDialogManager::changedMap()->insert("KDoubleSpinBox", SIGNAL(valueChanged(double))); 0100 } 0101 0102 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED 0103 KNumInput::KNumInput(QWidget *parent, KNumInput *below) 0104 : QWidget(parent), d(new KNumInputPrivate(this, below)) 0105 { 0106 setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); 0107 setFocusPolicy(Qt::StrongFocus); 0108 } 0109 #endif 0110 0111 KNumInput::~KNumInput() 0112 { 0113 if (d->previousNumInput) { 0114 d->previousNumInput->d->nextNumInput = d->nextNumInput; 0115 } 0116 0117 if (d->nextNumInput) { 0118 d->nextNumInput->d->previousNumInput = d->previousNumInput; 0119 } 0120 0121 delete d; 0122 } 0123 0124 QSlider *KNumInput::slider() const 0125 { 0126 return d->slider; 0127 } 0128 0129 bool KNumInput::showSlider() const 0130 { 0131 return d->slider; 0132 } 0133 0134 void KNumInput::setLabel(const QString &label, Qt::Alignment a) 0135 { 0136 if (label.isEmpty()) { 0137 delete d->label; 0138 d->label = nullptr; 0139 d->labelAlignment = Qt::Alignment(); 0140 } else { 0141 if (!d->label) { 0142 d->label = new QLabel(this); 0143 } 0144 d->label->setText(label); 0145 d->label->setObjectName("KNumInput::QLabel"); 0146 d->label->setAlignment(a); 0147 // if no vertical alignment set, use Top alignment 0148 if (!(a & (Qt::AlignTop | Qt::AlignBottom | Qt::AlignVCenter))) { 0149 a |= Qt::AlignTop; 0150 } 0151 d->labelAlignment = a; 0152 } 0153 0154 layout(true); 0155 } 0156 0157 QString KNumInput::label() const 0158 { 0159 return d->label ? d->label->text() : QString(); 0160 } 0161 0162 void KNumInput::layout(bool deep) 0163 { 0164 int w1 = d->column1Width; 0165 int w2 = d->column2Width; 0166 0167 // label sizeHint 0168 d->labelSize = (d->label ? d->label->sizeHint() : QSize(0, 0)); 0169 0170 if (d->label && (d->labelAlignment & Qt::AlignVCenter)) { 0171 d->column1Width = d->labelSize.width() + 4; 0172 } else { 0173 d->column1Width = 0; 0174 } 0175 0176 // slider sizeHint 0177 d->sliderSize = (d->slider ? d->slider->sizeHint() : QSize(0, 0)); 0178 0179 doLayout(); 0180 0181 if (!deep) { 0182 d->column1Width = w1; 0183 d->column2Width = w2; 0184 return; 0185 } 0186 0187 w2 = d->column2Width; 0188 0189 KNumInput *p = d->previousNumInput; 0190 while (p) { 0191 p->doLayout(); 0192 w1 = qMax(w1, p->d->column1Width); 0193 w2 = qMax(w2, p->d->column2Width); 0194 p = p->d->previousNumInput; 0195 } 0196 0197 p = d->nextNumInput; 0198 while (p) { 0199 p->doLayout(); 0200 w1 = qMax(w1, p->d->column1Width); 0201 w2 = qMax(w2, p->d->column2Width); 0202 p = p->d->nextNumInput; 0203 } 0204 0205 p = this; 0206 while (p) { 0207 p->d->column1Width = w1; 0208 p->d->column2Width = w2; 0209 p = p->d->previousNumInput; 0210 } 0211 0212 p = d->nextNumInput; 0213 while (p) { 0214 p->d->column1Width = w1; 0215 p->d->column2Width = w2; 0216 p = p->d->nextNumInput; 0217 } 0218 0219 // kDebug() << "w1 " << w1 << " w2 " << w2; 0220 } 0221 0222 QSize KNumInput::sizeHint() const 0223 { 0224 return minimumSizeHint(); 0225 } 0226 0227 void KNumInput::setSteps(int minor, int major) 0228 { 0229 if (d->slider) { 0230 d->slider->setSingleStep(minor); 0231 d->slider->setPageStep(major); 0232 } 0233 } 0234 0235 // ---------------------------------------------------------------------------- 0236 0237 class Q_DECL_HIDDEN KIntSpinBox::KIntSpinBoxPrivate 0238 { 0239 public: 0240 KIntSpinBoxPrivate(KIntSpinBox *q, int val_base = 10): q(q), val_base(val_base) 0241 { 0242 connect(q, SIGNAL(valueChanged(int)), q, SLOT(updateSuffix(int))); 0243 } 0244 0245 void updateSuffix(int value) 0246 { 0247 if (!pluralSuffix.isEmpty()) { 0248 KLocalizedString s = pluralSuffix; 0249 q->setSuffix(s.subs(value).toString()); 0250 } 0251 } 0252 0253 KIntSpinBox *q; 0254 int val_base; 0255 KLocalizedString pluralSuffix; 0256 }; 0257 0258 KIntSpinBox::KIntSpinBox(QWidget *parent) 0259 : QSpinBox(parent), d(new KIntSpinBoxPrivate(this)) 0260 { 0261 setValue(0); 0262 } 0263 0264 KIntSpinBox::~KIntSpinBox() 0265 { 0266 delete d; 0267 } 0268 0269 KIntSpinBox::KIntSpinBox(int lower, int upper, int singleStep, int value, QWidget *parent, int base) 0270 : QSpinBox(parent), d(new KIntSpinBoxPrivate(this, base)) 0271 { 0272 setRange(lower, upper); 0273 setSingleStep(singleStep); 0274 setValue(value); 0275 } 0276 0277 void KIntSpinBox::setBase(int base) 0278 { 0279 d->val_base = base; 0280 } 0281 0282 int KIntSpinBox::base() const 0283 { 0284 return d->val_base; 0285 } 0286 0287 QString KIntSpinBox::textFromValue(int v) const 0288 { 0289 return QString::number(v, d->val_base); 0290 } 0291 0292 int KIntSpinBox::valueFromText(const QString &text) const 0293 { 0294 bool ok; 0295 QString theText = text; 0296 if (theText.startsWith(prefix())) { 0297 theText.remove(0, prefix().length()); 0298 } 0299 if (theText.endsWith(suffix())) { 0300 theText.chop(suffix().length()); 0301 } 0302 return theText.trimmed().toInt(&ok, d->val_base); 0303 } 0304 0305 void KIntSpinBox::setEditFocus(bool mark) 0306 { 0307 lineEdit()->setFocus(); 0308 if (mark) { 0309 lineEdit()->selectAll(); 0310 } 0311 } 0312 0313 void KIntSpinBox::setSuffix(const KLocalizedString &suffix) 0314 { 0315 d->pluralSuffix = suffix; 0316 if (suffix.isEmpty()) { 0317 setSuffix(QString()); 0318 } else { 0319 d->updateSuffix(value()); 0320 } 0321 } 0322 0323 // ---------------------------------------------------------------------------- 0324 0325 class Q_DECL_HIDDEN KIntNumInput::KIntNumInputPrivate 0326 { 0327 public: 0328 KIntNumInput *q; 0329 int referencePoint; 0330 short blockRelative; 0331 KIntSpinBox *intSpinBox; 0332 QSize intSpinBoxSize; 0333 0334 KIntNumInputPrivate(KIntNumInput *q, int r) 0335 : q(q), 0336 referencePoint(r), 0337 blockRelative(0) {} 0338 }; 0339 0340 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED 0341 KIntNumInput::KIntNumInput(KNumInput *below, int val, QWidget *parent, int _base) 0342 : KNumInput(parent, below) 0343 , d(new KIntNumInputPrivate(this, val)) 0344 { 0345 initWidget(val, _base); 0346 } 0347 #endif 0348 0349 KIntNumInput::KIntNumInput(QWidget *parent) 0350 : KNumInput(parent) 0351 , d(new KIntNumInputPrivate(this, 0)) 0352 { 0353 initWidget(0, 10); 0354 } 0355 0356 KIntNumInput::KIntNumInput(int val, QWidget *parent, int _base) 0357 : KNumInput(parent) 0358 , d(new KIntNumInputPrivate(this, val)) 0359 { 0360 initWidget(val, _base); 0361 } 0362 0363 QSpinBox *KIntNumInput::spinBox() const 0364 { 0365 return d->intSpinBox; 0366 } 0367 0368 void KIntNumInput::initWidget(int val, int _base) 0369 { 0370 d->intSpinBox = new KIntSpinBox(INT_MIN, INT_MAX, 1, val, this, _base); 0371 d->intSpinBox->setObjectName("KIntNumInput::KIntSpinBox"); 0372 // the KIntValidator is broken beyond believe for 0373 // spinboxes which have suffix or prefix texts, so 0374 // better don't use it unless absolutely necessary 0375 0376 if (_base != 10) { 0377 kWarning() << "WARNING: Validation is broken in KIntNumInput! Needs to be fixed."; 0378 // d->intSpinBox->setValidator(new KIntValidator(this, _base, "KNumInput::KIntValidator")); 0379 } 0380 0381 connect(d->intSpinBox, SIGNAL(valueChanged(int)), SLOT(spinValueChanged(int))); 0382 connect(this, SIGNAL(valueChanged(int)), 0383 SLOT(slotEmitRelativeValueChanged(int))); 0384 0385 setFocusProxy(d->intSpinBox); 0386 layout(true); 0387 } 0388 0389 void KIntNumInput::setReferencePoint(int ref) 0390 { 0391 // clip to valid range: 0392 ref = qMin(maximum(), qMax(minimum(), ref)); 0393 d->referencePoint = ref; 0394 } 0395 0396 int KIntNumInput::referencePoint() const 0397 { 0398 return d->referencePoint; 0399 } 0400 0401 void KIntNumInput::spinValueChanged(int val) 0402 { 0403 K_USING_KNUMINPUT_P(priv); 0404 0405 if (priv->slider) { 0406 priv->slider->setValue(val); 0407 } 0408 0409 emit valueChanged(val); 0410 } 0411 0412 void KIntNumInput::slotEmitRelativeValueChanged(int value) 0413 { 0414 if (d->blockRelative || !d->referencePoint) { 0415 return; 0416 } 0417 emit relativeValueChanged(double(value) / double(d->referencePoint)); 0418 } 0419 0420 void KIntNumInput::setSliderEnabled(bool slider) 0421 { 0422 K_USING_KNUMINPUT_P(priv); 0423 if (slider) { 0424 if (!priv->slider) { 0425 priv->slider = new QSlider(Qt::Horizontal, this); 0426 connect(priv->slider, SIGNAL(valueChanged(int)), 0427 d->intSpinBox, SLOT(setValue(int))); 0428 priv->slider->setTickPosition(QSlider::TicksBelow); 0429 layout(true); 0430 } 0431 0432 const int value = d->intSpinBox->value(); 0433 priv->slider->setRange(d->intSpinBox->minimum(), d->intSpinBox->maximum()); 0434 priv->slider->setPageStep(d->intSpinBox->singleStep()); 0435 priv->slider->setValue(value); 0436 0437 // calculate (upper-lower)/10 without overflowing int's: 0438 const int major = calcDiffByTen(d->intSpinBox->maximum(), d->intSpinBox->minimum()); 0439 0440 priv->slider->setSingleStep(d->intSpinBox->singleStep()); 0441 priv->slider->setPageStep(qMax(1, major)); 0442 priv->slider->setTickInterval(major); 0443 } else { 0444 if (priv->slider) { 0445 layout(true); 0446 } 0447 delete priv->slider; 0448 priv->slider = nullptr; 0449 } 0450 } 0451 0452 void KIntNumInput::setRange(int lower, int upper, int singleStep) 0453 { 0454 if (upper < lower || singleStep <= 0) { 0455 kWarning() << "WARNING: KIntNumInput::setRange() called with bad arguments. Ignoring call..."; 0456 return; 0457 } 0458 0459 d->intSpinBox->setMinimum(lower); 0460 d->intSpinBox->setMaximum(upper); 0461 d->intSpinBox->setSingleStep(singleStep); 0462 0463 singleStep = d->intSpinBox->singleStep(); // maybe QRangeControl didn't like our lineStep? 0464 0465 // check that reference point is still inside valid range: 0466 setReferencePoint(referencePoint()); 0467 0468 layout(true); 0469 0470 // update slider information if it's shown 0471 K_USING_KNUMINPUT_P(priv); 0472 setSliderEnabled(priv->slider); 0473 } 0474 0475 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED 0476 void KIntNumInput::setRange(int lower, int upper, int singleStep, bool slider) 0477 { 0478 setRange(lower, upper, singleStep); 0479 setSliderEnabled(slider); 0480 } 0481 #endif 0482 0483 void KIntNumInput::setMinimum(int min) 0484 { 0485 setRange(min, d->intSpinBox->maximum(), d->intSpinBox->singleStep()); 0486 } 0487 0488 int KIntNumInput::minimum() const 0489 { 0490 return d->intSpinBox->minimum(); 0491 } 0492 0493 void KIntNumInput::setMaximum(int max) 0494 { 0495 setRange(d->intSpinBox->minimum(), max, d->intSpinBox->singleStep()); 0496 } 0497 0498 int KIntNumInput::maximum() const 0499 { 0500 return d->intSpinBox->maximum(); 0501 } 0502 0503 int KIntNumInput::singleStep() const 0504 { 0505 return d->intSpinBox->singleStep(); 0506 } 0507 0508 void KIntNumInput::setSingleStep(int singleStep) 0509 { 0510 d->intSpinBox->setSingleStep(singleStep); 0511 } 0512 0513 void KIntNumInput::setSuffix(const QString &suffix) 0514 { 0515 d->intSpinBox->setSuffix(suffix); 0516 0517 layout(true); 0518 } 0519 0520 void KIntNumInput::setSuffix(const KLocalizedString &suffix) 0521 { 0522 d->intSpinBox->setSuffix(suffix); 0523 layout(true); 0524 } 0525 0526 QString KIntNumInput::suffix() const 0527 { 0528 return d->intSpinBox->suffix(); 0529 } 0530 0531 void KIntNumInput::setPrefix(const QString &prefix) 0532 { 0533 d->intSpinBox->setPrefix(prefix); 0534 0535 layout(true); 0536 } 0537 0538 QString KIntNumInput::prefix() const 0539 { 0540 return d->intSpinBox->prefix(); 0541 } 0542 0543 void KIntNumInput::setEditFocus(bool mark) 0544 { 0545 d->intSpinBox->setEditFocus(mark); 0546 } 0547 0548 QSize KIntNumInput::minimumSizeHint() const 0549 { 0550 K_USING_KNUMINPUT_P(priv); 0551 ensurePolished(); 0552 0553 int w; 0554 int h; 0555 0556 h = qMax(d->intSpinBoxSize.height(), priv->sliderSize.height()); 0557 0558 // if in extra row, then count it here 0559 if (priv->label && (priv->labelAlignment & (Qt::AlignBottom | Qt::AlignTop))) { 0560 h += 4 + priv->labelSize.height(); 0561 } else { 0562 // label is in the same row as the other widgets 0563 h = qMax(h, priv->labelSize.height() + 2); 0564 } 0565 0566 const int spacingHint = style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); 0567 w = priv->slider ? priv->slider->sizeHint().width() + spacingHint : 0; 0568 w += priv->column1Width + priv->column2Width; 0569 0570 if (priv->labelAlignment & (Qt::AlignTop | Qt::AlignBottom)) { 0571 w = qMax(w, priv->labelSize.width() + 4); 0572 } 0573 0574 return QSize(w, h); 0575 } 0576 0577 void KIntNumInput::doLayout() 0578 { 0579 K_USING_KNUMINPUT_P(priv); 0580 0581 d->intSpinBoxSize = d->intSpinBox->sizeHint(); 0582 priv->column2Width = d->intSpinBoxSize.width(); 0583 0584 if (priv->label) { 0585 priv->label->setBuddy(d->intSpinBox); 0586 } 0587 } 0588 0589 void KIntNumInput::resizeEvent(QResizeEvent *e) 0590 { 0591 K_USING_KNUMINPUT_P(priv); 0592 0593 int w = priv->column1Width; 0594 int h = 0; 0595 const int spacingHint = style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); 0596 0597 if (priv->label && (priv->labelAlignment & Qt::AlignTop)) { 0598 priv->label->setGeometry(0, 0, e->size().width(), priv->labelSize.height()); 0599 h += priv->labelSize.height() + spacingHint; 0600 } 0601 0602 if (priv->label && (priv->labelAlignment & Qt::AlignVCenter)) { 0603 priv->label->setGeometry(0, 0, w, d->intSpinBoxSize.height()); 0604 } 0605 0606 if (qApp->layoutDirection() == Qt::RightToLeft) { 0607 d->intSpinBox->setGeometry(w, h, priv->slider ? priv->column2Width : qMax(priv->column2Width, e->size().width() - w), d->intSpinBoxSize.height()); 0608 w += priv->column2Width + spacingHint; 0609 0610 if (priv->slider) { 0611 priv->slider->setGeometry(w, h, e->size().width() - w, d->intSpinBoxSize.height() + spacingHint); 0612 } 0613 } else if (priv->slider) { 0614 priv->slider->setGeometry(w, h, e->size().width() - (w + priv->column2Width + spacingHint), d->intSpinBoxSize.height() + spacingHint); 0615 d->intSpinBox->setGeometry(w + priv->slider->size().width() + spacingHint, h, priv->column2Width, d->intSpinBoxSize.height()); 0616 } else { 0617 d->intSpinBox->setGeometry(w, h, qMax(priv->column2Width, e->size().width() - w), d->intSpinBoxSize.height()); 0618 } 0619 0620 h += d->intSpinBoxSize.height() + 2; 0621 0622 if (priv->label && (priv->labelAlignment & Qt::AlignBottom)) { 0623 priv->label->setGeometry(0, h, priv->labelSize.width(), priv->labelSize.height()); 0624 } 0625 } 0626 0627 KIntNumInput::~KIntNumInput() 0628 { 0629 delete d; 0630 } 0631 0632 void KIntNumInput::setValue(int val) 0633 { 0634 d->intSpinBox->setValue(val); 0635 // slider value is changed by spinValueChanged 0636 } 0637 0638 void KIntNumInput::setRelativeValue(double r) 0639 { 0640 if (!d->referencePoint) { 0641 return; 0642 } 0643 ++d->blockRelative; 0644 setValue(qRound(d->referencePoint * r + 0.5)); 0645 --d->blockRelative; 0646 } 0647 0648 double KIntNumInput::relativeValue() const 0649 { 0650 if (!d->referencePoint) { 0651 return 0; 0652 } 0653 return double(value()) / double(d->referencePoint); 0654 } 0655 0656 int KIntNumInput::value() const 0657 { 0658 return d->intSpinBox->value(); 0659 } 0660 0661 void KIntNumInput::setSpecialValueText(const QString &text) 0662 { 0663 d->intSpinBox->setSpecialValueText(text); 0664 layout(true); 0665 } 0666 0667 QString KIntNumInput::specialValueText() const 0668 { 0669 return d->intSpinBox->specialValueText(); 0670 } 0671 0672 void KIntNumInput::setLabel(const QString &label, Qt::Alignment a) 0673 { 0674 K_USING_KNUMINPUT_P(priv); 0675 0676 KNumInput::setLabel(label, a); 0677 0678 if (priv->label) { 0679 priv->label->setBuddy(d->intSpinBox); 0680 } 0681 } 0682 0683 // ---------------------------------------------------------------------------- 0684 0685 class Q_DECL_HIDDEN KDoubleNumInput::KDoubleNumInputPrivate 0686 { 0687 public: 0688 KDoubleNumInputPrivate(double r) 0689 : spin(nullptr), 0690 referencePoint(r), 0691 blockRelative(0), 0692 exponentRatio(1.0) {} 0693 QDoubleSpinBox *spin; 0694 double referencePoint; 0695 short blockRelative; 0696 QSize editSize; 0697 QString specialValue; 0698 double exponentRatio; 0699 }; 0700 0701 KDoubleNumInput::KDoubleNumInput(QWidget *parent) 0702 : KNumInput(parent) 0703 , d(new KDoubleNumInputPrivate(0.0)) 0704 0705 { 0706 initWidget(0.0, 0.0, 9999.0, 0.01, 2); 0707 } 0708 0709 KDoubleNumInput::KDoubleNumInput(double lower, double upper, double value, QWidget *parent, 0710 double singleStep, int precision) 0711 : KNumInput(parent) 0712 , d(new KDoubleNumInputPrivate(value)) 0713 { 0714 initWidget(value, lower, upper, singleStep, precision); 0715 } 0716 0717 #ifndef KDELIBS4SUPPORT_NO_DEPRECATED 0718 KDoubleNumInput::KDoubleNumInput(KNumInput *below, 0719 double lower, double upper, double value, QWidget *parent, 0720 double singleStep, int precision) 0721 : KNumInput(parent, below) 0722 , d(new KDoubleNumInputPrivate(value)) 0723 { 0724 initWidget(value, lower, upper, singleStep, precision); 0725 } 0726 #endif 0727 0728 KDoubleNumInput::~KDoubleNumInput() 0729 { 0730 delete d; 0731 } 0732 0733 QString KDoubleNumInput::specialValueText() const 0734 { 0735 return d->specialValue; 0736 } 0737 0738 void KDoubleNumInput::initWidget(double value, double lower, double upper, 0739 double singleStep, int precision) 0740 { 0741 d->spin = new QDoubleSpinBox(this); 0742 d->spin->setRange(lower, upper); 0743 d->spin->setSingleStep(singleStep); 0744 d->spin->setValue(value); 0745 d->spin->setDecimals(precision); 0746 0747 d->spin->setObjectName("KDoubleNumInput::QDoubleSpinBox"); 0748 setFocusProxy(d->spin); 0749 connect(d->spin, SIGNAL(valueChanged(double)), 0750 this, SIGNAL(valueChanged(double))); 0751 connect(this, SIGNAL(valueChanged(double)), 0752 this, SLOT(slotEmitRelativeValueChanged(double))); 0753 0754 updateLegacyMembers(); 0755 0756 layout(true); 0757 } 0758 0759 void KDoubleNumInput::updateLegacyMembers() 0760 { 0761 d->specialValue = specialValueText(); 0762 } 0763 0764 double KDoubleNumInput::mapSliderToSpin(int val) const 0765 { 0766 K_USING_KNUMINPUT_P(priv); 0767 0768 // map [slidemin,slidemax] to [spinmin,spinmax] 0769 const double spinmin = d->spin->minimum(); 0770 const double spinmax = d->spin->maximum(); 0771 const double slidemin = priv->slider->minimum(); // cast int to double to avoid 0772 const double slidemax = priv->slider->maximum(); // overflow in rel denominator 0773 const double rel = (double(val) - slidemin) / (slidemax - slidemin); 0774 Q_ASSERT(d->exponentRatio > 0.0); 0775 return spinmin + pow(rel, d->exponentRatio) * (spinmax - spinmin); 0776 } 0777 0778 void KDoubleNumInput::sliderMoved(int val) 0779 { 0780 d->spin->setValue(mapSliderToSpin(val)); 0781 } 0782 0783 void KDoubleNumInput::spinBoxChanged(double val) 0784 { 0785 K_USING_KNUMINPUT_P(priv); 0786 0787 const double spinmin = d->spin->minimum(); 0788 const double spinmax = d->spin->maximum(); 0789 const double slidemin = priv->slider->minimum(); // cast int to double to avoid 0790 const double slidemax = priv->slider->maximum(); // overflow in rel denominator 0791 0792 Q_ASSERT(d->exponentRatio > 0.0); 0793 const double rel = pow((val - spinmin) / (spinmax - spinmin), 1.0 / d->exponentRatio); 0794 0795 if (priv->slider) { 0796 priv->slider->blockSignals(true); 0797 priv->slider->setValue(qRound(slidemin + rel * (slidemax - slidemin))); 0798 priv->slider->blockSignals(false); 0799 } 0800 } 0801 0802 void KDoubleNumInput::slotEmitRelativeValueChanged(double value) 0803 { 0804 if (!d->referencePoint) { 0805 return; 0806 } 0807 emit relativeValueChanged(value / d->referencePoint); 0808 } 0809 0810 QSize KDoubleNumInput::minimumSizeHint() const 0811 { 0812 K_USING_KNUMINPUT_P(priv); 0813 0814 ensurePolished(); 0815 0816 int w; 0817 int h; 0818 0819 h = qMax(d->editSize.height(), priv->sliderSize.height()); 0820 0821 // if in extra row, then count it here 0822 if (priv->label && (priv->labelAlignment & (Qt::AlignBottom | Qt::AlignTop))) { 0823 h += 4 + priv->labelSize.height(); 0824 } else { 0825 // label is in the same row as the other widgets 0826 h = qMax(h, priv->labelSize.height() + 2); 0827 } 0828 0829 const int spacingHint = style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); 0830 w = priv->slider ? priv->slider->sizeHint().width() + spacingHint : 0; 0831 w += priv->column1Width + priv->column2Width; 0832 0833 if (priv->labelAlignment & (Qt::AlignTop | Qt::AlignBottom)) { 0834 w = qMax(w, priv->labelSize.width() + 4); 0835 } 0836 0837 return QSize(w, h); 0838 } 0839 0840 void KDoubleNumInput::resizeEvent(QResizeEvent *e) 0841 { 0842 K_USING_KNUMINPUT_P(priv); 0843 0844 int w = priv->column1Width; 0845 int h = 0; 0846 const int spacingHint = style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); 0847 0848 if (priv->label && (priv->labelAlignment & Qt::AlignTop)) { 0849 priv->label->setGeometry(0, 0, e->size().width(), priv->labelSize.height()); 0850 h += priv->labelSize.height() + 4; 0851 } 0852 0853 if (priv->label && (priv->labelAlignment & Qt::AlignVCenter)) { 0854 priv->label->setGeometry(0, 0, w, d->editSize.height()); 0855 } 0856 0857 if (qApp->layoutDirection() == Qt::RightToLeft) { 0858 d->spin->setGeometry(w, h, priv->slider ? priv->column2Width 0859 : e->size().width() - w, d->editSize.height()); 0860 w += priv->column2Width + spacingHint; 0861 0862 if (priv->slider) { 0863 priv->slider->setGeometry(w, h, e->size().width() - w, d->editSize.height() + spacingHint); 0864 } 0865 } else if (priv->slider) { 0866 priv->slider->setGeometry(w, h, e->size().width() - 0867 (priv->column1Width + priv->column2Width + spacingHint), 0868 d->editSize.height() + spacingHint); 0869 d->spin->setGeometry(w + priv->slider->width() + spacingHint, h, 0870 priv->column2Width, d->editSize.height()); 0871 } else { 0872 d->spin->setGeometry(w, h, e->size().width() - w, d->editSize.height()); 0873 } 0874 0875 h += d->editSize.height() + 2; 0876 0877 if (priv->label && (priv->labelAlignment & Qt::AlignBottom)) { 0878 priv->label->setGeometry(0, h, priv->labelSize.width(), priv->labelSize.height()); 0879 } 0880 } 0881 0882 void KDoubleNumInput::doLayout() 0883 { 0884 K_USING_KNUMINPUT_P(priv); 0885 0886 d->editSize = d->spin->sizeHint(); 0887 priv->column2Width = d->editSize.width(); 0888 } 0889 0890 void KDoubleNumInput::setValue(double val) 0891 { 0892 d->spin->setValue(val); 0893 } 0894 0895 void KDoubleNumInput::setRelativeValue(double r) 0896 { 0897 if (!d->referencePoint) { 0898 return; 0899 } 0900 ++d->blockRelative; 0901 setValue(r * d->referencePoint); 0902 --d->blockRelative; 0903 } 0904 0905 void KDoubleNumInput::setReferencePoint(double ref) 0906 { 0907 // clip to valid range: 0908 ref = qMin(maximum(), qMax(minimum(), ref)); 0909 d->referencePoint = ref; 0910 } 0911 0912 void KDoubleNumInput::setRange(double lower, double upper, double singleStep, 0913 bool slider) 0914 { 0915 K_USING_KNUMINPUT_P(priv); 0916 0917 if (priv->slider) { 0918 // don't update the slider to avoid an endless recursion 0919 QDoubleSpinBox *spin = d->spin; 0920 disconnect(spin, SIGNAL(valueChanged(double)), 0921 priv->slider, SLOT(setValue(int))); 0922 } 0923 d->spin->setRange(lower, upper); 0924 d->spin->setSingleStep(singleStep); 0925 0926 setSliderEnabled(slider); 0927 0928 setReferencePoint(referencePoint()); 0929 0930 layout(true); 0931 updateLegacyMembers(); 0932 } 0933 0934 void KDoubleNumInput::setSliderEnabled(bool enabled) 0935 { 0936 K_USING_KNUMINPUT_P(priv); 0937 if (enabled) { 0938 QDoubleSpinBox *spin = d->spin; 0939 const double range = spin->maximum() - spin->minimum(); 0940 const double steps = range * pow(10.0, spin->decimals()); 0941 if (!priv->slider) { 0942 priv->slider = new QSlider(Qt::Horizontal, this); 0943 priv->slider->setTickPosition(QSlider::TicksBelow); 0944 // feedback line: when one moves, the other moves, too: 0945 connect(priv->slider, SIGNAL(valueChanged(int)), 0946 SLOT(sliderMoved(int))); 0947 layout(true); 0948 } 0949 if (steps > 1000 || d->exponentRatio != 1.0) { 0950 priv->slider->setRange(0, 1000); 0951 priv->slider->setSingleStep(1); 0952 priv->slider->setPageStep(50); 0953 } else { 0954 const int singleSteps = qRound(steps); 0955 priv->slider->setRange(0, singleSteps); 0956 priv->slider->setSingleStep(1); 0957 const int pageSteps = qBound(1, singleSteps / 20, 10); 0958 priv->slider->setPageStep(pageSteps); 0959 } 0960 spinBoxChanged(spin->value()); 0961 connect(spin, SIGNAL(valueChanged(double)), SLOT(spinBoxChanged(double))); 0962 } else { 0963 if (priv->slider) { 0964 layout(true); 0965 } 0966 delete priv->slider; 0967 priv->slider = nullptr; 0968 } 0969 } 0970 0971 void KDoubleNumInput::setMinimum(double min) 0972 { 0973 K_USING_KNUMINPUT_P(priv); 0974 setRange(min, maximum(), d->spin->singleStep(), priv->slider); 0975 } 0976 0977 double KDoubleNumInput::minimum() const 0978 { 0979 return d->spin->minimum(); 0980 } 0981 0982 void KDoubleNumInput::setMaximum(double max) 0983 { 0984 K_USING_KNUMINPUT_P(priv); 0985 setRange(minimum(), max, d->spin->singleStep(), priv->slider); 0986 } 0987 0988 double KDoubleNumInput::maximum() const 0989 { 0990 return d->spin->maximum(); 0991 } 0992 0993 double KDoubleNumInput::singleStep() const 0994 { 0995 return d->spin->singleStep(); 0996 } 0997 0998 void KDoubleNumInput::setSingleStep(double singleStep) 0999 { 1000 d->spin->setSingleStep(singleStep); 1001 } 1002 1003 double KDoubleNumInput::value() const 1004 { 1005 return d->spin->value(); 1006 } 1007 1008 double KDoubleNumInput::relativeValue() const 1009 { 1010 if (!d->referencePoint) { 1011 return 0; 1012 } 1013 return value() / d->referencePoint; 1014 } 1015 1016 double KDoubleNumInput::referencePoint() const 1017 { 1018 return d->referencePoint; 1019 } 1020 1021 QString KDoubleNumInput::suffix() const 1022 { 1023 return d->spin->suffix(); 1024 } 1025 1026 QString KDoubleNumInput::prefix() const 1027 { 1028 return d->spin->prefix(); 1029 } 1030 1031 void KDoubleNumInput::setSuffix(const QString &suffix) 1032 { 1033 d->spin->setSuffix(suffix); 1034 1035 layout(true); 1036 } 1037 1038 void KDoubleNumInput::setPrefix(const QString &prefix) 1039 { 1040 d->spin->setPrefix(prefix); 1041 1042 layout(true); 1043 } 1044 1045 void KDoubleNumInput::setDecimals(int decimals) 1046 { 1047 d->spin->setDecimals(decimals); 1048 1049 layout(true); 1050 } 1051 1052 int KDoubleNumInput::decimals() const 1053 { 1054 return d->spin->decimals(); 1055 } 1056 1057 void KDoubleNumInput::setSpecialValueText(const QString &text) 1058 { 1059 d->spin->setSpecialValueText(text); 1060 1061 layout(true); 1062 updateLegacyMembers(); 1063 } 1064 1065 void KDoubleNumInput::setLabel(const QString &label, Qt::Alignment a) 1066 { 1067 K_USING_KNUMINPUT_P(priv); 1068 1069 KNumInput::setLabel(label, a); 1070 1071 if (priv->label) { 1072 priv->label->setBuddy(d->spin); 1073 } 1074 } 1075 1076 double KDoubleNumInput::exponentRatio() const 1077 { 1078 return d->exponentRatio; 1079 } 1080 1081 void KDoubleNumInput::setExponentRatio(double dbl) 1082 { 1083 Q_ASSERT(dbl > 0.0); 1084 if (dbl > 0.0) { 1085 d->exponentRatio = dbl; 1086 spinBoxChanged(d->spin->value()); // used to reset the value of the slider 1087 } else { 1088 kError() << "ExponentRatio need to be strictly positive."; 1089 } 1090 } 1091 1092 #include "moc_knuminput.cpp"