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 }
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