spinbox2.cpp
00001 /* 00002 * spinbox2.cpp - spin box with extra pair of spin buttons (for TQt 3) 00003 * Program: kalarm 00004 * Copyright © 2001-2005,2008 by David Jarvie <djarvie@kde.org> 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License along 00017 * with this program; if not, write to the Free Software Foundation, Inc., 00018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include <tqglobal.h> 00022 00023 #include <stdlib.h> 00024 00025 #include <tqstyle.h> 00026 #include <tqobjectlist.h> 00027 #include <tqapplication.h> 00028 #include <tqpixmap.h> 00029 #include <tqcursor.h> 00030 #include <tqtimer.h> 00031 #include <tqwmatrix.h> 00032 00033 #include "spinbox2.moc" 00034 #include "spinbox2private.moc" 00035 00036 00037 /* List of styles which need to display the extra pair of spin buttons as a 00038 * left-to-right mirror image. This is only necessary when, for example, the 00039 * corners of widgets are rounded. For most styles, it is better not to mirror 00040 * the spin widgets so as to keep the normal lighting/shading on either side. 00041 */ 00042 static const char* mirrorStyles[] = { 00043 "PlastikStyle", 00044 0 // list terminator 00045 }; 00046 static bool mirrorStyle(const TQStyle&); 00047 00048 00049 int SpinBox2::mReverseLayout = -1; 00050 00051 SpinBox2::SpinBox2(TQWidget* parent, const char* name) 00052 : TQFrame(parent, name), 00053 mReverseWithLayout(true) 00054 { 00055 mUpdown2Frame = new TQFrame(this); 00056 mSpinboxFrame = new TQFrame(this); 00057 mUpdown2 = new ExtraSpinBox(mUpdown2Frame, "updown2"); 00058 // mSpinbox = new MainSpinBox(0, 1, 1, this, mSpinboxFrame); 00059 mSpinbox = new MainSpinBox(this, mSpinboxFrame); 00060 init(); 00061 } 00062 00063 SpinBox2::SpinBox2(int minValue, int maxValue, int step, int step2, TQWidget* parent, const char* name) 00064 : TQFrame(parent, name), 00065 mReverseWithLayout(true) 00066 { 00067 mUpdown2Frame = new TQFrame(this); 00068 mSpinboxFrame = new TQFrame(this); 00069 mUpdown2 = new ExtraSpinBox(minValue, maxValue, step2, mUpdown2Frame, "updown2"); 00070 mSpinbox = new MainSpinBox(minValue, maxValue, step, this, mSpinboxFrame); 00071 setSteps(step, step2); 00072 init(); 00073 } 00074 00075 void SpinBox2::init() 00076 { 00077 if (mReverseLayout < 0) 00078 mReverseLayout = TQApplication::reverseLayout() ? 1 : 0; 00079 mMinValue = mSpinbox->minValue(); 00080 mMaxValue = mSpinbox->maxValue(); 00081 mLineStep = mSpinbox->lineStep(); 00082 mLineShiftStep = mSpinbox->lineShiftStep(); 00083 mPageStep = mUpdown2->lineStep(); 00084 mPageShiftStep = mUpdown2->lineShiftStep(); 00085 mSpinbox->setSelectOnStep(false); // default 00086 mUpdown2->setSelectOnStep(false); // always false 00087 setFocusProxy(mSpinbox); 00088 mUpdown2->setFocusPolicy(TQ_NoFocus); 00089 mSpinMirror = new SpinMirror(mUpdown2, mUpdown2Frame, this); 00090 if (!mirrorStyle(style())) 00091 mSpinMirror->hide(); // hide mirrored spin buttons when they are inappropriate 00092 connect(mSpinbox, TQT_SIGNAL(valueChanged(int)), TQT_SLOT(valueChange())); 00093 connect(mSpinbox, TQT_SIGNAL(valueChanged(int)), TQT_SIGNAL(valueChanged(int))); 00094 connect(mSpinbox, TQT_SIGNAL(valueChanged(const TQString&)), TQT_SIGNAL(valueChanged(const TQString&))); 00095 connect(mUpdown2, TQT_SIGNAL(stepped(int)), TQT_SLOT(stepPage(int))); 00096 connect(mUpdown2, TQT_SIGNAL(styleUpdated()), TQT_SLOT(updateMirror())); 00097 } 00098 00099 void SpinBox2::setReadOnly(bool ro) 00100 { 00101 if (static_cast<int>(ro) != static_cast<int>(mSpinbox->isReadOnly())) 00102 { 00103 mSpinbox->setReadOnly(ro); 00104 mUpdown2->setReadOnly(ro); 00105 mSpinMirror->setReadOnly(ro); 00106 } 00107 } 00108 00109 void SpinBox2::setReverseWithLayout(bool reverse) 00110 { 00111 if (reverse != mReverseWithLayout) 00112 { 00113 mReverseWithLayout = reverse; 00114 setSteps(mLineStep, mPageStep); 00115 setShiftSteps(mLineShiftStep, mPageShiftStep); 00116 } 00117 } 00118 00119 void SpinBox2::setEnabled(bool enabled) 00120 { 00121 TQFrame::setEnabled(enabled); 00122 updateMirror(); 00123 } 00124 00125 void SpinBox2::setWrapping(bool on) 00126 { 00127 mSpinbox->setWrapping(on); 00128 mUpdown2->setWrapping(on); 00129 } 00130 00131 TQRect SpinBox2::up2Rect() const 00132 { 00133 return mUpdown2->upRect(); 00134 } 00135 00136 TQRect SpinBox2::down2Rect() const 00137 { 00138 return mUpdown2->downRect(); 00139 } 00140 00141 void SpinBox2::setLineStep(int step) 00142 { 00143 mLineStep = step; 00144 if (reverseButtons()) 00145 mUpdown2->setLineStep(step); // reverse layout, but still set the right buttons 00146 else 00147 mSpinbox->setLineStep(step); 00148 } 00149 00150 void SpinBox2::setSteps(int line, int page) 00151 { 00152 mLineStep = line; 00153 mPageStep = page; 00154 if (reverseButtons()) 00155 { 00156 mUpdown2->setLineStep(line); // reverse layout, but still set the right buttons 00157 mSpinbox->setLineStep(page); 00158 } 00159 else 00160 { 00161 mSpinbox->setLineStep(line); 00162 mUpdown2->setLineStep(page); 00163 } 00164 } 00165 00166 void SpinBox2::setShiftSteps(int line, int page) 00167 { 00168 mLineShiftStep = line; 00169 mPageShiftStep = page; 00170 if (reverseButtons()) 00171 { 00172 mUpdown2->setLineShiftStep(line); // reverse layout, but still set the right buttons 00173 mSpinbox->setLineShiftStep(page); 00174 } 00175 else 00176 { 00177 mSpinbox->setLineShiftStep(line); 00178 mUpdown2->setLineShiftStep(page); 00179 } 00180 } 00181 00182 void SpinBox2::setButtonSymbols(TQSpinBox::ButtonSymbols newSymbols) 00183 { 00184 if (mSpinbox->buttonSymbols() == newSymbols) 00185 return; 00186 mSpinbox->setButtonSymbols(newSymbols); 00187 mUpdown2->setButtonSymbols(newSymbols); 00188 } 00189 00190 int SpinBox2::bound(int val) const 00191 { 00192 return (val < mMinValue) ? mMinValue : (val > mMaxValue) ? mMaxValue : val; 00193 } 00194 00195 void SpinBox2::setMinValue(int val) 00196 { 00197 mMinValue = val; 00198 mSpinbox->setMinValue(val); 00199 mUpdown2->setMinValue(val); 00200 } 00201 00202 void SpinBox2::setMaxValue(int val) 00203 { 00204 mMaxValue = val; 00205 mSpinbox->setMaxValue(val); 00206 mUpdown2->setMaxValue(val); 00207 } 00208 00209 void SpinBox2::valueChange() 00210 { 00211 int val = mSpinbox->value(); 00212 bool blocked = mUpdown2->signalsBlocked(); 00213 mUpdown2->blockSignals(true); 00214 mUpdown2->setValue(val); 00215 mUpdown2->blockSignals(blocked); 00216 } 00217 00218 /****************************************************************************** 00219 * Called when the widget is about to be displayed. 00220 * (At construction time, the spin button widths cannot be determined correctly, 00221 * so we need to wait until now to definitively rearrange the widget.) 00222 */ 00223 void SpinBox2::showEvent(TQShowEvent*) 00224 { 00225 arrange(); 00226 } 00227 00228 TQSize SpinBox2::sizeHint() const 00229 { 00230 getMetrics(); 00231 TQSize size = mSpinbox->sizeHint(); 00232 size.setWidth(size.width() - xSpinbox + wUpdown2 + wGap); 00233 return size; 00234 } 00235 00236 TQSize SpinBox2::minimumSizeHint() const 00237 { 00238 getMetrics(); 00239 TQSize size = mSpinbox->minimumSizeHint(); 00240 size.setWidth(size.width() - xSpinbox + wUpdown2 + wGap); 00241 return size; 00242 } 00243 00244 void SpinBox2::styleChange(TQStyle&) 00245 { 00246 if (mirrorStyle(style())) 00247 mSpinMirror->show(); // show rounded corners with Plastik etc. 00248 else 00249 mSpinMirror->hide(); // keep normal shading with other styles 00250 arrange(); 00251 } 00252 00253 /****************************************************************************** 00254 * Called when the extra pair of spin buttons has repainted after a style change. 00255 * Updates the mirror image of the spin buttons. 00256 */ 00257 void SpinBox2::updateMirror() 00258 { 00259 mSpinMirror->setNormalButtons(TQPixmap::grabWidget(mUpdown2Frame, 0, 0)); 00260 } 00261 00262 /****************************************************************************** 00263 * Set the positions and sizes of all the child widgets. 00264 */ 00265 void SpinBox2::arrange() 00266 { 00267 getMetrics(); 00268 TQRect arrowRect = TQStyle::visualRect(TQRect(0, 0, wUpdown2, height()), this); 00269 mUpdown2Frame->setGeometry(arrowRect); 00270 mUpdown2->setGeometry(-xUpdown2, 0, mUpdown2->width(), height()); 00271 mSpinboxFrame->setGeometry(TQStyle::visualRect(TQRect(wUpdown2 + wGap, 0, width() - wUpdown2 - wGap, height()), this)); 00272 mSpinbox->setGeometry(-xSpinbox, 0, mSpinboxFrame->width() + xSpinbox, height()); 00273 mSpinMirror->resize(wUpdown2, mUpdown2->height()); 00274 mSpinMirror->setGeometry(arrowRect); 00275 //mSpinMirror->setGeometry(TQStyle::visualRect(TQRect(0, 11, wUpdown2, height()), this)); 00276 mSpinMirror->setNormalButtons(TQPixmap::grabWidget(mUpdown2Frame, 0, 0)); 00277 } 00278 00279 /****************************************************************************** 00280 * Calculate the width and position of the extra pair of spin buttons. 00281 * Style-specific adjustments are made for a better appearance. 00282 */ 00283 void SpinBox2::getMetrics() const 00284 { 00285 TQRect rect = mUpdown2->style().querySubControlMetrics(TQStyle::CC_SpinWidget, mUpdown2, TQStyle::SC_SpinWidgetButtonField); 00286 if (style().inherits("PlastikStyle")) 00287 rect.setLeft(rect.left() - 1); // Plastik excludes left border from spin widget rectangle 00288 xUpdown2 = mReverseLayout ? 0 : rect.left(); 00289 wUpdown2 = mUpdown2->width() - rect.left(); 00290 xSpinbox = mSpinbox->style().querySubControlMetrics(TQStyle::CC_SpinWidget, mSpinbox, TQStyle::SC_SpinWidgetEditField).left(); 00291 wGap = 0; 00292 00293 // Make style-specific adjustments for a better appearance 00294 if (style().inherits(TQMOTIFPLUSSTYLE_OBJECT_NAME_STRING)) 00295 { 00296 xSpinbox = 0; // show the edit control left border 00297 wGap = 2; // leave a space to the right of the left-hand pair of spin buttons 00298 } 00299 } 00300 00301 /****************************************************************************** 00302 * Called when the extra pair of spin buttons is clicked to step the value. 00303 * Normally this is a page step, but with a right-to-left language where the 00304 * button functions are reversed, this is a line step. 00305 */ 00306 void SpinBox2::stepPage(int step) 00307 { 00308 if (abs(step) == mUpdown2->lineStep()) 00309 mSpinbox->setValue(mUpdown2->value()); 00310 else 00311 { 00312 // It's a shift step 00313 int oldValue = mSpinbox->value(); 00314 if (!reverseButtons()) 00315 { 00316 // The button pairs have the normal function. 00317 // Page shift stepping - step up or down to a multiple of the 00318 // shift page increment, leaving unchanged the part of the value 00319 // which is the remainder from the page increment. 00320 if (oldValue >= 0) 00321 oldValue -= oldValue % mUpdown2->lineStep(); 00322 else 00323 oldValue += (-oldValue) % mUpdown2->lineStep(); 00324 } 00325 int adjust = mSpinbox->shiftStepAdjustment(oldValue, step); 00326 if (adjust == -step 00327 && ((step > 0 && oldValue + step >= mSpinbox->maxValue()) 00328 || (step < 0 && oldValue + step <= mSpinbox->minValue()))) 00329 adjust = 0; // allow stepping to the minimum or maximum value 00330 mSpinbox->addValue(adjust + step); 00331 } 00332 bool focus = mSpinbox->selectOnStep() && mUpdown2->hasFocus(); 00333 if (focus) 00334 mSpinbox->selectAll(); 00335 00336 // Make the covering arrows image show the pressed arrow 00337 mSpinMirror->redraw(TQPixmap::grabWidget(mUpdown2Frame, 0, 0)); 00338 } 00339 00340 00341 /*============================================================================= 00342 = Class SpinBox2::MainSpinBox 00343 =============================================================================*/ 00344 00345 /****************************************************************************** 00346 * Return the initial adjustment to the value for a shift step up or down, for 00347 * the main (visible) spin box. 00348 * Normally this is a line step, but with a right-to-left language where the 00349 * button functions are reversed, this is a page step. 00350 */ 00351 int SpinBox2::MainSpinBox::shiftStepAdjustment(int oldValue, int shiftStep) 00352 { 00353 if (owner->reverseButtons()) 00354 { 00355 // The button pairs have the opposite function from normal. 00356 // Page shift stepping - step up or down to a multiple of the 00357 // shift page increment, leaving unchanged the part of the value 00358 // which is the remainder from the page increment. 00359 if (oldValue >= 0) 00360 oldValue -= oldValue % lineStep(); 00361 else 00362 oldValue += (-oldValue) % lineStep(); 00363 } 00364 return SpinBox::shiftStepAdjustment(oldValue, shiftStep); 00365 } 00366 00367 00368 /*============================================================================= 00369 = Class ExtraSpinBox 00370 =============================================================================*/ 00371 00372 /****************************************************************************** 00373 * Repaint the widget. 00374 * If it's the first time since a style change, tell the parent SpinBox2 to 00375 * update the SpinMirror with the new unpressed button image. We make the 00376 * presumably reasonable assumption that when a style change occurs, the spin 00377 * buttons are unpressed. 00378 */ 00379 void ExtraSpinBox::paintEvent(TQPaintEvent* e) 00380 { 00381 SpinBox::paintEvent(e); 00382 if (mNewStylePending) 00383 { 00384 mNewStylePending = false; 00385 emit styleUpdated(); 00386 } 00387 } 00388 00389 00390 /*============================================================================= 00391 = Class SpinMirror 00392 =============================================================================*/ 00393 00394 SpinMirror::SpinMirror(SpinBox* spinbox, TQFrame* spinFrame, TQWidget* parent, const char* name) 00395 : TQCanvasView(new TQCanvas, parent, name), 00396 mSpinbox(spinbox), 00397 mSpinFrame(spinFrame), 00398 mReadOnly(false) 00399 { 00400 setVScrollBarMode(TQScrollView::AlwaysOff); 00401 setHScrollBarMode(TQScrollView::AlwaysOff); 00402 setFrameStyle(TQFrame::NoFrame); 00403 00404 // Find the spin widget which is part of the spin box, in order to 00405 // pass on its shift-button presses. 00406 TQObjectList* spinwidgets = spinbox->queryList(TQSPINWIDGET_OBJECT_NAME_STRING, 0, false, true); 00407 mSpinWidget = (SpinBox*)spinwidgets->getFirst(); 00408 delete spinwidgets; 00409 } 00410 00411 void SpinMirror::setNormalButtons(const TQPixmap& px) 00412 { 00413 mNormalButtons = px; 00414 redraw(mNormalButtons); 00415 } 00416 00417 void SpinMirror::redraw() 00418 { 00419 redraw(TQPixmap::grabWidget(mSpinFrame, 0, 0)); 00420 } 00421 00422 void SpinMirror::redraw(const TQPixmap& px) 00423 { 00424 TQCanvas* c = canvas(); 00425 c->setBackgroundPixmap(px); 00426 c->setAllChanged(); 00427 c->update(); 00428 } 00429 00430 void SpinMirror::resize(int w, int h) 00431 { 00432 canvas()->resize(w, h); 00433 TQCanvasView::resize(w, h); 00434 resizeContents(w, h); 00435 setWorldMatrix(TQWMatrix(-1, 0, 0, 1, w - 1, 0)); // mirror left to right 00436 } 00437 00438 /****************************************************************************** 00439 * Pass on all mouse events to the spinbox which we're covering up. 00440 */ 00441 void SpinMirror::contentsMouseEvent(TQMouseEvent* e) 00442 { 00443 if (mReadOnly) 00444 return; 00445 TQPoint pt = contentsToViewport(e->pos()); 00446 pt.setX(pt.x() + mSpinbox->upRect().left()); 00447 TQApplication::postEvent(mSpinWidget, new TQMouseEvent(e->type(), pt, e->button(), e->state())); 00448 00449 // If the mouse button has been pressed or released, refresh the spin buttons 00450 switch (e->type()) 00451 { 00452 case TQEvent::MouseButtonPress: 00453 case TQEvent::MouseButtonRelease: 00454 TQTimer::singleShot(0, this, TQT_SLOT(redraw())); 00455 break; 00456 default: 00457 break; 00458 } 00459 } 00460 00461 /****************************************************************************** 00462 * Pass on all mouse events to the spinbox which we're covering up. 00463 */ 00464 void SpinMirror::contentsWheelEvent(TQWheelEvent* e) 00465 { 00466 if (mReadOnly) 00467 return; 00468 TQPoint pt = contentsToViewport(e->pos()); 00469 pt.setX(pt.x() + mSpinbox->upRect().left()); 00470 TQApplication::postEvent(mSpinWidget, new TQWheelEvent(pt, e->delta(), e->state(), e->orientation())); 00471 } 00472 00473 /****************************************************************************** 00474 * Pass on to the main spinbox events which are needed to activate mouseover and 00475 * other graphic effects when the mouse cursor enters and leaves the widget. 00476 */ 00477 bool SpinMirror::event(TQEvent* e) 00478 { 00479 switch (e->type()) 00480 { 00481 case TQEvent::Leave: 00482 case TQEvent::Enter: 00483 TQApplication::postEvent(mSpinWidget, new TQEvent(e->type())); 00484 TQTimer::singleShot(0, this, TQT_SLOT(redraw())); 00485 break; 00486 case TQEvent::FocusIn: 00487 mSpinbox->setFocus(); 00488 TQTimer::singleShot(0, this, TQT_SLOT(redraw())); 00489 break; 00490 default: 00491 break; 00492 } 00493 return TQCanvasView::event(e); 00494 } 00495 00496 00497 /*============================================================================= 00498 = Local functions 00499 =============================================================================*/ 00500 00501 /****************************************************************************** 00502 * Determine whether the extra pair of spin buttons needs to be mirrored 00503 * left-to-right in the specified style. 00504 */ 00505 static bool mirrorStyle(const TQStyle& style) 00506 { 00507 for (const char** s = mirrorStyles; *s; ++s) 00508 if (style.inherits(*s)) 00509 return true; 00510 return false; 00511 }