kalarm/lib

spinbox2.cpp
1/*
2 * spinbox2.cpp - spin box with extra pair of spin buttons (for TQt 3)
3 * Program: kalarm
4 * Copyright © 2001-2005,2008 by David Jarvie <djarvie@kde.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20
21#include <tqglobal.h>
22
23#include <stdlib.h>
24
25#include <tqstyle.h>
26#include <tqobjectlist.h>
27#include <tqapplication.h>
28#include <tqpixmap.h>
29#include <tqcursor.h>
30#include <tqtimer.h>
31#include <tqwmatrix.h>
32
33#include "spinbox2.moc"
34#include "spinbox2private.moc"
35
36
37/* List of styles which need to display the extra pair of spin buttons as a
38 * left-to-right mirror image. This is only necessary when, for example, the
39 * corners of widgets are rounded. For most styles, it is better not to mirror
40 * the spin widgets so as to keep the normal lighting/shading on either side.
41 */
42static const char* mirrorStyles[] = {
43 "PlastikStyle",
44 0 // list terminator
45};
46static bool mirrorStyle(const TQStyle&);
47
48
49int SpinBox2::mReverseLayout = -1;
50
51SpinBox2::SpinBox2(TQWidget* parent, const char* name)
52 : TQFrame(parent, name),
53 mReverseWithLayout(true)
54{
55 mUpdown2Frame = new TQFrame(this);
56 mSpinboxFrame = new TQFrame(this);
57 mUpdown2 = new ExtraSpinBox(mUpdown2Frame, "updown2");
58// mSpinbox = new MainSpinBox(0, 1, 1, this, mSpinboxFrame);
59 mSpinbox = new MainSpinBox(this, mSpinboxFrame);
60 init();
61}
62
63SpinBox2::SpinBox2(int minValue, int maxValue, int step, int step2, TQWidget* parent, const char* name)
64 : TQFrame(parent, name),
65 mReverseWithLayout(true)
66{
67 mUpdown2Frame = new TQFrame(this);
68 mSpinboxFrame = new TQFrame(this);
69 mUpdown2 = new ExtraSpinBox(minValue, maxValue, step2, mUpdown2Frame, "updown2");
70 mSpinbox = new MainSpinBox(minValue, maxValue, step, this, mSpinboxFrame);
71 setSteps(step, step2);
72 init();
73}
74
75void SpinBox2::init()
76{
77 if (mReverseLayout < 0)
78 mReverseLayout = TQApplication::reverseLayout() ? 1 : 0;
79 mMinValue = mSpinbox->minValue();
80 mMaxValue = mSpinbox->maxValue();
81 mLineStep = mSpinbox->lineStep();
82 mLineShiftStep = mSpinbox->lineShiftStep();
83 mPageStep = mUpdown2->lineStep();
84 mPageShiftStep = mUpdown2->lineShiftStep();
85 mSpinbox->setSelectOnStep(false); // default
86 mUpdown2->setSelectOnStep(false); // always false
87 setFocusProxy(mSpinbox);
88 mUpdown2->setFocusPolicy(TQWidget::NoFocus);
89 mSpinMirror = new SpinMirror(mUpdown2, mUpdown2Frame, this);
90 if (!mirrorStyle(style()))
91 mSpinMirror->hide(); // hide mirrored spin buttons when they are inappropriate
92 connect(mSpinbox, TQ_SIGNAL(valueChanged(int)), TQ_SLOT(valueChange()));
93 connect(mSpinbox, TQ_SIGNAL(valueChanged(int)), TQ_SIGNAL(valueChanged(int)));
94 connect(mSpinbox, TQ_SIGNAL(valueChanged(const TQString&)), TQ_SIGNAL(valueChanged(const TQString&)));
95 connect(mUpdown2, TQ_SIGNAL(stepped(int)), TQ_SLOT(stepPage(int)));
96 connect(mUpdown2, TQ_SIGNAL(styleUpdated()), TQ_SLOT(updateMirror()));
97}
98
100{
101 if (static_cast<int>(ro) != static_cast<int>(mSpinbox->isReadOnly()))
102 {
103 mSpinbox->setReadOnly(ro);
104 mUpdown2->setReadOnly(ro);
105 mSpinMirror->setReadOnly(ro);
106 }
107}
108
110{
111 if (reverse != mReverseWithLayout)
112 {
113 mReverseWithLayout = reverse;
114 setSteps(mLineStep, mPageStep);
115 setShiftSteps(mLineShiftStep, mPageShiftStep);
116 }
117}
118
119void SpinBox2::setEnabled(bool enabled)
120{
121 TQFrame::setEnabled(enabled);
122 updateMirror();
123}
124
126{
127 mSpinbox->setWrapping(on);
128 mUpdown2->setWrapping(on);
129}
130
131TQRect SpinBox2::up2Rect() const
132{
133 return mUpdown2->upRect();
134}
135
137{
138 return mUpdown2->downRect();
139}
140
142{
143 mLineStep = step;
144 if (reverseButtons())
145 mUpdown2->setLineStep(step); // reverse layout, but still set the right buttons
146 else
147 mSpinbox->setLineStep(step);
148}
149
150void SpinBox2::setSteps(int line, int page)
151{
152 mLineStep = line;
153 mPageStep = page;
154 if (reverseButtons())
155 {
156 mUpdown2->setLineStep(line); // reverse layout, but still set the right buttons
157 mSpinbox->setLineStep(page);
158 }
159 else
160 {
161 mSpinbox->setLineStep(line);
162 mUpdown2->setLineStep(page);
163 }
164}
165
166void SpinBox2::setShiftSteps(int line, int page)
167{
168 mLineShiftStep = line;
169 mPageShiftStep = page;
170 if (reverseButtons())
171 {
172 mUpdown2->setLineShiftStep(line); // reverse layout, but still set the right buttons
173 mSpinbox->setLineShiftStep(page);
174 }
175 else
176 {
177 mSpinbox->setLineShiftStep(line);
178 mUpdown2->setLineShiftStep(page);
179 }
180}
181
182void SpinBox2::setButtonSymbols(TQSpinBox::ButtonSymbols newSymbols)
183{
184 if (mSpinbox->buttonSymbols() == newSymbols)
185 return;
186 mSpinbox->setButtonSymbols(newSymbols);
187 mUpdown2->setButtonSymbols(newSymbols);
188}
189
190int SpinBox2::bound(int val) const
191{
192 return (val < mMinValue) ? mMinValue : (val > mMaxValue) ? mMaxValue : val;
193}
194
196{
197 mMinValue = val;
198 mSpinbox->setMinValue(val);
199 mUpdown2->setMinValue(val);
200}
201
203{
204 mMaxValue = val;
205 mSpinbox->setMaxValue(val);
206 mUpdown2->setMaxValue(val);
207}
208
209void SpinBox2::valueChange()
210{
211 int val = mSpinbox->value();
212 bool blocked = mUpdown2->signalsBlocked();
213 mUpdown2->blockSignals(true);
214 mUpdown2->setValue(val);
215 mUpdown2->blockSignals(blocked);
216}
217
218/******************************************************************************
219* Called when the widget is about to be displayed.
220* (At construction time, the spin button widths cannot be determined correctly,
221* so we need to wait until now to definitively rearrange the widget.)
222*/
223void SpinBox2::showEvent(TQShowEvent*)
224{
225 arrange();
226}
227
228TQSize SpinBox2::sizeHint() const
229{
230 getMetrics();
231 TQSize size = mSpinbox->sizeHint();
232 size.setWidth(size.width() - xSpinbox + wUpdown2 + wGap);
233 return size;
234}
235
236TQSize SpinBox2::minimumSizeHint() const
237{
238 getMetrics();
239 TQSize size = mSpinbox->minimumSizeHint();
240 size.setWidth(size.width() - xSpinbox + wUpdown2 + wGap);
241 return size;
242}
243
244void SpinBox2::styleChange(TQStyle&)
245{
246 if (mirrorStyle(style()))
247 mSpinMirror->show(); // show rounded corners with Plastik etc.
248 else
249 mSpinMirror->hide(); // keep normal shading with other styles
250 arrange();
251}
252
253/******************************************************************************
254* Called when the extra pair of spin buttons has repainted after a style change.
255* Updates the mirror image of the spin buttons.
256*/
257void SpinBox2::updateMirror()
258{
259 mSpinMirror->setNormalButtons(TQPixmap::grabWidget(mUpdown2Frame, 0, 0));
260}
261
262/******************************************************************************
263* Set the positions and sizes of all the child widgets.
264*/
265void SpinBox2::arrange()
266{
267 getMetrics();
268 TQRect arrowRect = TQStyle::visualRect(TQRect(0, 0, wUpdown2, height()), this);
269 mUpdown2Frame->setGeometry(arrowRect);
270 mUpdown2->setGeometry(-xUpdown2, 0, mUpdown2->width(), height());
271 mSpinboxFrame->setGeometry(TQStyle::visualRect(TQRect(wUpdown2 + wGap, 0, width() - wUpdown2 - wGap, height()), this));
272 mSpinbox->setGeometry(-xSpinbox, 0, mSpinboxFrame->width() + xSpinbox, height());
273 mSpinMirror->resize(wUpdown2, mUpdown2->height());
274 mSpinMirror->setGeometry(arrowRect);
275//mSpinMirror->setGeometry(TQStyle::visualRect(TQRect(0, 11, wUpdown2, height()), this));
276 mSpinMirror->setNormalButtons(TQPixmap::grabWidget(mUpdown2Frame, 0, 0));
277}
278
279/******************************************************************************
280* Calculate the width and position of the extra pair of spin buttons.
281* Style-specific adjustments are made for a better appearance.
282*/
283void SpinBox2::getMetrics() const
284{
285 TQRect rect = mUpdown2->style().querySubControlMetrics(TQStyle::CC_SpinWidget, mUpdown2, TQStyle::SC_SpinWidgetButtonField);
286 if (style().inherits("PlastikStyle"))
287 rect.setLeft(rect.left() - 1); // Plastik excludes left border from spin widget rectangle
288 xUpdown2 = mReverseLayout ? 0 : rect.left();
289 wUpdown2 = mUpdown2->width() - rect.left();
290 xSpinbox = mSpinbox->style().querySubControlMetrics(TQStyle::CC_SpinWidget, mSpinbox, TQStyle::SC_SpinWidgetEditField).left();
291 wGap = 0;
292
293 // Make style-specific adjustments for a better appearance
294 if (style().inherits("TQMotifPlusStyle"))
295 {
296 xSpinbox = 0; // show the edit control left border
297 wGap = 2; // leave a space to the right of the left-hand pair of spin buttons
298 }
299}
300
301/******************************************************************************
302* Called when the extra pair of spin buttons is clicked to step the value.
303* Normally this is a page step, but with a right-to-left language where the
304* button functions are reversed, this is a line step.
305*/
306void SpinBox2::stepPage(int step)
307{
308 if (abs(step) == mUpdown2->lineStep())
309 mSpinbox->setValue(mUpdown2->value());
310 else
311 {
312 // It's a shift step
313 int oldValue = mSpinbox->value();
314 if (!reverseButtons())
315 {
316 // The button pairs have the normal function.
317 // Page shift stepping - step up or down to a multiple of the
318 // shift page increment, leaving unchanged the part of the value
319 // which is the remainder from the page increment.
320 if (oldValue >= 0)
321 oldValue -= oldValue % mUpdown2->lineStep();
322 else
323 oldValue += (-oldValue) % mUpdown2->lineStep();
324 }
325 int adjust = mSpinbox->shiftStepAdjustment(oldValue, step);
326 if (adjust == -step
327 && ((step > 0 && oldValue + step >= mSpinbox->maxValue())
328 || (step < 0 && oldValue + step <= mSpinbox->minValue())))
329 adjust = 0; // allow stepping to the minimum or maximum value
330 mSpinbox->addValue(adjust + step);
331 }
332 bool focus = mSpinbox->selectOnStep() && mUpdown2->hasFocus();
333 if (focus)
334 mSpinbox->selectAll();
335
336 // Make the covering arrows image show the pressed arrow
337 mSpinMirror->redraw(TQPixmap::grabWidget(mUpdown2Frame, 0, 0));
338}
339
340
341/*=============================================================================
342= Class SpinBox2::MainSpinBox
343=============================================================================*/
344
345/******************************************************************************
346* Return the initial adjustment to the value for a shift step up or down, for
347* the main (visible) spin box.
348* Normally this is a line step, but with a right-to-left language where the
349* button functions are reversed, this is a page step.
350*/
351int SpinBox2::MainSpinBox::shiftStepAdjustment(int oldValue, int shiftStep)
352{
353 if (owner->reverseButtons())
354 {
355 // The button pairs have the opposite function from normal.
356 // Page shift stepping - step up or down to a multiple of the
357 // shift page increment, leaving unchanged the part of the value
358 // which is the remainder from the page increment.
359 if (oldValue >= 0)
360 oldValue -= oldValue % lineStep();
361 else
362 oldValue += (-oldValue) % lineStep();
363 }
364 return SpinBox::shiftStepAdjustment(oldValue, shiftStep);
365}
366
367
368/*=============================================================================
369= Class ExtraSpinBox
370=============================================================================*/
371
372/******************************************************************************
373* Repaint the widget.
374* If it's the first time since a style change, tell the parent SpinBox2 to
375* update the SpinMirror with the new unpressed button image. We make the
376* presumably reasonable assumption that when a style change occurs, the spin
377* buttons are unpressed.
378*/
379void ExtraSpinBox::paintEvent(TQPaintEvent* e)
380{
381 SpinBox::paintEvent(e);
382 if (mNewStylePending)
383 {
384 mNewStylePending = false;
385 emit styleUpdated();
386 }
387}
388
389
390/*=============================================================================
391= Class SpinMirror
392=============================================================================*/
393
394SpinMirror::SpinMirror(SpinBox* spinbox, TQFrame* spinFrame, TQWidget* parent, const char* name)
395 : TQCanvasView(new TQCanvas, parent, name),
396 mSpinbox(spinbox),
397 mSpinFrame(spinFrame),
398 mReadOnly(false)
399{
400 setVScrollBarMode(TQScrollView::AlwaysOff);
401 setHScrollBarMode(TQScrollView::AlwaysOff);
402 setFrameStyle(TQFrame::NoFrame);
403
404 // Find the spin widget which is part of the spin box, in order to
405 // pass on its shift-button presses.
406 TQObjectList* spinwidgets = spinbox->queryList("TQSpinWidget", 0, false, true);
407 mSpinWidget = (SpinBox*)spinwidgets->getFirst();
408 delete spinwidgets;
409}
410
411void SpinMirror::setNormalButtons(const TQPixmap& px)
412{
413 mNormalButtons = px;
414 redraw(mNormalButtons);
415}
416
417void SpinMirror::redraw()
418{
419 redraw(TQPixmap::grabWidget(mSpinFrame, 0, 0));
420}
421
422void SpinMirror::redraw(const TQPixmap& px)
423{
424 TQCanvas* c = canvas();
425 c->setBackgroundPixmap(px);
426 c->setAllChanged();
427 c->update();
428}
429
430void SpinMirror::resize(int w, int h)
431{
432 canvas()->resize(w, h);
433 TQCanvasView::resize(w, h);
434 resizeContents(w, h);
435 setWorldMatrix(TQWMatrix(-1, 0, 0, 1, w - 1, 0)); // mirror left to right
436}
437
438/******************************************************************************
439* Pass on all mouse events to the spinbox which we're covering up.
440*/
441void SpinMirror::contentsMouseEvent(TQMouseEvent* e)
442{
443 if (mReadOnly)
444 return;
445 TQPoint pt = contentsToViewport(e->pos());
446 pt.setX(pt.x() + mSpinbox->upRect().left());
447 TQApplication::postEvent(mSpinWidget, new TQMouseEvent(e->type(), pt, e->button(), e->state()));
448
449 // If the mouse button has been pressed or released, refresh the spin buttons
450 switch (e->type())
451 {
452 case TQEvent::MouseButtonPress:
453 case TQEvent::MouseButtonRelease:
454 TQTimer::singleShot(0, this, TQ_SLOT(redraw()));
455 break;
456 default:
457 break;
458 }
459}
460
461/******************************************************************************
462* Pass on all mouse events to the spinbox which we're covering up.
463*/
464void SpinMirror::contentsWheelEvent(TQWheelEvent* e)
465{
466 if (mReadOnly)
467 return;
468 TQPoint pt = contentsToViewport(e->pos());
469 pt.setX(pt.x() + mSpinbox->upRect().left());
470 TQApplication::postEvent(mSpinWidget, new TQWheelEvent(pt, e->delta(), e->state(), e->orientation()));
471}
472
473/******************************************************************************
474* Pass on to the main spinbox events which are needed to activate mouseover and
475* other graphic effects when the mouse cursor enters and leaves the widget.
476*/
477bool SpinMirror::event(TQEvent* e)
478{
479 switch (e->type())
480 {
481 case TQEvent::Leave:
482 case TQEvent::Enter:
483 TQApplication::postEvent(mSpinWidget, new TQEvent(e->type()));
484 TQTimer::singleShot(0, this, TQ_SLOT(redraw()));
485 break;
486 case TQEvent::FocusIn:
487 mSpinbox->setFocus();
488 TQTimer::singleShot(0, this, TQ_SLOT(redraw()));
489 break;
490 default:
491 break;
492 }
493 return TQCanvasView::event(e);
494}
495
496
497/*=============================================================================
498= Local functions
499=============================================================================*/
500
501/******************************************************************************
502* Determine whether the extra pair of spin buttons needs to be mirrored
503* left-to-right in the specified style.
504*/
505static bool mirrorStyle(const TQStyle& style)
506{
507 for (const char** s = mirrorStyles; *s; ++s)
508 if (style.inherits(*s))
509 return true;
510 return false;
511}
TQRect down2Rect() const
Returns the geometry of the left-hand "down" button.
Definition: spinbox2.cpp:136
int minValue() const
Returns the minimum value of the spin box.
Definition: spinbox2.h:138
bool reverseButtons() const
Returns whether the spin button pairs will be reversed for a right-to-left language.
Definition: spinbox2.h:89
void valueChanged(int value)
Signal which is emitted whenever the value of the spin box changes.
int bound(int val) const
Returns the specified value clamped to the range of the spin box.
Definition: spinbox2.cpp:190
void setSteps(int line, int page)
Sets the unshifted step increments for the two pairs of spin buttons, i.e.
Definition: spinbox2.cpp:150
void setReverseWithLayout(bool reverse)
Sets whether the spin button pairs should be reversed for a right-to-left language.
Definition: spinbox2.cpp:109
int maxValue() const
Returns the maximum value of the spin box.
Definition: spinbox2.h:140
void setLineStep(int step)
Sets the unshifted step increment for the right-hand spin buttons, i.e.
Definition: spinbox2.cpp:141
virtual void setMinValue(int val)
Sets the minimum value of the spin box.
Definition: spinbox2.cpp:195
TQRect up2Rect() const
Returns the geometry of the left-hand "up" button.
Definition: spinbox2.cpp:131
virtual void setReadOnly(bool readOnly)
Sets whether the spin box can be changed by the user.
Definition: spinbox2.cpp:99
virtual void setMaxValue(int val)
Sets the maximum value of the spin box.
Definition: spinbox2.cpp:202
virtual void setWrapping(bool on)
Sets whether it is possible to step the value from the highest value to the lowest value and vice ver...
Definition: spinbox2.cpp:125
SpinBox2(TQWidget *parent=0, const char *name=0)
Constructor.
Definition: spinbox2.cpp:51
virtual void setButtonSymbols(TQSpinBox::ButtonSymbols)
Sets the button symbols to use (arrows or plus/minus).
Definition: spinbox2.cpp:182
virtual void setEnabled(bool enabled)
Sets whether the widget is enabled.
Definition: spinbox2.cpp:119
void setShiftSteps(int line, int page)
Sets the shifted step increments for the two pairs of spin buttons, i.e.
Definition: spinbox2.cpp:166
Spin box with accelerated shift key stepping and read-only option.
Definition: spinbox.h:43
virtual int shiftStepAdjustment(int oldValue, int shiftStep)
Returns the initial adjustment to the value for a shift step up or down.
Definition: spinbox.cpp:445