File indexing completed on 2025-04-27 03:58:37

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2014-11-30
0007  * Description : Save space slider widget
0008  *
0009  * SPDX-FileCopyrightText: 2014-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  * SPDX-FileCopyrightText:      2010 by Justin Noel <justin at ics dot com>
0011  * SPDX-FileCopyrightText:      2010 by Cyrille Berger <cberger at cberger dot net>
0012  *
0013  * SPDX-License-Identifier: GPL-2.0-or-later
0014  *
0015  * ============================================================ */
0016 
0017 #include "dsliderspinbox.h"
0018 
0019 // C++ includes
0020 
0021 #include <cmath>
0022 
0023 // Qt includes
0024 
0025 #include <QStyle>
0026 #include <QPainter>
0027 #include <QLineEdit>
0028 #include <QKeyEvent>
0029 #include <QMouseEvent>
0030 #include <QApplication>
0031 #include <QIntValidator>
0032 #include <QDoubleSpinBox>
0033 
0034 namespace Digikam
0035 {
0036 
0037 class Q_DECL_HIDDEN DAbstractSliderSpinBoxPrivate
0038 {
0039 public:
0040 
0041     DAbstractSliderSpinBoxPrivate()
0042       : edit(nullptr),
0043         validator(nullptr),
0044         dummySpinBox(nullptr),
0045         upButtonDown(false),
0046         downButtonDown(false),
0047         factor(1.0),
0048         fastSliderStep(5),
0049         slowFactor(0.1),
0050         shiftPercent(0.0),
0051         shiftMode(false),
0052         exponentRatio(0.0),
0053         value(0),
0054         maximum(100),
0055         minimum(0),
0056         singleStep(1),
0057         style(STYLE_NOQUIRK),
0058         blockUpdateSignalOnDrag(false),
0059         isDragging             (false)
0060     {
0061     }
0062 
0063     enum Style
0064     {
0065         STYLE_NOQUIRK,
0066         STYLE_MACINTOSH,
0067         STYLE_PLASTIQUE,
0068         STYLE_WINDOWS,
0069         STYLE_BREEZE,
0070         STYLE_FUSION,
0071         STYLE_OXYGEN,
0072         STYLE_GTK2
0073     };
0074 
0075     QLineEdit*        edit;
0076     QDoubleValidator* validator;
0077     QSpinBox*         dummySpinBox;
0078     bool              upButtonDown;
0079     bool              downButtonDown;
0080     int               factor;
0081     int               fastSliderStep;
0082     double            slowFactor;
0083     double            shiftPercent;
0084     bool              shiftMode;
0085     QString           prefix;
0086     QString           suffix;
0087     double            exponentRatio;
0088     int               value;
0089     int               maximum;
0090     int               minimum;
0091     int               singleStep;
0092     Style             style;
0093     bool              blockUpdateSignalOnDrag;
0094     bool              isDragging;
0095 };
0096 
0097 DAbstractSliderSpinBox::DAbstractSliderSpinBox(QWidget* const parent, DAbstractSliderSpinBoxPrivate* const q)
0098     : QWidget(parent),
0099       d_ptr(q)
0100 {
0101     Q_D(DAbstractSliderSpinBox);
0102 
0103     QEvent e(QEvent::StyleChange);
0104     this->changeEvent(&e);
0105 
0106     d->edit = new QLineEdit(this);
0107     d->edit->setContentsMargins(QMargins());
0108     d->edit->setAlignment(Qt::AlignCenter);
0109     d->edit->installEventFilter(this);
0110     d->edit->setFrame(false);
0111     d->edit->hide();
0112 
0113     // Make edit transparent
0114 
0115     d->edit->setAutoFillBackground(false);
0116     QPalette pal = d->edit->palette();
0117     pal.setColor(QPalette::Base, Qt::transparent);
0118     d->edit->setPalette(pal);
0119 
0120     connect(d->edit, SIGNAL(editingFinished()),
0121             this, SLOT(editLostFocus()));
0122 
0123     d->validator = new QDoubleValidator(d->edit);
0124     d->edit->setValidator(d->validator);
0125 
0126     setExponentRatio(1.0);
0127 
0128     // Set sane defaults
0129 
0130     setFocusPolicy(Qt::StrongFocus);
0131     setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
0132 
0133     // dummy needed to fix a bug in the polyester theme
0134 
0135     d->dummySpinBox = new QSpinBox(this);
0136     d->dummySpinBox->hide();
0137 }
0138 
0139 DAbstractSliderSpinBox::~DAbstractSliderSpinBox()
0140 {
0141     Q_D(DAbstractSliderSpinBox);
0142     delete d;
0143 }
0144 
0145 void DAbstractSliderSpinBox::showEdit()
0146 {
0147     Q_D(DAbstractSliderSpinBox);
0148 
0149     if (d->edit->isVisible())
0150     {
0151         return;
0152     }
0153 
0154     if (d->style == DAbstractSliderSpinBoxPrivate::STYLE_PLASTIQUE)
0155     {
0156         d->edit->setGeometry(progressRect(spinBoxOptions()).adjusted(0, 0, -2, 0));
0157     }
0158     else
0159     {
0160         d->edit->setGeometry(progressRect(spinBoxOptions()));
0161     }
0162 
0163     d->edit->setText(valueString());
0164     d->edit->selectAll();
0165     d->edit->show();
0166     d->edit->setFocus(Qt::OtherFocusReason);
0167     update();
0168 }
0169 
0170 void DAbstractSliderSpinBox::hideEdit()
0171 {
0172     Q_D(DAbstractSliderSpinBox);
0173 
0174     d->edit->hide();
0175     update();
0176 }
0177 
0178 void DAbstractSliderSpinBox::paintEvent(QPaintEvent* e)
0179 {
0180     Q_D(DAbstractSliderSpinBox);
0181     Q_UNUSED(e)
0182 
0183     QPainter painter(this);
0184 
0185     switch (d->style)
0186     {
0187         case DAbstractSliderSpinBoxPrivate::STYLE_PLASTIQUE:
0188             paintPlastique(painter);
0189             break;
0190 
0191         case DAbstractSliderSpinBoxPrivate::STYLE_BREEZE:
0192             paintBreeze(painter);
0193             break;
0194 
0195         case DAbstractSliderSpinBoxPrivate::STYLE_FUSION:
0196             paintFusion(painter);
0197             break;
0198 
0199         default:
0200             paint(painter);
0201             break;
0202     }
0203 
0204     painter.end();
0205 }
0206 
0207 void DAbstractSliderSpinBox::paint(QPainter& painter)
0208 {
0209     Q_D(DAbstractSliderSpinBox);
0210 
0211     // Create options to draw spin box parts
0212 
0213     QStyleOptionSpinBox spinOpts = spinBoxOptions();
0214     spinOpts.rect.adjust(0, 2, 0, -2);
0215 
0216     // Draw "SpinBox".Clip off the area of the lineEdit to avoid double
0217     // borders being drawn
0218 
0219     painter.save();
0220     painter.setClipping(true);
0221 
0222     QRect eraseRect(QPoint(rect().x(), rect().y()),
0223                     QPoint(progressRect(spinOpts).right(), rect().bottom()));
0224 
0225     painter.setClipRegion(QRegion(rect()).subtracted(eraseRect));
0226     style()->drawComplexControl(QStyle::CC_SpinBox, &spinOpts, &painter, d->dummySpinBox);
0227     painter.setClipping(false);
0228     painter.restore();
0229 
0230     QStyleOptionProgressBar progressOpts = progressBarOptions();
0231 
0232     if ((d->style == DAbstractSliderSpinBoxPrivate::STYLE_GTK2)    ||
0233         (d->style == DAbstractSliderSpinBoxPrivate::STYLE_OXYGEN)  ||
0234         (d->style == DAbstractSliderSpinBoxPrivate::STYLE_WINDOWS) ||
0235         (d->style == DAbstractSliderSpinBoxPrivate::STYLE_MACINTOSH))
0236     {
0237         progressOpts.state |= QStyle::State_Horizontal;
0238     }
0239 
0240     progressOpts.rect.adjust(0, 2, 0, -2);
0241     style()->drawControl(QStyle::CE_ProgressBar, &progressOpts, &painter, nullptr);
0242 
0243     // Draw focus if necessary
0244 
0245     if (hasFocus() && d->edit->hasFocus())
0246     {
0247         QStyleOptionFocusRect focusOpts;
0248         focusOpts.initFrom(this);
0249         focusOpts.rect = progressOpts.rect;
0250         focusOpts.backgroundColor = palette().color(QPalette::Window);
0251         style()->drawPrimitive(QStyle::PE_FrameFocusRect, &focusOpts, &painter, this);
0252     }
0253 }
0254 
0255 void DAbstractSliderSpinBox::paintFusion(QPainter& painter)
0256 {
0257     Q_D(DAbstractSliderSpinBox);
0258 
0259     QStyleOptionSpinBox spinOpts         = spinBoxOptions();
0260     QStyleOptionProgressBar progressOpts = progressBarOptions();
0261     spinOpts.frame                       = true;
0262     spinOpts.rect.adjust(0, -1, 0, 1);
0263 /*
0264     spinOpts.palette().setBrush(QPalette::Base, palette().highlight());
0265 */
0266     style()->drawComplexControl(QStyle::CC_SpinBox, &spinOpts, &painter, d->dummySpinBox);
0267 
0268     painter.save();
0269 
0270     QRect rect = progressOpts.rect.adjusted(1, 2, -4, -2);
0271     QRect leftRect;
0272 
0273     int progressIndicatorPos = (progressOpts.progress - double(progressOpts.minimum)) / qMax(double(1.0),
0274                                 double(progressOpts.maximum) - progressOpts.minimum) * rect.width();
0275 
0276     if      ((progressIndicatorPos >= 0) && (progressIndicatorPos <= rect.width()))
0277     {
0278         leftRect = QRect(rect.left(), rect.top(), progressIndicatorPos, rect.height());
0279     }
0280     else if (progressIndicatorPos > rect.width())
0281     {
0282         painter.setPen(palette().highlightedText().color());
0283     }
0284     else
0285     {
0286         painter.setPen(palette().buttonText().color());
0287     }
0288 
0289     QRegion rightRect = rect;
0290     rightRect = rightRect.subtracted(leftRect);
0291 
0292     QTextOption textOption(Qt::AlignAbsolute | Qt::AlignHCenter | Qt::AlignVCenter);
0293     textOption.setWrapMode(QTextOption::NoWrap);
0294 
0295     if (!(d->edit && d->edit->isVisible()))
0296     {
0297         painter.setClipRegion(rightRect);
0298         painter.setClipping(true);
0299         painter.drawText(rect.adjusted(-2, 0, 2, 0), progressOpts.text, textOption);
0300         painter.setClipping(false);
0301     }
0302 
0303     if (!leftRect.isNull())
0304     {
0305         painter.setClipRect(leftRect.adjusted(0, -1, 1, 1));
0306         painter.setPen(palette().highlight().color());
0307         painter.setBrush(palette().highlight());
0308 
0309         spinOpts.palette.setBrush(QPalette::Base, palette().highlight());
0310         style()->drawComplexControl(QStyle::CC_SpinBox, &spinOpts, &painter, d->dummySpinBox);
0311 
0312         if (!(d->edit && d->edit->isVisible()))
0313         {
0314             painter.setPen(palette().highlightedText().color());
0315             painter.setClipping(true);
0316             painter.drawText(rect.adjusted(-2, 0, 2, 0), progressOpts.text, textOption);
0317         }
0318 
0319         painter.setClipping(false);
0320     }
0321 
0322     painter.restore();
0323 }
0324 
0325 void DAbstractSliderSpinBox::paintPlastique(QPainter& painter)
0326 {
0327     Q_D(DAbstractSliderSpinBox);
0328 
0329     QStyleOptionSpinBox spinOpts         = spinBoxOptions();
0330     QStyleOptionProgressBar progressOpts = progressBarOptions();
0331 
0332     style()->drawComplexControl(QStyle::CC_SpinBox, &spinOpts, &painter, d->dummySpinBox);
0333 
0334     painter.save();
0335 
0336     QRect rect = progressOpts.rect.adjusted(2, 0, -2, 0);
0337     QRect leftRect;
0338 
0339     int progressIndicatorPos = (progressOpts.progress - double(progressOpts.minimum)) / qMax(double(1.0),
0340                                 double(progressOpts.maximum) - progressOpts.minimum) * rect.width();
0341 
0342     if      ((progressIndicatorPos >= 0) && (progressIndicatorPos <= rect.width()))
0343     {
0344         leftRect = QRect(rect.left(), rect.top(), progressIndicatorPos, rect.height());
0345     }
0346     else if (progressIndicatorPos > rect.width())
0347     {
0348         painter.setPen(palette().highlightedText().color());
0349     }
0350     else
0351     {
0352         painter.setPen(palette().buttonText().color());
0353     }
0354 
0355     QRegion rightRect = rect;
0356     rightRect = rightRect.subtracted(leftRect);
0357 
0358     QTextOption textOption(Qt::AlignAbsolute | Qt::AlignHCenter | Qt::AlignVCenter);
0359     textOption.setWrapMode(QTextOption::NoWrap);
0360 
0361     if (!(d->edit && d->edit->isVisible()))
0362     {
0363         painter.setClipRegion(rightRect);
0364         painter.setClipping(true);
0365         painter.drawText(rect.adjusted(-2, 0, 2, 0), progressOpts.text, textOption);
0366         painter.setClipping(false);
0367     }
0368 
0369     if (!leftRect.isNull())
0370     {
0371         painter.setPen(palette().highlight().color());
0372         painter.setBrush(palette().highlight());
0373         painter.drawRect(leftRect.adjusted(0, 0, 0, -1));
0374 
0375         if (!(d->edit && d->edit->isVisible()))
0376         {
0377             painter.setPen(palette().highlightedText().color());
0378             painter.setClipRect(leftRect.adjusted(0, 0, 1, 0));
0379             painter.setClipping(true);
0380             painter.drawText(rect.adjusted(-2, 0, 2, 0), progressOpts.text, textOption);
0381             painter.setClipping(false);
0382         }
0383     }
0384 
0385     painter.restore();
0386 }
0387 
0388 void DAbstractSliderSpinBox::paintBreeze(QPainter& painter)
0389 {
0390     Q_D(DAbstractSliderSpinBox);
0391 
0392     QStyleOptionSpinBox spinOpts         = spinBoxOptions();
0393     QStyleOptionProgressBar progressOpts = progressBarOptions();
0394     QString valueText                    = progressOpts.text;
0395     progressOpts.text                    = QLatin1String("");
0396     progressOpts.rect.adjust(0, 1, 0, -1);
0397 
0398     style()->drawComplexControl(QStyle::CC_SpinBox, &spinOpts, &painter, this);
0399     style()->drawControl(QStyle::CE_ProgressBarGroove, &progressOpts, &painter, this);
0400 
0401     painter.save();
0402 
0403     QRect leftRect;
0404 
0405     int progressIndicatorPos = (progressOpts.progress - double(progressOpts.minimum)) / qMax(double(1.0),
0406                                 double(progressOpts.maximum) - progressOpts.minimum) * progressOpts.rect.width();
0407 
0408     if      ((progressIndicatorPos >= 0) && (progressIndicatorPos <= progressOpts.rect.width()))
0409     {
0410         leftRect = QRect(progressOpts.rect.left(), progressOpts.rect.top(), progressIndicatorPos, progressOpts.rect.height());
0411     }
0412     else if (progressIndicatorPos > progressOpts.rect.width())
0413     {
0414         painter.setPen(palette().highlightedText().color());
0415     }
0416     else
0417     {
0418         painter.setPen(palette().buttonText().color());
0419     }
0420 
0421     QRegion rightRect = progressOpts.rect;
0422     rightRect         = rightRect.subtracted(leftRect);
0423     painter.setClipRegion(rightRect);
0424 
0425     QTextOption textOption(Qt::AlignAbsolute | Qt::AlignHCenter | Qt::AlignVCenter);
0426     textOption.setWrapMode(QTextOption::NoWrap);
0427 
0428     if (!(d->edit && d->edit->isVisible()))
0429     {
0430         painter.drawText(progressOpts.rect, valueText, textOption);
0431     }
0432 
0433     if (!leftRect.isNull())
0434     {
0435         painter.setPen(palette().highlightedText().color());
0436         painter.setClipRect(leftRect);
0437         style()->drawControl(QStyle::CE_ProgressBarContents, &progressOpts, &painter, this);
0438 
0439         if (!(d->edit && d->edit->isVisible()))
0440         {
0441             painter.drawText(progressOpts.rect, valueText, textOption);
0442         }
0443     }
0444 
0445     painter.restore();
0446 }
0447 
0448 void DAbstractSliderSpinBox::mousePressEvent(QMouseEvent* e)
0449 {
0450     Q_D(DAbstractSliderSpinBox);
0451 
0452     QStyleOptionSpinBox spinOpts = spinBoxOptions();
0453 
0454     // Depress buttons or highlight slider
0455     // Also used to emulate mouse grab...
0456 
0457     if      (e->buttons() & Qt::LeftButton)
0458     {
0459         if      (upButtonRect(spinOpts).contains(e->pos()))
0460         {
0461             d->upButtonDown = true;
0462         }
0463         else if (downButtonRect(spinOpts).contains(e->pos()))
0464         {
0465             d->downButtonDown = true;
0466         }
0467     }
0468     else if (e->buttons() & Qt::RightButton)
0469     {
0470         showEdit();
0471     }
0472 
0473     update();
0474 }
0475 
0476 void DAbstractSliderSpinBox::mouseReleaseEvent(QMouseEvent* e)
0477 {
0478     Q_D(DAbstractSliderSpinBox);
0479 
0480     QStyleOptionSpinBox spinOpts = spinBoxOptions();
0481 
0482     d->isDragging = false;
0483 
0484     // Step up/down for buttons
0485     // Emulating mouse grab too
0486 
0487     if      (upButtonRect(spinOpts).contains(e->pos()) && d->upButtonDown)
0488     {
0489         setInternalValue(d->value + d->singleStep);
0490     }
0491     else if (downButtonRect(spinOpts).contains(e->pos()) && d->downButtonDown)
0492     {
0493         setInternalValue(d->value - d->singleStep);
0494     }
0495     else if (progressRect(spinOpts).contains(e->pos()) &&
0496              !(d->edit->isVisible())                      &&
0497              !(d->upButtonDown || d->downButtonDown))
0498     {
0499         // Snap to percentage for progress area
0500         setInternalValue(valueForX(e->pos().x(),e->modifiers()));
0501     }
0502     else
0503     {
0504         // Confirm the last known value, since we might be ignoring move events
0505         setInternalValue(d->value);
0506     }
0507 
0508     d->upButtonDown   = false;
0509     d->downButtonDown = false;
0510     update();
0511 }
0512 
0513 void DAbstractSliderSpinBox::mouseMoveEvent(QMouseEvent* e)
0514 {
0515     Q_D(DAbstractSliderSpinBox);
0516 
0517     if (e->modifiers() & Qt::ShiftModifier)
0518     {
0519         if (!d->shiftMode)
0520         {
0521             d->shiftPercent = pow(double(d->value - d->minimum) / double(d->maximum - d->minimum), 1 / double(d->exponentRatio));
0522             d->shiftMode = true;
0523         }
0524     }
0525     else
0526     {
0527         d->shiftMode = false;
0528     }
0529 
0530     // Respect emulated mouse grab.
0531 
0532     if (e->buttons() & Qt::LeftButton && !(d->downButtonDown || d->upButtonDown))
0533     {
0534         d->isDragging = true;
0535         setInternalValue(valueForX(e->pos().x(),e->modifiers()), d->blockUpdateSignalOnDrag);
0536         update();
0537     }
0538 }
0539 
0540 void DAbstractSliderSpinBox::keyPressEvent(QKeyEvent* e)
0541 {
0542     Q_D(DAbstractSliderSpinBox);
0543 
0544     switch (e->key())
0545     {
0546         case Qt::Key_Up:
0547         case Qt::Key_Right:
0548             setInternalValue(d->value + d->singleStep);
0549 
0550             if (d->edit->isVisible())
0551             {
0552                 d->edit->setText(valueString());
0553             }
0554 
0555             update();
0556             break;
0557 
0558         case Qt::Key_Down:
0559         case Qt::Key_Left:
0560             setInternalValue(d->value - d->singleStep);
0561 
0562             if (d->edit->isVisible())
0563             {
0564                 d->edit->setText(valueString());
0565             }
0566 
0567             update();
0568             break;
0569 
0570         case Qt::Key_Shift:
0571             d->shiftPercent = pow(double(d->value - d->minimum) / double(d->maximum - d->minimum), 1 / double(d->exponentRatio));
0572             d->shiftMode = true;
0573             break;
0574 
0575         case Qt::Key_Enter: // Line edit isn't "accepting" key strokes...
0576         case Qt::Key_Return:
0577         case Qt::Key_Escape:
0578         case Qt::Key_Control:
0579         case Qt::Key_Alt:
0580         case Qt::Key_AltGr:
0581         case Qt::Key_Super_L:
0582         case Qt::Key_Super_R:
0583             break;
0584 
0585         default:
0586             showEdit();
0587             d->edit->event(e);
0588             break;
0589     }
0590 }
0591 
0592 void DAbstractSliderSpinBox::wheelEvent(QWheelEvent *e)
0593 {
0594     Q_D(DAbstractSliderSpinBox);
0595 
0596     if      (e->angleDelta().y() > 0)
0597     {
0598         setInternalValue(d->value + d->singleStep);
0599     }
0600     else if (e->angleDelta().y() < 0)
0601     {
0602         setInternalValue(d->value - d->singleStep);
0603     }
0604 
0605     update();
0606     e->accept();
0607 }
0608 
0609 void DAbstractSliderSpinBox::focusInEvent(QFocusEvent* e)
0610 {
0611     if (e->reason() == Qt::TabFocusReason)
0612     {
0613         showEdit();
0614     }
0615 
0616     e->accept();
0617 }
0618 
0619 bool DAbstractSliderSpinBox::eventFilter(QObject* recv, QEvent* e)
0620 {
0621     Q_D(DAbstractSliderSpinBox);
0622 
0623     if (recv == static_cast<QObject*>(d->edit) && e->type() == QEvent::KeyRelease)
0624     {
0625         QKeyEvent* keyEvent = static_cast<QKeyEvent*>(e);
0626 
0627         switch (keyEvent->key())
0628         {
0629             case Qt::Key_Enter:
0630             case Qt::Key_Return:
0631                 setInternalValue(QLocale::system().toDouble(d->edit->text()) * d->factor);
0632                 hideEdit();
0633                 return true;
0634 
0635             case Qt::Key_Escape:
0636                 d->edit->setText(valueString());
0637                 hideEdit();
0638                 return true;
0639 
0640             default:
0641                 break;
0642         }
0643     }
0644     else if (d->edit->isVisible() && e->type() == QEvent::ShortcutOverride)
0645     {
0646         QKeyEvent* const keyEvent = static_cast<QKeyEvent*>(e);
0647 
0648         switch (keyEvent->key())
0649         {
0650             case Qt::Key_Tab:
0651             case Qt::Key_Enter:
0652             case Qt::Key_Return:
0653             case Qt::Key_Escape:
0654                 e->accept();
0655                 return true;
0656 
0657             default:
0658                 break;
0659         }
0660     }
0661 
0662     return false;
0663 }
0664 
0665 QSize DAbstractSliderSpinBox::sizeHint() const
0666 {
0667     const Q_D(DAbstractSliderSpinBox);
0668 
0669     QStyleOptionSpinBox spinOpts = spinBoxOptions();
0670     QFont ft(font());
0671 
0672     if (d->style == DAbstractSliderSpinBoxPrivate::STYLE_NOQUIRK)
0673     {
0674         // Some styles use bold font in progressbars
0675         // unfortunately there is no reliable way to check for that
0676         ft.setBold(true);
0677     }
0678 
0679     QFontMetrics fm(ft);
0680     QSize hint(fm.boundingRect(d->prefix + QString::number(d->maximum) + d->suffix).size());
0681     hint += QSize(0, 2);
0682 
0683     switch (d->style)
0684     {
0685         case DAbstractSliderSpinBoxPrivate::STYLE_PLASTIQUE:
0686             hint += QSize(8, 0);
0687             break;
0688 
0689         case DAbstractSliderSpinBoxPrivate::STYLE_BREEZE:
0690             hint += QSize(2, 0);
0691             break;
0692 
0693         case DAbstractSliderSpinBoxPrivate::STYLE_MACINTOSH:
0694         case DAbstractSliderSpinBoxPrivate::STYLE_WINDOWS:
0695         case DAbstractSliderSpinBoxPrivate::STYLE_FUSION:
0696         case DAbstractSliderSpinBoxPrivate::STYLE_OXYGEN:
0697         case DAbstractSliderSpinBoxPrivate::STYLE_GTK2:
0698             hint += QSize(8, 8);
0699             break;
0700 
0701         case DAbstractSliderSpinBoxPrivate::STYLE_NOQUIRK:
0702             // almost all "modern" styles have a margin around controls
0703             hint += QSize(6, 6);
0704             break;
0705 
0706         default:
0707             break;
0708     }
0709 
0710     // Getting the size of the buttons is a pain as the calcs require a rect
0711     // that is "big enough". We run the calc twice to get the "smallest" buttons
0712     // This code was inspired by QAbstractSpinBox
0713     QSize extra(1000, 0);
0714     spinOpts.rect.setSize(hint + extra);
0715     extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &spinOpts,
0716                                             QStyle::SC_SpinBoxEditField, this).size();
0717     spinOpts.rect.setSize(hint + extra);
0718     extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &spinOpts,
0719                                             QStyle::SC_SpinBoxEditField, this).size();
0720     hint += extra;
0721 
0722     spinOpts.rect.setSize(hint);
0723 
0724     return style()->sizeFromContents(QStyle::CT_SpinBox, &spinOpts, hint);
0725 }
0726 
0727 QSize DAbstractSliderSpinBox::minimumSizeHint() const
0728 {
0729     return sizeHint();
0730 }
0731 
0732 QSize DAbstractSliderSpinBox::minimumSize() const
0733 {
0734     return QWidget::minimumSize().expandedTo(minimumSizeHint());
0735 }
0736 
0737 QStyleOptionSpinBox DAbstractSliderSpinBox::spinBoxOptions() const
0738 {
0739     const Q_D(DAbstractSliderSpinBox);
0740 
0741     QStyleOptionSpinBox opts;
0742     opts.initFrom(this);
0743     opts.frame         = false;
0744     opts.buttonSymbols = QAbstractSpinBox::UpDownArrows;
0745     opts.subControls   = QStyle::SC_SpinBoxUp | QStyle::SC_SpinBoxDown;
0746 
0747     // Disable non-logical buttons
0748 
0749     if      (d->value == d->minimum)
0750     {
0751         opts.stepEnabled = QAbstractSpinBox::StepUpEnabled;
0752     }
0753     else if (d->value == d->maximum)
0754     {
0755         opts.stepEnabled = QAbstractSpinBox::StepDownEnabled;
0756     }
0757     else
0758     {
0759         opts.stepEnabled = QAbstractSpinBox::StepUpEnabled | QAbstractSpinBox::StepDownEnabled;
0760     }
0761 
0762     // Deal with depressed buttons
0763 
0764     if      (d->upButtonDown)
0765     {
0766         opts.activeSubControls = QStyle::SC_SpinBoxUp;
0767     }
0768     else if (d->downButtonDown)
0769     {
0770         opts.activeSubControls = QStyle::SC_SpinBoxDown;
0771     }
0772     else
0773     {
0774         opts.activeSubControls = QStyle::SC_None;
0775     }
0776 
0777     return opts;
0778 }
0779 
0780 QStyleOptionProgressBar DAbstractSliderSpinBox::progressBarOptions() const
0781 {
0782     const Q_D(DAbstractSliderSpinBox);
0783 
0784     QStyleOptionSpinBox spinOpts = spinBoxOptions();
0785 
0786     // Create opts for drawing the progress portion
0787 
0788     QStyleOptionProgressBar progressOpts;
0789     progressOpts.initFrom(this);
0790     progressOpts.maximum         = d->maximum;
0791     progressOpts.minimum         = d->minimum;
0792 
0793     double minDbl                = d->minimum;
0794     double dValues               = (d->maximum - minDbl);
0795 
0796     progressOpts.progress        = dValues * pow((d->value - minDbl) / dValues, 1.0 / d->exponentRatio) + minDbl;
0797     progressOpts.text            = d->prefix + valueString() + d->suffix;
0798     progressOpts.textAlignment   = Qt::AlignCenter;
0799     progressOpts.textVisible     = !(d->edit->isVisible());
0800 
0801     // Change opts rect to be only the ComboBox's text area
0802 
0803     progressOpts.rect            = progressRect(spinOpts);
0804 
0805     return progressOpts;
0806 }
0807 
0808 QRect DAbstractSliderSpinBox::progressRect(const QStyleOptionSpinBox& spinBoxOptions) const
0809 {
0810     const Q_D(DAbstractSliderSpinBox);
0811 
0812     QRect ret = style()->subControlRect(QStyle::CC_SpinBox, &spinBoxOptions,
0813                                         QStyle::SC_SpinBoxEditField);
0814 
0815     switch (d->style)
0816     {
0817         case DAbstractSliderSpinBoxPrivate::STYLE_PLASTIQUE:
0818             ret.adjust(-2, 0, 1, 0);
0819             break;
0820 
0821         case DAbstractSliderSpinBoxPrivate::STYLE_BREEZE:
0822             ret.adjust(1, 0, 0, 0);
0823             break;
0824 
0825         default:
0826             break;
0827     }
0828 
0829     return ret;
0830 }
0831 
0832 QRect DAbstractSliderSpinBox::upButtonRect(const QStyleOptionSpinBox& spinBoxOptions) const
0833 {
0834     return style()->subControlRect(QStyle::CC_SpinBox, &spinBoxOptions,
0835                                    QStyle::SC_SpinBoxUp);
0836 }
0837 
0838 QRect DAbstractSliderSpinBox::downButtonRect(const QStyleOptionSpinBox& spinBoxOptions) const
0839 {
0840     return style()->subControlRect(QStyle::CC_SpinBox, &spinBoxOptions,
0841                                    QStyle::SC_SpinBoxDown);
0842 }
0843 
0844 int DAbstractSliderSpinBox::valueForX(int x, Qt::KeyboardModifiers modifiers) const
0845 {
0846     const Q_D(DAbstractSliderSpinBox);
0847 
0848     QStyleOptionSpinBox spinOpts = spinBoxOptions();
0849 
0850     QRect correctedProgRect;
0851 
0852     if      (d->style == DAbstractSliderSpinBoxPrivate::STYLE_FUSION)
0853     {
0854         correctedProgRect = progressRect(spinOpts).adjusted(2, 0, -2, 0);
0855     }
0856     else if (d->style == DAbstractSliderSpinBoxPrivate::STYLE_BREEZE)
0857     {
0858         correctedProgRect = progressRect(spinOpts);
0859     }
0860     else
0861     {
0862         // Adjust for magic number in style code (margins)
0863         correctedProgRect = progressRect(spinOpts).adjusted(2, 2, -2, -2);
0864     }
0865 
0866     // Compute the distance of the progress bar, in pixel
0867     double leftDbl  = correctedProgRect.left();
0868     double xDbl     = x - leftDbl;
0869 
0870     // Compute the ration of the progress bar used, linearly (ignoring the exponent)
0871     double rightDbl = correctedProgRect.right();
0872     double minDbl   = d->minimum;
0873     double maxDbl   = d->maximum;
0874 
0875     double dValues  = (maxDbl - minDbl);
0876     double percent  = (xDbl / (rightDbl - leftDbl));
0877 
0878     // If SHIFT is pressed, movement should be slowed.
0879     if (modifiers & Qt::ShiftModifier)
0880     {
0881         percent = d->shiftPercent + (percent - d->shiftPercent) * d->slowFactor;
0882     }
0883 
0884     // Final value
0885     double realvalue = ((dValues * pow(percent, d->exponentRatio)) + minDbl);
0886     // If key CTRL is pressed, round to the closest step.
0887     if (modifiers & Qt::ControlModifier)
0888     {
0889         double fstep = d->fastSliderStep;
0890 
0891         if (modifiers & Qt::ShiftModifier)
0892         {
0893             fstep *= d->slowFactor;
0894         }
0895 
0896         realvalue = floor((realvalue+fstep / 2) / fstep) * fstep;
0897     }
0898 
0899     return int(realvalue);
0900 }
0901 
0902 void DAbstractSliderSpinBox::setPrefix(const QString& prefix)
0903 {
0904     Q_D(DAbstractSliderSpinBox);
0905     d->prefix = prefix;
0906 }
0907 
0908 void DAbstractSliderSpinBox::setSuffix(const QString& suffix)
0909 {
0910     Q_D(DAbstractSliderSpinBox);
0911     d->suffix = suffix;
0912 }
0913 
0914 void DAbstractSliderSpinBox::setExponentRatio(double dbl)
0915 {
0916     Q_D(DAbstractSliderSpinBox);
0917     Q_ASSERT(dbl > 0);
0918     d->exponentRatio = dbl;
0919 }
0920 
0921 void DAbstractSliderSpinBox::setBlockUpdateSignalOnDrag(bool blockUpdateSignal)
0922 {
0923     Q_D(DAbstractSliderSpinBox);
0924     d->blockUpdateSignalOnDrag = blockUpdateSignal;
0925 }
0926 
0927 void DAbstractSliderSpinBox::contextMenuEvent(QContextMenuEvent* event)
0928 {
0929     event->accept();
0930 }
0931 
0932 void DAbstractSliderSpinBox::editLostFocus()
0933 {
0934     Q_D(DAbstractSliderSpinBox);
0935 
0936     if (!d->edit->hasFocus())
0937     {
0938         if (d->edit->isModified())
0939         {
0940             setInternalValue(QLocale::system().toDouble(d->edit->text()) * d->factor);
0941         }
0942 
0943         hideEdit();
0944     }
0945 }
0946 
0947 void DAbstractSliderSpinBox::setInternalValue(int value)
0948 {
0949     setInternalValue(value, false);
0950 }
0951 
0952 bool DAbstractSliderSpinBox::isDragging() const
0953 {
0954     Q_D(const DAbstractSliderSpinBox);
0955     return d->isDragging;
0956 }
0957 
0958 void DAbstractSliderSpinBox::changeEvent(QEvent* e)
0959 {
0960     Q_D(DAbstractSliderSpinBox);
0961 
0962     QWidget::changeEvent(e);
0963 
0964     if (e->type() == QEvent::StyleChange)
0965     {
0966         if      (style()->objectName() == QLatin1String("macintosh"))
0967         {
0968             d->style = DAbstractSliderSpinBoxPrivate::STYLE_MACINTOSH;
0969         }
0970         else if (style()->objectName() == QLatin1String("plastique"))
0971         {
0972             d->style = DAbstractSliderSpinBoxPrivate::STYLE_PLASTIQUE;
0973         }
0974         else if (style()->objectName() == QLatin1String("windows"))
0975         {
0976             d->style = DAbstractSliderSpinBoxPrivate::STYLE_WINDOWS;
0977         }
0978         else if (style()->objectName() == QLatin1String("breeze"))
0979         {
0980             d->style = DAbstractSliderSpinBoxPrivate::STYLE_BREEZE;
0981         }
0982         else if (style()->objectName() == QLatin1String("fusion"))
0983         {
0984             d->style = DAbstractSliderSpinBoxPrivate::STYLE_FUSION;
0985         }
0986         else if (style()->objectName() == QLatin1String("oxygen"))
0987         {
0988             d->style = DAbstractSliderSpinBoxPrivate::STYLE_OXYGEN;
0989         }
0990         else if (style()->objectName() == QLatin1String("gtk2"))
0991         {
0992             d->style = DAbstractSliderSpinBoxPrivate::STYLE_GTK2;
0993         }
0994         else
0995         {
0996             d->style = DAbstractSliderSpinBoxPrivate::STYLE_NOQUIRK;
0997         }
0998     }
0999 }
1000 
1001 // ---------------------------------------------------------------------------------------------
1002 
1003 class Q_DECL_HIDDEN DSliderSpinBoxPrivate : public DAbstractSliderSpinBoxPrivate
1004 {
1005 };
1006 
1007 DSliderSpinBox::DSliderSpinBox(QWidget* const parent)
1008     : DAbstractSliderSpinBox(parent, new DSliderSpinBoxPrivate)
1009 {
1010     setRange(0, 99);
1011 }
1012 
1013 DSliderSpinBox::~DSliderSpinBox()
1014 {
1015 }
1016 
1017 void DSliderSpinBox::setRange(int minimum, int maximum)
1018 {
1019     Q_D(DSliderSpinBox);
1020 
1021     d->minimum        = minimum;
1022     d->maximum        = maximum;
1023     d->fastSliderStep = (maximum-minimum + 1) / 20;
1024     d->validator->setRange(minimum, maximum, 0);
1025     update();
1026 }
1027 
1028 int DSliderSpinBox::minimum() const
1029 {
1030     const Q_D(DSliderSpinBox);
1031 
1032     return d->minimum;
1033 }
1034 
1035 void DSliderSpinBox::setMinimum(int minimum)
1036 {
1037     Q_D(DSliderSpinBox);
1038     setRange(minimum, d->maximum);
1039 }
1040 
1041 int DSliderSpinBox::maximum() const
1042 {
1043     const Q_D(DSliderSpinBox);
1044 
1045     return d->maximum;
1046 }
1047 
1048 void DSliderSpinBox::setMaximum(int maximum)
1049 {
1050     Q_D(DSliderSpinBox);
1051     setRange(d->minimum, maximum);
1052 }
1053 
1054 int DSliderSpinBox::fastSliderStep() const
1055 {
1056     const Q_D(DSliderSpinBox);
1057 
1058     return d->fastSliderStep;
1059 }
1060 
1061 void DSliderSpinBox::setFastSliderStep(int step)
1062 {
1063     Q_D(DSliderSpinBox);
1064     d->fastSliderStep = step;
1065 }
1066 
1067 int DSliderSpinBox::value()
1068 {
1069     Q_D(DSliderSpinBox);
1070 
1071     return d->value;
1072 }
1073 
1074 void DSliderSpinBox::setValue(int value)
1075 {
1076     setInternalValue(value, false);
1077     update();
1078 }
1079 
1080 QString DSliderSpinBox::valueString() const
1081 {
1082     const Q_D(DSliderSpinBox);
1083 
1084     QLocale locale;
1085 
1086     return locale.toString((double)d->value, 'f', d->validator->decimals());
1087 }
1088 
1089 void DSliderSpinBox::setSingleStep(int value)
1090 {
1091     Q_D(DSliderSpinBox);
1092     d->singleStep = value;
1093 }
1094 
1095 void DSliderSpinBox::setPageStep(int value)
1096 {
1097     Q_UNUSED(value);
1098 }
1099 
1100 void DSliderSpinBox::setInternalValue(int _value, bool blockUpdateSignal)
1101 {
1102     Q_D(DAbstractSliderSpinBox);
1103 
1104     d->value = qBound(d->minimum, _value, d->maximum);
1105 
1106     if (!blockUpdateSignal)
1107     {
1108         Q_EMIT valueChanged(value());
1109     }
1110 }
1111 
1112 // ---------------------------------------------------------------------------------------------
1113 
1114 class Q_DECL_HIDDEN DDoubleSliderSpinBoxPrivate : public DAbstractSliderSpinBoxPrivate
1115 {
1116 };
1117 
1118 DDoubleSliderSpinBox::DDoubleSliderSpinBox(QWidget* const parent)
1119     : DAbstractSliderSpinBox(parent, new DDoubleSliderSpinBoxPrivate)
1120 {
1121 }
1122 
1123 DDoubleSliderSpinBox::~DDoubleSliderSpinBox()
1124 {
1125 }
1126 
1127 void DDoubleSliderSpinBox::setRange(double minimum, double maximum, int decimals)
1128 {
1129     Q_D(DDoubleSliderSpinBox);
1130 
1131     d->factor  = pow(10.0, decimals);
1132 
1133     d->minimum = minimum * d->factor;
1134     d->maximum = maximum * d->factor;
1135 
1136     // This code auto-compute a new step when pressing control.
1137     // A flag defaulting to "do not change the fast step" should be added, but it implies changing every call
1138 
1139     if      (((maximum - minimum) >= 2.0) || (decimals <= 0))
1140     {
1141         // Quick step on integers
1142         d->fastSliderStep = int(pow(10.0, decimals));
1143     }
1144     else if (decimals == 1)
1145     {
1146         d->fastSliderStep = (maximum - minimum) * d->factor / 10;
1147     }
1148     else
1149     {
1150         d->fastSliderStep = (maximum - minimum) * d->factor / 20;
1151     }
1152 
1153     d->validator->setRange(minimum, maximum, decimals);
1154     update();
1155     setValue(value());
1156 }
1157 
1158 double DDoubleSliderSpinBox::minimum() const
1159 {
1160     const Q_D(DAbstractSliderSpinBox);
1161 
1162     return d->minimum / d->factor;
1163 }
1164 
1165 void DDoubleSliderSpinBox::setMinimum(double minimum)
1166 {
1167     Q_D(DAbstractSliderSpinBox);
1168     setRange(minimum, d->maximum);
1169 }
1170 
1171 double DDoubleSliderSpinBox::maximum() const
1172 {
1173     const Q_D(DAbstractSliderSpinBox);
1174 
1175     return d->maximum / d->factor;
1176 }
1177 
1178 void DDoubleSliderSpinBox::setMaximum(double maximum)
1179 {
1180     Q_D(DAbstractSliderSpinBox);
1181     setRange(d->minimum, maximum);
1182 }
1183 
1184 double DDoubleSliderSpinBox::fastSliderStep() const
1185 {
1186     const Q_D(DAbstractSliderSpinBox);
1187 
1188     return d->fastSliderStep;
1189 }
1190 
1191 void DDoubleSliderSpinBox::setFastSliderStep(double step)
1192 {
1193     Q_D(DAbstractSliderSpinBox);
1194     d->fastSliderStep = step;
1195 }
1196 
1197 double DDoubleSliderSpinBox::value()
1198 {
1199     Q_D(DAbstractSliderSpinBox);
1200 
1201     return (double)d->value / d->factor;
1202 }
1203 
1204 void DDoubleSliderSpinBox::setValue(double value)
1205 {
1206     Q_D(DAbstractSliderSpinBox);
1207 
1208     setInternalValue(d->value = qRound(value * d->factor), false);
1209     update();
1210 }
1211 
1212 void DDoubleSliderSpinBox::setSingleStep(double value)
1213 {
1214     Q_D(DAbstractSliderSpinBox);
1215     d->singleStep = value * d->factor;
1216 }
1217 
1218 QString DDoubleSliderSpinBox::valueString() const
1219 {
1220     const Q_D(DAbstractSliderSpinBox);
1221 
1222     QLocale locale;
1223 
1224     return locale.toString((double)d->value / d->factor, 'f', d->validator->decimals());
1225 }
1226 
1227 void DDoubleSliderSpinBox::setInternalValue(int _value, bool blockUpdateSignal)
1228 {
1229     Q_D(DAbstractSliderSpinBox);
1230 
1231     d->value = qBound(d->minimum, _value, d->maximum);
1232 
1233     if (!blockUpdateSignal)
1234     {
1235         Q_EMIT valueChanged(value());
1236     }
1237 }
1238 
1239 } // namespace Digikam
1240 
1241 #include "moc_dsliderspinbox.cpp"