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 }
TQRect down2Rect() const
Returns the geometry of the left-hand "down" button.
Definition: spinbox2.cpp:136
int minValue() const
Returns the minimum value of the spin box.
Definition: spinbox2.h:138
bool reverseButtons() const
Returns whether the spin button pairs will be reversed for a right-to-left language.
Definition: spinbox2.h:89
void valueChanged(int value)
Signal which is emitted whenever the value of the spin box changes.
int bound(int val) const
Returns the specified value clamped to the range of the spin box.
Definition: spinbox2.cpp:190
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 setReverseWithLayout(bool reverse)
Sets whether the spin button pairs should be reversed for a right-to-left language.
Definition: spinbox2.cpp:109
int maxValue() const
Returns the maximum value of the spin box.
Definition: spinbox2.h:140
void setLineStep(int step)
Sets the unshifted step increment for the right-hand spin buttons, i.e.
Definition: spinbox2.cpp:141
virtual void setMinValue(int val)
Sets the minimum value of the spin box.
Definition: spinbox2.cpp:195
TQRect up2Rect() const
Returns the geometry of the left-hand "up" button.
Definition: spinbox2.cpp:131
virtual void setReadOnly(bool readOnly)
Sets whether the spin box can be changed by the user.
Definition: spinbox2.cpp:99
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
SpinBox2(TQWidget *parent=0, const char *name=0)
Constructor.
Definition: spinbox2.cpp:51
virtual void setButtonSymbols(TQSpinBox::ButtonSymbols)
Sets the button symbols to use (arrows or plus/minus).
Definition: spinbox2.cpp:182
virtual void setEnabled(bool enabled)
Sets whether the widget is enabled.
Definition: spinbox2.cpp:119
void setShiftSteps(int line, int page)
Sets the shifted step increments for the two pairs of spin buttons, i.e.
Definition: spinbox2.cpp:166
Spin box with accelerated shift key stepping and read-only option.
Definition: spinbox.h:43
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