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