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 
27 SpinBox::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 
35 SpinBox::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 
43 void 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 
72 void SpinBox::setReadOnly(bool ro)
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 
83 int SpinBox::bound(int val) const
84 {
85  return (val < mMinValue) ? mMinValue : (val > mMaxValue) ? mMaxValue : val;
86 }
87 
88 void SpinBox::setMinValue(int val)
89 {
90  mMinValue = val;
91  TQSpinBox::setMinValue(val);
92  mShiftMinBound = false;
93 }
94 
95 void SpinBox::setMaxValue(int val)
96 {
97  mMaxValue = val;
98  TQSpinBox::setMaxValue(val);
99  mShiftMaxBound = false;
100 }
101 
102 void SpinBox::setLineStep(int step)
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 */
135 void 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 */
188 void 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 */
202 bool 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 */
354 bool 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 */
445 int 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 */
469 int 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 }
void addValue(int change)
Adds a value to the current value of the spin box.
Definition: spinbox.h:71
virtual void setReadOnly(bool readOnly)
Sets whether the spin box can be changed by the user.
Definition: spinbox.cpp:72
void stepped(int step)
Signal emitted when the spin box's value is stepped (by the shifted or unshifted increment).
int bound(int val) const
Returns the specified value clamped to the range of the spin box.
Definition: spinbox.cpp:83
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
virtual void stepDown()
Decrements the value of the spin box by the unshifted step increment.
Definition: spinbox.cpp:123
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
void setLineShiftStep(int step)
Sets the shifted step increment, i.e.
Definition: spinbox.cpp:109
void setMinValue(int val)
Sets the minimum value of the spin box.
Definition: spinbox.cpp:88
void setLineStep(int step)
Sets the unshifted step increment, i.e.
Definition: spinbox.cpp:102
virtual void updateDisplay()
Updates the contents of the embedded TQLineEdit to reflect the current value using mapValueToText().
Definition: spinbox.cpp:193
virtual void valueChange()
A virtual method called whenever the value of the spin box has changed.
Definition: spinbox.cpp:158
virtual bool eventFilter(TQObject *, TQEvent *)
Receives events destined for the spin widget or for the edit field.
Definition: spinbox.cpp:202
void setMaxValue(int val)
Sets the maximum value of the spin box.
Definition: spinbox.cpp:95