1 /* This file is part of the KDE project
2 Copyright (C) 2012 Oleg Kukharchuk <oleg.kuh@gmail.com>
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this program; see the file COPYING. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #include "kexislider.h"
21
22 #include <QBoxLayout>
23 #include <QSpinBox>
24 #include <QPainter>
25 #include <QStyle>
26 #include <QSlider>
27 #include <QStyleOptionSlider>
28
29
30 class Q_DECL_HIDDEN KexiSlider::Private
31 {
32 public:
Private()33 Private() {}
34
35 Slider *slider;
36 QSpinBox *spinBox;
37 QBoxLayout *layout;
38 };
39
40
41 class Slider : public QSlider
42 {
43 Q_OBJECT
44 public:
Slider(QWidget * parent)45 explicit Slider(QWidget *parent) :
46 QSlider(parent)
47 {
48
49 }
50
sizeHint() const51 QSize sizeHint() const
52 {
53 if (tickPosition() == QSlider::NoTicks)
54 return QSlider::sizeHint();
55
56 // preserve space for labels
57 QSize extra(0,0);
58
59 QFontMetrics fm(font());
60
61 int h = fm.height() + 3;
62 int w = fm.width(QString::number(maximum())) + 3;
63
64 if (orientation() == Qt::Horizontal) {
65 extra.setHeight(tickPosition() == QSlider::TicksBothSides ? h * 2 : h);
66 } else {
67 extra.setWidth(tickPosition() == QSlider::TicksBothSides ? w * 2 : w);
68 }
69
70 return QSlider::sizeHint() + extra;
71 }
72
73 protected:
paintEvent(QPaintEvent * ev)74 virtual void paintEvent(QPaintEvent *ev)
75 {
76 if (tickPosition() == QSlider::NoTicks)
77 return QSlider::paintEvent(ev);
78
79 QPainter p(this);
80 QStyleOptionSlider option;
81 initStyleOption(&option);
82
83 const QSlider::TickPosition ticks( option.tickPosition );
84 const int available(style()->proxy()->pixelMetric(QStyle::PM_SliderSpaceAvailable, &option, this));
85 int interval = option.tickInterval;
86 if( interval < 1 ) interval = option.pageStep;
87 if( interval < 1 ) return;
88
89 const QRect r(option.rect);
90 const QPalette palette(option.palette);
91 const int fudge(style()->proxy()->pixelMetric(QStyle::PM_SliderLength, &option, this) / 2);
92 int current(option.minimum);
93 int nextLabel = current;
94
95 const QFontMetrics fm(fontMetrics());
96 int h = fm.height() + 3;
97 int w = fm.width(QString::number(option.maximum)) + 3;
98
99 if(available<w)
100 nextLabel = -1;
101
102 qreal i = qreal(available) / qreal(orientation() == Qt::Horizontal ? w : h);
103 qreal t = qreal(option.maximum)/qreal(interval);
104 int valStep = t/ i + 1;
105
106 // Since there is no subrect for tickmarks do a translation here.
107 p.save();
108 p.translate(r.x(), r.y());
109
110 p.setPen(palette.color(QPalette::WindowText));
111 int extra = (option.tickPosition == QSlider::TicksBothSides ? 2 : 1);
112 int tickSize(option.orientation == Qt::Horizontal ? (r.height() - h*extra)/3:(r.width() - w*extra)/3);
113
114 while(current <= option.maximum)
115 {
116
117 const int position(QStyle::sliderPositionFromValue(option.minimum, option.maximum,
118 current, available, option.upsideDown) + fudge);
119
120 // calculate positions
121 if(option.orientation == Qt::Horizontal)
122 {
123
124 if (ticks & QSlider::TicksAbove) {
125 p.drawLine(position, h, position, tickSize + h);
126 if(current == nextLabel)
127 p.drawText(QRect(position - w/2, 0, w, h), Qt::AlignHCenter, QString::number(current));
128 }
129 if (ticks & QSlider::TicksBelow) {
130 p.drawLine( position, r.height() - h - tickSize, position, r.height() - h );
131 if(current == nextLabel)
132 p.drawText(QRect(position - w/2, r.height() - h + 3, w, h), Qt::AlignHCenter, QString::number(current));
133 }
134 } else {
135 if (ticks & QSlider::TicksAbove) {
136 p.drawLine(w, position, tickSize + w, position);
137 if(current == nextLabel)
138 p.drawText(QRect(0, position - h/2, w - 3, h), Qt::AlignRight | Qt::AlignVCenter, QString::number(current));
139 }
140 if (ticks & QSlider::TicksBelow) {
141 p.drawLine(r.width() - w - tickSize, position, r.width() - w, position );
142 if(current == nextLabel)
143 p.drawText(QRect(r.width() - w + 3, position - h/2, w, h), Qt::AlignVCenter, QString::number(current));
144 }
145 }
146
147 // go to next position
148 if (current == nextLabel)
149 nextLabel += interval*valStep;
150 current += interval;
151
152 }
153 p.restore();
154 option.subControls = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle;
155
156 style()->proxy()->drawComplexControl(QStyle::CC_Slider, &option, &p, this);
157 }
158 };
159
KexiSlider(QWidget * parent)160 KexiSlider::KexiSlider(QWidget *parent)
161 : QWidget(parent)
162 , d(new Private)
163 {
164 init(Qt::Horizontal);
165 }
166
KexiSlider(Qt::Orientation orientation,QWidget * parent)167 KexiSlider::KexiSlider(Qt::Orientation orientation, QWidget *parent)
168 : QWidget(parent)
169 , d(new Private)
170 {
171 init(orientation);
172 }
173
init(Qt::Orientation orientation)174 void KexiSlider::init(Qt::Orientation orientation)
175 {
176 d->layout=new QBoxLayout(QBoxLayout::LeftToRight, this);
177 d->layout->setSpacing(2);
178 d->layout->setMargin(0);
179 d->slider = new Slider(this);
180 d->spinBox = new QSpinBox(this);
181 d->spinBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
182
183 d->layout->addWidget(d->spinBox,0, Qt::AlignVCenter);
184 d->layout->addWidget(d->slider,0, Qt::AlignVCenter);
185
186 connect(d->slider, SIGNAL(valueChanged(int)), this, SIGNAL(valueChanged(int)));
187 connect(d->slider, SIGNAL(sliderPressed()), this, SIGNAL(sliderPressed()));
188 connect(d->slider, SIGNAL(sliderReleased()), this, SIGNAL(sliderReleased()));
189 connect(d->slider, SIGNAL(valueChanged(int)), d->spinBox, SLOT(setValue(int)));
190 connect(d->spinBox, SIGNAL(valueChanged(int)), d->slider, SLOT(setValue(int)));
191
192 setMaximum(100);
193 setOrientation(orientation);
194 setTickPosition(QSlider::TicksAbove);
195 }
196
~KexiSlider()197 KexiSlider::~KexiSlider()
198 {
199 delete d;
200 }
201
setMinimum(int min)202 void KexiSlider::setMinimum(int min)
203 {
204 d->spinBox->setMinimum(min);
205 d->slider->setMinimum(min);
206 }
207
setMaximum(int max)208 void KexiSlider::setMaximum(int max)
209 {
210 d->spinBox->setMaximum(max);
211 d->slider->setMaximum(max);
212 }
213
setValue(int val)214 void KexiSlider::setValue(int val)
215 {
216 d->slider->setValue(val);
217 }
218
minimum() const219 int KexiSlider::minimum() const
220 {
221 return d->slider->minimum();
222 }
223
maximum() const224 int KexiSlider::maximum() const
225 {
226 return d->slider->maximum();
227 }
228
value() const229 int KexiSlider::value() const
230 {
231 return d->slider->value();
232 }
233
setPageStep(int step)234 void KexiSlider::setPageStep(int step)
235 {
236 d->slider->setPageStep(step);
237 }
238
pageStep() const239 int KexiSlider::pageStep() const
240 {
241 return d->slider->pageStep();
242 }
243
setSingleStep(int step)244 void KexiSlider::setSingleStep(int step)
245 {
246 d->spinBox->setSingleStep(step);
247 d->slider->setSingleStep(step);
248 }
249
singleStep() const250 int KexiSlider::singleStep() const
251 {
252 return d->slider->singleStep();
253 }
254
setOrientation(Qt::Orientation o)255 void KexiSlider::setOrientation(Qt::Orientation o)
256 {
257 d->layout->removeWidget(d->spinBox);
258 d->slider->setOrientation(o);
259 if(o == Qt::Horizontal)
260 d->layout->insertWidget(0, d->spinBox);
261 else
262 d->layout->addWidget(d->spinBox);
263 updateLayout();
264 }
265
orientation() const266 Qt::Orientation KexiSlider::orientation() const
267 {
268 return d->slider->orientation();
269 }
270
setTickInterval(int ti)271 void KexiSlider::setTickInterval(int ti)
272 {
273 d->slider->setTickInterval(ti);
274 }
275
tickInterval() const276 int KexiSlider::tickInterval() const
277 {
278 return d->slider->tickInterval();
279 }
280
setTickPosition(QSlider::TickPosition pos)281 void KexiSlider::setTickPosition(QSlider::TickPosition pos)
282 {
283 d->slider->setTickPosition(pos);
284 updateLayout();
285 }
286
tickPosition() const287 QSlider::TickPosition KexiSlider::tickPosition() const
288 {
289 return d->slider->tickPosition();
290 }
291
setShowEditor(bool show)292 void KexiSlider::setShowEditor(bool show)
293 {
294 d->spinBox->setVisible(show);
295
296 }
297
showEditor() const298 bool KexiSlider::showEditor() const
299 {
300 return d->spinBox->isVisible();
301 }
302
updateLayout()303 void KexiSlider::updateLayout()
304 {
305 d->layout->setDirection(orientation() == Qt::Horizontal ?
306 QBoxLayout::LeftToRight : QBoxLayout::TopToBottom);
307
308 if (tickPosition() == QSlider::TicksBothSides
309 || tickPosition() == QSlider::NoTicks) {
310 d->layout->setAlignment(d->slider, orientation() == Qt::Horizontal ?
311 Qt::AlignVCenter :Qt::AlignHCenter);
312 d->layout->setAlignment(d->spinBox, orientation() == Qt::Horizontal ?
313 Qt::AlignVCenter :Qt::AlignHCenter);
314 } else {
315 if (orientation() == Qt::Horizontal) {
316 d->layout->setAlignment(d->slider,
317 tickPosition() == QSlider::TicksAbove ? Qt::AlignBottom : Qt::AlignTop);
318 d->layout->setAlignment(d->spinBox,
319 tickPosition() == QSlider::TicksAbove ? Qt::AlignBottom : Qt::AlignTop);
320 } else {
321 d->layout->setAlignment(d->slider,
322 tickPosition() == QSlider::TicksLeft ? Qt::AlignRight : Qt::AlignLeft);
323 d->layout->setAlignment(d->spinBox,
324 tickPosition() == QSlider::TicksLeft ? Qt::AlignRight : Qt::AlignLeft);
325 }
326 }
327 }
328
329 #include "kexislider.moc"
330