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  */
42 static const char* mirrorStyles[] = {
43  "PlastikStyle",
44  0 // list terminator
45 };
46 static bool mirrorStyle(const TQStyle&);
47 
48 
49 int SpinBox2::mReverseLayout = -1;
50 
51 SpinBox2::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 
63 SpinBox2::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 
75 void 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 
99 void SpinBox2::setReadOnly(bool ro)
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 
119 void SpinBox2::setEnabled(bool enabled)
120 {
121  TQFrame::setEnabled(enabled);
122  updateMirror();
123 }
124 
126 {
127  mSpinbox->setWrapping(on);
128  mUpdown2->setWrapping(on);
129 }
130 
131 TQRect SpinBox2::up2Rect() const
132 {
133  return mUpdown2->upRect();
134 }
135 
136 TQRect SpinBox2::down2Rect() const
137 {
138  return mUpdown2->downRect();
139 }
140 
141 void SpinBox2::setLineStep(int step)
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 
150 void 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 
166 void 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 
182 void SpinBox2::setButtonSymbols(TQSpinBox::ButtonSymbols newSymbols)
183 {
184  if (mSpinbox->buttonSymbols() == newSymbols)
185  return;
186  mSpinbox->setButtonSymbols(newSymbols);
187  mUpdown2->setButtonSymbols(newSymbols);
188 }
189 
190 int 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 
209 void 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 */
223 void SpinBox2::showEvent(TQShowEvent*)
224 {
225  arrange();
226 }
227 
228 TQSize SpinBox2::sizeHint() const
229 {
230  getMetrics();
231  TQSize size = mSpinbox->sizeHint();
232  size.setWidth(size.width() - xSpinbox + wUpdown2 + wGap);
233  return size;
234 }
235 
236 TQSize SpinBox2::minimumSizeHint() const
237 {
238  getMetrics();
239  TQSize size = mSpinbox->minimumSizeHint();
240  size.setWidth(size.width() - xSpinbox + wUpdown2 + wGap);
241  return size;
242 }
243 
244 void 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 */
257 void 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 */
265 void 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 */
283 void 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 */
306 void 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 */
351 int 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 */
379 void 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 
394 SpinMirror::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 
411 void SpinMirror::setNormalButtons(const TQPixmap& px)
412 {
413  mNormalButtons = px;
414  redraw(mNormalButtons);
415 }
416 
417 void SpinMirror::redraw()
418 {
419  redraw(TQPixmap::grabWidget(mSpinFrame, 0, 0));
420 }
421 
422 void SpinMirror::redraw(const TQPixmap& px)
423 {
424  TQCanvas* c = canvas();
425  c->setBackgroundPixmap(px);
426  c->setAllChanged();
427  c->update();
428 }
429 
430 void 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 */
441 void 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 */
464 void 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 */
477 bool 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 */
505 static 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 }
virtual void setReadOnly(bool readOnly)
Sets whether the spin box can be changed by the user.
Definition: spinbox2.cpp:99
virtual void setMinValue(int val)
Sets the minimum value of the spin box.
Definition: spinbox2.cpp:195
int minValue() const
Returns the minimum value of the spin box.
Definition: spinbox2.h:138
TQRect up2Rect() const
Returns the geometry of the left-hand "up" button.
Definition: spinbox2.cpp:131
bool reverseButtons() const
Returns whether the spin button pairs will be reversed for a right-to-left language.
Definition: spinbox2.h:89
virtual void setEnabled(bool enabled)
Sets whether the widget is enabled.
Definition: spinbox2.cpp:119
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
int bound(int val) const
Returns the specified value clamped to the range of the spin box.
Definition: spinbox2.cpp:190
int maxValue() const
Returns the maximum value of the spin box.
Definition: spinbox2.h:140
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 setLineStep(int step)
Sets the unshifted step increment for the right-hand spin buttons, i.e.
Definition: spinbox2.cpp:141
Spin box with accelerated shift key stepping and read-only option.
Definition: spinbox.h:42
virtual void setButtonSymbols(TQSpinBox::ButtonSymbols)
Sets the button symbols to use (arrows or plus/minus).
Definition: spinbox2.cpp:182
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
SpinBox2(TQWidget *parent=0, const char *name=0)
Constructor.
Definition: spinbox2.cpp:51
TQRect down2Rect() const
Returns the geometry of the left-hand "down" button.
Definition: spinbox2.cpp:136
void setShiftSteps(int line, int page)
Sets the shifted step increments for the two pairs of spin buttons, i.e.
Definition: spinbox2.cpp:166
void setReverseWithLayout(bool reverse)
Sets whether the spin button pairs should be reversed for a right-to-left language.
Definition: spinbox2.cpp:109
void valueChanged(int value)
Signal which is emitted whenever the value of the spin box changes.