File indexing completed on 2024-05-12 16:40:57

0001 /* This file is part of the KDE project
0002    Copyright (C) 2012 Oleg Kukharchuk <oleg.kuh@gmail.com>
0003 
0004    This program is free software; you can redistribute it and/or
0005    modify it under the terms of the GNU Library General Public
0006    License as published by the Free Software Foundation; either
0007    version 2 of the License, or (at your option) any later version.
0008 
0009    This program is distributed in the hope that it will be useful,
0010    but WITHOUT ANY WARRANTY; without even the implied warranty of
0011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012    Library General Public License for more details.
0013 
0014    You should have received a copy of the GNU Library General Public License
0015    along with this program; see the file COPYING.  If not, write to
0016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0017  * Boston, MA 02110-1301, USA.
0018 */
0019 
0020 #include "kexislider.h"
0021 
0022 #include <QBoxLayout>
0023 #include <QSpinBox>
0024 #include <QPainter>
0025 #include <QStyle>
0026 #include <QSlider>
0027 #include <QStyleOptionSlider>
0028 
0029 
0030 class Q_DECL_HIDDEN KexiSlider::Private
0031 {
0032 public:
0033     Private() {}
0034 
0035     Slider *slider;
0036     QSpinBox *spinBox;
0037     QBoxLayout *layout;
0038 };
0039 
0040 
0041 class Slider : public QSlider
0042 {
0043     Q_OBJECT
0044 public:
0045     explicit Slider(QWidget *parent) :
0046         QSlider(parent)
0047     {
0048 
0049     }
0050 
0051     QSize sizeHint() const override
0052     {
0053         if (tickPosition() == QSlider::NoTicks)
0054             return QSlider::sizeHint();
0055 
0056         // preserve space for labels
0057         QSize extra(0,0);
0058 
0059         QFontMetrics fm(font());
0060 
0061         int h = fm.height() + 3;
0062         int w = fm.width(QString::number(maximum())) + 3;
0063 
0064         if (orientation() == Qt::Horizontal) {
0065             extra.setHeight(tickPosition() == QSlider::TicksBothSides ? h * 2 : h);
0066         } else {
0067             extra.setWidth(tickPosition() == QSlider::TicksBothSides ? w * 2 : w);
0068         }
0069 
0070         return QSlider::sizeHint() + extra;
0071     }
0072 
0073 protected:
0074     virtual void paintEvent(QPaintEvent *ev) override
0075     {
0076         if (tickPosition() == QSlider::NoTicks)
0077             return QSlider::paintEvent(ev);
0078 
0079         QPainter p(this);
0080         QStyleOptionSlider option;
0081         initStyleOption(&option);
0082 
0083         const QSlider::TickPosition ticks( option.tickPosition );
0084         const int available(style()->proxy()->pixelMetric(QStyle::PM_SliderSpaceAvailable, &option, this));
0085         int interval = option.tickInterval;
0086         if( interval < 1 ) interval = option.pageStep;
0087         if( interval < 1 ) return;
0088 
0089         const QRect r(option.rect);
0090         const QPalette palette(option.palette);
0091         const int fudge(style()->proxy()->pixelMetric(QStyle::PM_SliderLength, &option, this) / 2);
0092         int current(option.minimum);
0093         int nextLabel = current;
0094 
0095         const QFontMetrics fm(fontMetrics());
0096         int h = fm.height() + 3;
0097         int w = fm.width(QString::number(option.maximum)) + 3;
0098 
0099         if(available<w)
0100             nextLabel = -1;
0101 
0102         qreal i = qreal(available) / qreal(orientation() == Qt::Horizontal ? w : h);
0103         qreal t = qreal(option.maximum)/qreal(interval);
0104         int valStep = t/ i + 1;
0105 
0106         // Since there is no subrect for tickmarks do a translation here.
0107         p.save();
0108         p.translate(r.x(), r.y());
0109 
0110         p.setPen(palette.color(QPalette::WindowText));
0111         int extra = (option.tickPosition == QSlider::TicksBothSides ? 2 : 1);
0112         int tickSize(option.orientation == Qt::Horizontal ? (r.height() - h*extra)/3:(r.width() - w*extra)/3);
0113 
0114         while(current <= option.maximum)
0115         {
0116 
0117             const int position(QStyle::sliderPositionFromValue(option.minimum, option.maximum,
0118                                                                  current, available, option.upsideDown) + fudge);
0119 
0120             // calculate positions
0121             if(option.orientation == Qt::Horizontal)
0122             {
0123 
0124                 if (ticks & QSlider::TicksAbove) {
0125                     p.drawLine(position, h, position, tickSize + h);
0126                     if(current == nextLabel)
0127                         p.drawText(QRect(position - w/2, 0, w, h), Qt::AlignHCenter, QString::number(current));
0128                 }
0129                 if (ticks & QSlider::TicksBelow) {
0130                     p.drawLine( position, r.height() - h - tickSize, position, r.height() - h );
0131                     if(current == nextLabel)
0132                         p.drawText(QRect(position - w/2, r.height() - h + 3, w, h), Qt::AlignHCenter, QString::number(current));
0133                 }
0134             } else {
0135                 if (ticks & QSlider::TicksAbove) {
0136                     p.drawLine(w, position, tickSize + w, position);
0137                     if(current == nextLabel)
0138                         p.drawText(QRect(0, position - h/2, w - 3, h), Qt::AlignRight | Qt::AlignVCenter, QString::number(current));
0139                 }
0140                 if (ticks & QSlider::TicksBelow) {
0141                     p.drawLine(r.width() - w - tickSize, position, r.width() - w, position );
0142                     if(current == nextLabel)
0143                         p.drawText(QRect(r.width() - w + 3, position - h/2, w, h), Qt::AlignVCenter, QString::number(current));
0144                 }
0145             }
0146 
0147             // go to next position
0148             if (current == nextLabel)
0149                 nextLabel += interval*valStep;
0150             current += interval;
0151 
0152         }
0153         p.restore();
0154         option.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle;
0155 
0156         style()->proxy()->drawComplexControl(QStyle::CC_Slider, &option, &p, this);
0157     }
0158 };
0159 
0160 KexiSlider::KexiSlider(QWidget *parent)
0161     : QWidget(parent)
0162     , d(new Private)
0163 {
0164     init(Qt::Horizontal);
0165 }
0166 
0167 KexiSlider::KexiSlider(Qt::Orientation orientation, QWidget *parent)
0168     : QWidget(parent)
0169     , d(new Private)
0170 {
0171     init(orientation);
0172 }
0173 
0174 void KexiSlider::init(Qt::Orientation orientation)
0175 {
0176     d->layout=new QBoxLayout(QBoxLayout::LeftToRight, this);
0177     d->layout->setSpacing(2);
0178     d->layout->setMargin(0);
0179     d->slider = new Slider(this);
0180     d->spinBox = new QSpinBox(this);
0181     d->spinBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
0182 
0183     d->layout->addWidget(d->spinBox,0, Qt::AlignVCenter);
0184     d->layout->addWidget(d->slider,0, Qt::AlignVCenter);
0185 
0186     connect(d->slider, SIGNAL(valueChanged(int)), this, SIGNAL(valueChanged(int)));
0187     connect(d->slider, SIGNAL(sliderPressed()), this, SIGNAL(sliderPressed()));
0188     connect(d->slider, SIGNAL(sliderReleased()), this, SIGNAL(sliderReleased()));
0189     connect(d->slider, SIGNAL(valueChanged(int)), d->spinBox, SLOT(setValue(int)));
0190     connect(d->spinBox, SIGNAL(valueChanged(int)), d->slider, SLOT(setValue(int)));
0191 
0192     setMaximum(100);
0193     setOrientation(orientation);
0194     setTickPosition(QSlider::TicksAbove);
0195 }
0196 
0197 KexiSlider::~KexiSlider()
0198 {
0199     delete d;
0200 }
0201 
0202 void KexiSlider::setMinimum(int min)
0203 {
0204     d->spinBox->setMinimum(min);
0205     d->slider->setMinimum(min);
0206 }
0207 
0208 void KexiSlider::setMaximum(int max)
0209 {
0210     d->spinBox->setMaximum(max);
0211     d->slider->setMaximum(max);
0212 }
0213 
0214 void KexiSlider::setValue(int val)
0215 {
0216     d->slider->setValue(val);
0217 }
0218 
0219 int KexiSlider::minimum() const
0220 {
0221     return d->slider->minimum();
0222 }
0223 
0224 int KexiSlider::maximum() const
0225 {
0226     return d->slider->maximum();
0227 }
0228 
0229 int KexiSlider::value() const
0230 {
0231     return d->slider->value();
0232 }
0233 
0234 void KexiSlider::setPageStep(int step)
0235 {
0236     d->slider->setPageStep(step);
0237 }
0238 
0239 int KexiSlider::pageStep() const
0240 {
0241     return d->slider->pageStep();
0242 }
0243 
0244 void KexiSlider::setSingleStep(int step)
0245 {
0246     d->spinBox->setSingleStep(step);
0247     d->slider->setSingleStep(step);
0248 }
0249 
0250 int KexiSlider::singleStep() const
0251 {
0252     return d->slider->singleStep();
0253 }
0254 
0255 void KexiSlider::setOrientation(Qt::Orientation o)
0256 {
0257     d->layout->removeWidget(d->spinBox);
0258     d->slider->setOrientation(o);
0259     if(o == Qt::Horizontal)
0260         d->layout->insertWidget(0, d->spinBox);
0261     else
0262         d->layout->addWidget(d->spinBox);
0263     updateLayout();
0264 }
0265 
0266 Qt::Orientation KexiSlider::orientation() const
0267 {
0268     return d->slider->orientation();
0269 }
0270 
0271 void KexiSlider::setTickInterval(int ti)
0272 {
0273     d->slider->setTickInterval(ti);
0274 }
0275 
0276 int KexiSlider::tickInterval() const
0277 {
0278     return d->slider->tickInterval();
0279 }
0280 
0281 void KexiSlider::setTickPosition(QSlider::TickPosition pos)
0282 {
0283     d->slider->setTickPosition(pos);
0284     updateLayout();
0285 }
0286 
0287 QSlider::TickPosition KexiSlider::tickPosition() const
0288 {
0289     return d->slider->tickPosition();
0290 }
0291 
0292 void KexiSlider::setShowEditor(bool show)
0293 {
0294     d->spinBox->setVisible(show);
0295 
0296 }
0297 
0298 bool KexiSlider::showEditor() const
0299 {
0300     return d->spinBox->isVisible();
0301 }
0302 
0303 void KexiSlider::updateLayout()
0304 {
0305     d->layout->setDirection(orientation() == Qt::Horizontal ?
0306                         QBoxLayout::LeftToRight : QBoxLayout::TopToBottom);
0307 
0308     if (tickPosition() == QSlider::TicksBothSides
0309             || tickPosition() == QSlider::NoTicks) {
0310         d->layout->setAlignment(d->slider, orientation() == Qt::Horizontal ?
0311                                    Qt::AlignVCenter :Qt::AlignHCenter);
0312         d->layout->setAlignment(d->spinBox, orientation() == Qt::Horizontal ?
0313                                    Qt::AlignVCenter :Qt::AlignHCenter);
0314     } else {
0315         if (orientation() == Qt::Horizontal) {
0316             d->layout->setAlignment(d->slider,
0317                                    tickPosition() == QSlider::TicksAbove ? Qt::AlignBottom : Qt::AlignTop);
0318             d->layout->setAlignment(d->spinBox,
0319                                    tickPosition() == QSlider::TicksAbove ? Qt::AlignBottom : Qt::AlignTop);
0320         } else {
0321             d->layout->setAlignment(d->slider,
0322                                    tickPosition() == QSlider::TicksLeft ? Qt::AlignRight : Qt::AlignLeft);
0323             d->layout->setAlignment(d->spinBox,
0324                                    tickPosition() == QSlider::TicksLeft ? Qt::AlignRight : Qt::AlignLeft);
0325         }
0326     }
0327 }
0328 
0329 #include "kexislider.moc"