kalarm/lib

spinbox.cpp
1/*
2 * spinbox.cpp - spin box with read-only option and shift-click step value
3 * Program: kalarm
4 * Copyright © 2002,2004,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 <tdeversion.h>
22#include <tqlineedit.h>
23#include <tqobjectlist.h>
24#include "spinbox.moc"
25
26
27SpinBox::SpinBox(TQWidget* parent, const char* name)
28 : TQSpinBox(0, 99999, 1, parent, name),
29 mMinValue(TQSpinBox::minValue()),
30 mMaxValue(TQSpinBox::maxValue())
31{
32 init();
33}
34
35SpinBox::SpinBox(int minValue, int maxValue, int step, TQWidget* parent, const char* name)
36 : TQSpinBox(minValue, maxValue, step, parent, name),
37 mMinValue(minValue),
38 mMaxValue(maxValue)
39{
40 init();
41}
42
43void SpinBox::init()
44{
45 int step = TQSpinBox::lineStep();
46 mLineStep = step;
47 mLineShiftStep = step;
48 mCurrentButton = NO_BUTTON;
49 mShiftMouse = false;
50 mShiftMinBound = false;
51 mShiftMaxBound = false;
52 mSelectOnStep = true;
53 mReadOnly = false;
54 mSuppressSignals = false;
55 mEdited = false;
56
57 // Find the spin widgets which are part of the spin boxes, in order to
58 // handle their shift-button presses.
59 TQObjectList* spinwidgets = queryList("TQSpinWidget", 0, false, true);
60 TQSpinWidget* spin = (TQSpinWidget*)spinwidgets->getFirst();
61 if (spin)
62 spin->installEventFilter(this); // handle shift-button presses
63 delete spinwidgets;
64 editor()->installEventFilter(this); // handle shift-up/down arrow presses
65
66#if KDE_IS_VERSION(3,1,90)
67 // Detect when the text field is edited
68 connect(editor(), TQ_SIGNAL(textChanged(const TQString&)), TQ_SLOT(textEdited()));
69#endif
70}
71
73{
74 if ((int)ro != (int)mReadOnly)
75 {
76 mReadOnly = ro;
77 editor()->setReadOnly(ro);
78 if (ro)
79 setShiftStepping(false, mCurrentButton);
80 }
81}
82
83int SpinBox::bound(int val) const
84{
85 return (val < mMinValue) ? mMinValue : (val > mMaxValue) ? mMaxValue : val;
86}
87
89{
90 mMinValue = val;
91 TQSpinBox::setMinValue(val);
92 mShiftMinBound = false;
93}
94
96{
97 mMaxValue = val;
98 TQSpinBox::setMaxValue(val);
99 mShiftMaxBound = false;
100}
101
103{
104 mLineStep = step;
105 if (!mShiftMouse)
106 TQSpinBox::setLineStep(step);
107}
108
110{
111 mLineShiftStep = step;
112 if (mShiftMouse)
113 TQSpinBox::setLineStep(step);
114}
115
117{
118 int step = TQSpinBox::lineStep();
119 addValue(step);
120 emit stepped(step);
121}
122
124{
125 int step = -TQSpinBox::lineStep();
126 addValue(step);
127 emit stepped(step);
128}
129
130/******************************************************************************
131* Adds a positive or negative increment to the current value, wrapping as appropriate.
132* If 'current' is true, any temporary 'shift' values for the range are used instead
133* of the real maximum and minimum values.
134*/
135void SpinBox::addValue(int change, bool current)
136{
137 int newval = value() + change;
138 int maxval = current ? TQSpinBox::maxValue() : mMaxValue;
139 int minval = current ? TQSpinBox::minValue() : mMinValue;
140 if (wrapping())
141 {
142 int range = maxval - minval + 1;
143 if (newval > maxval)
144 newval = minval + (newval - maxval - 1) % range;
145 else if (newval < minval)
146 newval = maxval - (minval - 1 - newval) % range;
147 }
148 else
149 {
150 if (newval > maxval)
151 newval = maxval;
152 else if (newval < minval)
153 newval = minval;
154 }
155 setValue(newval);
156}
157
159{
160 if (!mSuppressSignals)
161 {
162 int val = value();
163 if (mShiftMinBound && val >= mMinValue)
164 {
165 // Reinstate the minimum bound now that the value has returned to the normal range.
166 TQSpinBox::setMinValue(mMinValue);
167 mShiftMinBound = false;
168 }
169 if (mShiftMaxBound && val <= mMaxValue)
170 {
171 // Reinstate the maximum bound now that the value has returned to the normal range.
172 TQSpinBox::setMaxValue(mMaxValue);
173 mShiftMaxBound = false;
174 }
175
176 bool focus = !mSelectOnStep && hasFocus();
177 if (focus)
178 clearFocus(); // prevent selection of the spin box text
179 TQSpinBox::valueChange();
180 if (focus)
181 setFocus();
182 }
183}
184
185/******************************************************************************
186* Called whenever the line edit text is changed.
187*/
188void SpinBox::textEdited()
189{
190 mEdited = true;
191}
192
194{
195 mEdited = false;
196 TQSpinBox::updateDisplay();
197}
198
199/******************************************************************************
200* Receives events destined for the spin widget or for the edit field.
201*/
202bool SpinBox::eventFilter(TQObject* obj, TQEvent* e)
203{
204 if (obj == editor())
205 {
206 int step = 0;
207 bool shift = false;
208 switch (e->type())
209 {
210 case TQEvent::KeyPress:
211 {
212 // Up and down arrow keys step the value
213 TQKeyEvent* ke = (TQKeyEvent*)e;
214 int key = ke->key();
215 if (key == TQt::Key_Up)
216 step = 1;
217 else if (key == TQt::Key_Down)
218 step = -1;
219 shift = ((ke->state() & (TQt::ShiftButton | TQt::AltButton)) == TQt::ShiftButton);
220 break;
221 }
222 case TQEvent::Wheel:
223 {
224 TQWheelEvent* we = (TQWheelEvent*)e;
225 step = (we->delta() > 0) ? 1 : -1;
226 shift = ((we->state() & (TQt::ShiftButton | TQt::AltButton)) == TQt::ShiftButton);
227 break;
228 }
229#if KDE_IS_VERSION(3,1,90)
230 case TQEvent::Leave:
231 if (mEdited)
232 interpretText();
233 break;
234#endif
235 default:
236 break;
237 }
238 if (step)
239 {
240 if (mReadOnly)
241 return true; // discard up/down arrow keys or wheel
242 if (shift)
243 {
244 // Shift stepping
245 int val = value();
246 if (step > 0)
247 step = mLineShiftStep - val % mLineShiftStep;
248 else
249 step = - ((val + mLineShiftStep - 1) % mLineShiftStep + 1);
250 }
251 else
252 step = (step > 0) ? mLineStep : -mLineStep;
253 addValue(step, false);
254 return true;
255 }
256 }
257 else
258 {
259 int etype = e->type(); // avoid switch compile warnings
260 switch (etype)
261 {
262 case TQEvent::MouseButtonPress:
263 case TQEvent::MouseButtonDblClick:
264 {
265 TQMouseEvent* me = (TQMouseEvent*)e;
266 if (me->button() == TQt::LeftButton)
267 {
268 // It's a left button press. Set normal or shift stepping as appropriate.
269 if (mReadOnly)
270 return true; // discard the event
271 mCurrentButton = whichButton(me->pos());
272 if (mCurrentButton == NO_BUTTON)
273 return true;
274 bool shift = (me->state() & (TQt::ShiftButton | TQt::AltButton)) == TQt::ShiftButton;
275 if (setShiftStepping(shift, mCurrentButton))
276 return true; // hide the event from the spin widget
277 return false; // forward event to the destination widget
278 }
279 break;
280 }
281 case TQEvent::MouseButtonRelease:
282 {
283 TQMouseEvent* me = (TQMouseEvent*)e;
284 if (me->button() == TQt::LeftButton && mShiftMouse)
285 {
286 setShiftStepping(false, mCurrentButton); // cancel shift stepping
287 return false; // forward event to the destination widget
288 }
289 break;
290 }
291 case TQEvent::MouseMove:
292 {
293 TQMouseEvent* me = (TQMouseEvent*)e;
294 if (me->state() & TQt::LeftButton)
295 {
296 // The left button is down. Track which spin button it's in.
297 if (mReadOnly)
298 return true; // discard the event
299 int newButton = whichButton(me->pos());
300 if (newButton != mCurrentButton)
301 {
302 // The mouse has moved to a new spin button.
303 // Set normal or shift stepping as appropriate.
304 mCurrentButton = newButton;
305 bool shift = (me->state() & (TQt::ShiftButton | TQt::AltButton)) == TQt::ShiftButton;
306 if (setShiftStepping(shift, mCurrentButton))
307 return true; // hide the event from the spin widget
308 }
309 return false; // forward event to the destination widget
310 }
311 break;
312 }
313 case TQEvent::Wheel:
314 {
315 TQWheelEvent* we = (TQWheelEvent*)e;
316 bool shift = (we->state() & (TQt::ShiftButton | TQt::AltButton)) == TQt::ShiftButton;
317 if (setShiftStepping(shift, (we->delta() > 0 ? UP : DOWN)))
318 return true; // hide the event from the spin widget
319 return false; // forward event to the destination widget
320 }
321 case TQEvent::KeyPress:
322 case TQEvent::KeyRelease:
323 case TQEvent::AccelOverride: // this is needed to receive Shift presses!
324 {
325 TQKeyEvent* ke = (TQKeyEvent*)e;
326 int key = ke->key();
327 int state = ke->state();
328 if ((state & TQt::LeftButton)
329 && (key == TQt::Key_Shift || key == TQt::Key_Alt))
330 {
331 // The left mouse button is down, and the Shift or Alt key has changed
332 if (mReadOnly)
333 return true; // discard the event
334 state ^= (key == TQt::Key_Shift) ? TQt::ShiftButton : TQt::AltButton; // new state
335 bool shift = (state & (TQt::ShiftButton | TQt::AltButton)) == TQt::ShiftButton;
336 if ((!shift && mShiftMouse) || (shift && !mShiftMouse))
337 {
338 // The effective shift state has changed.
339 // Set normal or shift stepping as appropriate.
340 if (setShiftStepping(shift, mCurrentButton))
341 return true; // hide the event from the spin widget
342 }
343 }
344 break;
345 }
346 }
347 }
348 return TQSpinBox::eventFilter(obj, e);
349}
350
351/******************************************************************************
352* Set spin widget stepping to the normal or shift increment.
353*/
354bool SpinBox::setShiftStepping(bool shift, int currentButton)
355{
356 if (currentButton == NO_BUTTON)
357 shift = false;
358 if (shift && !mShiftMouse)
359 {
360 /* The value is to be stepped to a multiple of the shift increment.
361 * Adjust the value so that after the spin widget steps it, it will be correct.
362 * Then, if the mouse button is held down, the spin widget will continue to
363 * step by the shift amount.
364 */
365 int val = value();
366 int step = (currentButton == UP) ? mLineShiftStep : (currentButton == DOWN) ? -mLineShiftStep : 0;
367 int adjust = shiftStepAdjustment(val, step);
368 mShiftMouse = true;
369 if (adjust)
370 {
371 /* The value is to be stepped by other than the shift increment,
372 * presumably because it is being set to a multiple of the shift
373 * increment. Achieve this by making the adjustment here, and then
374 * allowing the normal step processing to complete the job by
375 * adding/subtracting the normal shift increment.
376 */
377 if (!wrapping())
378 {
379 // Prevent the step from going past the spinbox's range, or
380 // to the minimum value if that has a special text unless it is
381 // already at the minimum value + 1.
382 int newval = val + adjust + step;
383 int svt = specialValueText().isEmpty() ? 0 : 1;
384 int minval = mMinValue + svt;
385 if (newval <= minval || newval >= mMaxValue)
386 {
387 // Stepping to the minimum or maximum value
388 if (svt && newval <= mMinValue && val == mMinValue)
389 newval = mMinValue;
390 else
391 newval = (newval <= minval) ? minval : mMaxValue;
392 TQSpinBox::setValue(newval);
393 emit stepped(step);
394 return true;
395 }
396
397 // If the interim value will lie outside the spinbox's range,
398 // temporarily adjust the range to allow the value to be set.
399 int tempval = val + adjust;
400 if (tempval < mMinValue)
401 {
402 TQSpinBox::setMinValue(tempval);
403 mShiftMinBound = true;
404 }
405 else if (tempval > mMaxValue)
406 {
407 TQSpinBox::setMaxValue(tempval);
408 mShiftMaxBound = true;
409 }
410 }
411
412 // Don't process changes since this new value will be stepped immediately
413 mSuppressSignals = true;
414 bool blocked = signalsBlocked();
415 blockSignals(true);
416 addValue(adjust, true);
417 blockSignals(blocked);
418 mSuppressSignals = false;
419 }
420 TQSpinBox::setLineStep(mLineShiftStep);
421 }
422 else if (!shift && mShiftMouse)
423 {
424 // Reinstate to normal (non-shift) stepping
425 TQSpinBox::setLineStep(mLineStep);
426 TQSpinBox::setMinValue(mMinValue);
427 TQSpinBox::setMaxValue(mMaxValue);
428 mShiftMinBound = mShiftMaxBound = false;
429 mShiftMouse = false;
430 }
431 return false;
432}
433
434/******************************************************************************
435* Return the initial adjustment to the value for a shift step up or down.
436* The default is to step up or down to the nearest multiple of the shift
437* increment, so the adjustment returned is for stepping up the decrement
438* required to round down to a multiple of the shift increment <= current value,
439* or for stepping down the increment required to round up to a multiple of the
440* shift increment >= current value.
441* This method's caller then adjusts the resultant value if necessary to cater
442* for the widget's minimum/maximum value, and wrapping.
443* This should really be a static method, but it needs to be virtual...
444*/
445int SpinBox::shiftStepAdjustment(int oldValue, int shiftStep)
446{
447 if (oldValue == 0 || shiftStep == 0)
448 return 0;
449 if (shiftStep > 0)
450 {
451 if (oldValue >= 0)
452 return -(oldValue % shiftStep);
453 else
454 return (-oldValue - 1) % shiftStep + 1 - shiftStep;
455 }
456 else
457 {
458 shiftStep = -shiftStep;
459 if (oldValue >= 0)
460 return shiftStep - ((oldValue - 1) % shiftStep + 1);
461 else
462 return (-oldValue) % shiftStep;
463 }
464}
465
466/******************************************************************************
467* Find which spin widget button a mouse event is in.
468*/
469int SpinBox::whichButton(const TQPoint& pos)
470{
471 if (upRect().contains(pos))
472 return UP;
473 if (downRect().contains(pos))
474 return DOWN;
475 return NO_BUTTON;
476}
virtual bool eventFilter(TQObject *, TQEvent *)
Receives events destined for the spin widget or for the edit field.
Definition: spinbox.cpp:202
virtual void valueChange()
A virtual method called whenever the value of the spin box has changed.
Definition: spinbox.cpp:158
void stepped(int step)
Signal emitted when the spin box's value is stepped (by the shifted or unshifted increment).
void setLineShiftStep(int step)
Sets the shifted step increment, i.e.
Definition: spinbox.cpp:109
virtual void setReadOnly(bool readOnly)
Sets whether the spin box can be changed by the user.
Definition: spinbox.cpp:72
void setMinValue(int val)
Sets the minimum value of the spin box.
Definition: spinbox.cpp:88
virtual void stepUp()
Increments the value of the spin box by the unshifted step increment.
Definition: spinbox.cpp:116
SpinBox(TQWidget *parent=0, const char *name=0)
Constructor.
Definition: spinbox.cpp:27
void setLineStep(int step)
Sets the unshifted step increment, i.e.
Definition: spinbox.cpp:102
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
int bound(int val) const
Returns the specified value clamped to the range of the spin box.
Definition: spinbox.cpp:83
void addValue(int change)
Adds a value to the current value of the spin box.
Definition: spinbox.h:71
virtual void stepDown()
Decrements the value of the spin box by the unshifted step increment.
Definition: spinbox.cpp:123
void setMaxValue(int val)
Sets the maximum value of the spin box.
Definition: spinbox.cpp:95
virtual void updateDisplay()
Updates the contents of the embedded TQLineEdit to reflect the current value using mapValueToText().
Definition: spinbox.cpp:193