• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdepim
  • Sitemap
  • Contact Us
 

kalarm/lib

spinbox.cpp

Go to the documentation of this file.
00001 /*
00002  *  spinbox.cpp  -  spin box with read-only option and shift-click step value
00003  *  Program:  kalarm
00004  *  Copyright © 2002,2004-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 <kdeversion.h>
00022 #include <kdebug.h>
00023 
00024 #include <QLineEdit>
00025 #include <QObject>
00026 #include <QApplication>
00027 #include <QStyle>
00028 #include <QStyleOptionSpinBox>
00029 #include <QPainter>
00030 #include <QMouseEvent>
00031 #include <QKeyEvent>
00032 #include <QEvent>
00033 
00034 #include "spinbox.moc"
00035 
00036 
00037 SpinBox::SpinBox(QWidget* parent)
00038     : QSpinBox(parent),
00039       mMinValue(QSpinBox::minimum()),
00040       mMaxValue(QSpinBox::maximum())
00041 {
00042     setRange(0, 99999);
00043     init();
00044 }
00045 
00046 SpinBox::SpinBox(int minValue, int maxValue, QWidget* parent)
00047     : QSpinBox(parent),
00048       mMinValue(minValue),
00049       mMaxValue(maxValue)
00050 {
00051     setRange(minValue, maxValue);
00052     init();
00053 }
00054 
00055 void SpinBox::init()
00056 {
00057     int step = QSpinBox::singleStep();
00058     mLineStep        = step;
00059     mLineShiftStep   = step;
00060     mCurrentButton   = NO_BUTTON;
00061     mShiftMouse      = false;
00062     mShiftMinBound   = false;
00063     mShiftMaxBound   = false;
00064     mSelectOnStep    = true;
00065     mUpDownOnly      = false;
00066     mReadOnly        = false;
00067     mSuppressSignals = false;
00068     mEdited          = false;
00069 
00070     lineEdit()->installEventFilter(this);   // handle shift-up/down arrow presses
00071 
00072     // Detect when the text field is edited
00073     connect(lineEdit(), SIGNAL(textChanged(const QString&)), SLOT(textEdited()));
00074     connect(this, SIGNAL(valueChanged(int)), SLOT(valueChange()));
00075 }
00076 
00077 void SpinBox::setReadOnly(bool ro)
00078 {
00079     if ((int)ro != (int)mReadOnly)
00080     {
00081         mReadOnly = ro;
00082         lineEdit()->setReadOnly(ro);
00083         if (ro)
00084             setShiftStepping(false);
00085     }
00086 }
00087 
00088 int SpinBox::bound(int val) const
00089 {
00090     return (val < mMinValue) ? mMinValue : (val > mMaxValue) ? mMaxValue : val;
00091 }
00092 
00093 void SpinBox::setMinimum(int val)
00094 {
00095     mMinValue = val;
00096     QSpinBox::setMinimum(val);
00097     mShiftMinBound = false;
00098 }
00099 
00100 void SpinBox::setMaximum(int val)
00101 {
00102     mMaxValue = val;
00103     QSpinBox::setMaximum(val);
00104     mShiftMaxBound = false;
00105 }
00106 
00107 void SpinBox::setSingleStep(int step)
00108 {
00109     mLineStep = step;
00110     if (!mShiftMouse)
00111         QSpinBox::setSingleStep(step);
00112 }
00113 
00114 void SpinBox::setSingleShiftStep(int step)
00115 {
00116     mLineShiftStep = step;
00117     if (mShiftMouse)
00118         QSpinBox::setSingleStep(step);
00119 }
00120 
00121 void SpinBox::stepBy(int steps)
00122 {
00123     int increment = steps * QSpinBox::singleStep();
00124     addValue(increment);
00125     emit stepped(increment);
00126 }
00127 
00128 /******************************************************************************
00129 * Adds a positive or negative increment to the current value, wrapping as appropriate.
00130 * If 'current' is true, any temporary 'shift' values for the range are used instead
00131 * of the real maximum and minimum values.
00132 */
00133 void SpinBox::addValue(int change, bool current)
00134 {
00135     int newval = value() + change;
00136     int maxval = current ? QSpinBox::maximum() : mMaxValue;
00137     int minval = current ? QSpinBox::minimum() : mMinValue;
00138     if (wrapping())
00139     {
00140         int range = maxval - minval + 1;
00141         if (newval > maxval)
00142             newval = minval + (newval - maxval - 1) % range;
00143         else if (newval < minval)
00144             newval = maxval - (minval - 1 - newval) % range;
00145     }
00146     else
00147     {
00148         if (newval > maxval)
00149             newval = maxval;
00150         else if (newval < minval)
00151             newval = minval;
00152     }
00153     setValue(newval);
00154 }
00155 
00156 void SpinBox::valueChange()
00157 {
00158     if (!mSuppressSignals)
00159     {
00160         int val = value();
00161         if (mShiftMinBound  &&  val >= mMinValue)
00162         {
00163             // Reinstate the minimum bound now that the value has returned to the normal range.
00164             QSpinBox::setMinimum(mMinValue);
00165             mShiftMinBound = false;
00166         }
00167         if (mShiftMaxBound  &&  val <= mMaxValue)
00168         {
00169             // Reinstate the maximum bound now that the value has returned to the normal range.
00170             QSpinBox::setMaximum(mMaxValue);
00171             mShiftMaxBound = false;
00172         }
00173 
00174         if (!mSelectOnStep && hasFocus())
00175             lineEdit()->deselect();   // prevent selection of the spin box text
00176     }
00177 }
00178 
00179 /******************************************************************************
00180 * Called whenever the line edit text is changed.
00181 */
00182 void SpinBox::textEdited()
00183 {
00184     mEdited = true;
00185 }
00186 
00187 /******************************************************************************
00188 * Receives events destined for the spin widget or for the edit field.
00189 */
00190 bool SpinBox::eventFilter(QObject* obj, QEvent* e)
00191 {
00192     if (obj == lineEdit())
00193     {
00194         if (e->type() == QEvent::KeyPress)
00195         {
00196             // Up and down arrow keys step the value
00197             QKeyEvent* ke = (QKeyEvent*)e;
00198             int key = ke->key();
00199             if (key == Qt::Key_Up  ||  key == Qt::Key_Down)
00200             {
00201                 if (mReadOnly)
00202                     return true;    // discard up/down arrow keys
00203                 int step;
00204                 if ((ke->modifiers() & (Qt::ShiftModifier | Qt::AltModifier)) == Qt::ShiftModifier)
00205                 {
00206                     // Shift stepping
00207                     int val = value();
00208                     if (key == Qt::Key_Up)
00209                         step = mLineShiftStep - val % mLineShiftStep;
00210                     else
00211                         step = - ((val + mLineShiftStep - 1) % mLineShiftStep + 1);
00212                 }
00213                 else
00214                     step = (key == Qt::Key_Up) ? mLineStep : -mLineStep;
00215                 addValue(step, false);
00216                 return true;
00217             }
00218         }
00219     }
00220     return QSpinBox::eventFilter(obj, e);
00221 }
00222 
00223 void SpinBox::focusOutEvent(QFocusEvent* e)
00224 {
00225     if (mEdited)
00226     {
00227         interpretText();
00228         mEdited = false;
00229     }
00230     QSpinBox::focusOutEvent(e);
00231 }
00232 
00233 void SpinBox::mousePressEvent(QMouseEvent* e)
00234 {
00235     if (!clickEvent(e))
00236         QSpinBox::mousePressEvent(e);
00237 }
00238 
00239 void SpinBox::mouseDoubleClickEvent(QMouseEvent* e)
00240 {
00241     if (!clickEvent(e))
00242         QSpinBox::mouseDoubleClickEvent(e);
00243 }
00244 
00245 bool SpinBox::clickEvent(QMouseEvent* e)
00246 {
00247     if (e->button() == Qt::LeftButton)
00248     {
00249         // It's a left button press. Set normal or shift stepping as appropriate.
00250         if (mReadOnly)
00251             return true;   // discard the event
00252         mCurrentButton = whichButton(e->pos());
00253         if (mCurrentButton == NO_BUTTON)
00254         {
00255             e->accept();
00256             return true;
00257         }
00258         bool shift = (e->modifiers() & (Qt::ShiftModifier | Qt::AltModifier)) == Qt::ShiftModifier;
00259         if (setShiftStepping(shift))
00260         {
00261             e->accept();
00262             return true;     // hide the event from the spin widget
00263         }
00264     }
00265     return false;
00266 }
00267 
00268 void SpinBox::mouseReleaseEvent(QMouseEvent* e)
00269 {
00270     if (e->button() == Qt::LeftButton  &&  mShiftMouse)
00271         setShiftStepping(false);    // cancel shift stepping
00272     QSpinBox::mouseReleaseEvent(e);
00273 }
00274 
00275 void SpinBox::mouseMoveEvent(QMouseEvent* e)
00276 {
00277     if (e->buttons() & Qt::LeftButton)
00278     {
00279         // The left button is down. Track which spin button it's in.
00280         if (mReadOnly)
00281             return;   // discard the event
00282         int newButton = whichButton(e->pos());
00283         if (newButton != mCurrentButton)
00284         {
00285             // The mouse has moved to a new spin button.
00286             // Set normal or shift stepping as appropriate.
00287             mCurrentButton = newButton;
00288             bool shift = (e->modifiers() & (Qt::ShiftModifier | Qt::AltModifier)) == Qt::ShiftModifier;
00289             if (setShiftStepping(shift))
00290             {
00291                 e->accept();
00292                 return;     // hide the event from the spin widget
00293             }
00294         }
00295     }
00296     QSpinBox::mouseMoveEvent(e);
00297 }
00298 
00299 void SpinBox::keyPressEvent(QKeyEvent* e)
00300 {
00301     if (!keyEvent(e))
00302         QSpinBox::keyPressEvent(e);
00303 }
00304 
00305 void SpinBox::keyReleaseEvent(QKeyEvent* e)
00306 {
00307     if (!keyEvent(e))
00308         QSpinBox::keyReleaseEvent(e);
00309 }
00310 
00311 bool SpinBox::keyEvent(QKeyEvent* e)
00312 {
00313     int key   = e->key();
00314     int state = e->modifiers();
00315     if ((QApplication::mouseButtons() & Qt::LeftButton)
00316     &&  (key == Qt::Key_Shift  ||  key == Qt::Key_Alt))
00317     {
00318         // The left mouse button is down, and the Shift or Alt key has changed
00319         if (mReadOnly)
00320             return true;   // discard the event
00321         bool shift = (state & (Qt::ShiftModifier | Qt::AltModifier)) == Qt::ShiftModifier;
00322         if ((!shift && mShiftMouse)  ||  (shift && !mShiftMouse))
00323         {
00324             // The effective shift state has changed.
00325             // Set normal or shift stepping as appropriate.
00326             if (setShiftStepping(shift))
00327             {
00328                 e->accept();
00329                 return true;     // hide the event from the spin widget
00330             }
00331         }
00332     }
00333     return false;
00334 }
00335 
00336 /******************************************************************************
00337 * Set spin widget stepping to the normal or shift increment.
00338 */
00339 bool SpinBox::setShiftStepping(bool shift)
00340 {
00341     if (mCurrentButton == NO_BUTTON)
00342         shift = false;
00343     if (shift  &&  !mShiftMouse)
00344     {
00345         /* The value is to be stepped to a multiple of the shift increment.
00346          * Adjust the value so that after the spin widget steps it, it will be correct.
00347          * Then, if the mouse button is held down, the spin widget will continue to
00348          * step by the shift amount.
00349          */
00350         int val = value();
00351         int step = (mCurrentButton == UP) ? mLineShiftStep : (mCurrentButton == DOWN) ? -mLineShiftStep : 0;
00352         int adjust = shiftStepAdjustment(val, step);
00353         mShiftMouse = true;
00354         if (adjust)
00355         {
00356             /* The value is to be stepped by other than the shift increment,
00357              * presumably because it is being set to a multiple of the shift
00358              * increment. Achieve this by making the adjustment here, and then
00359              * allowing the normal step processing to complete the job by
00360              * adding/subtracting the normal shift increment.
00361              */
00362             if (!wrapping())
00363             {
00364                 // Prevent the step from going past the spinbox's range, or
00365                 // to the minimum value if that has a special text unless it is
00366                 // already at the minimum value + 1.
00367                 int newval = val + adjust + step;
00368                 int svt = specialValueText().isEmpty() ? 0 : 1;
00369                 int minval = mMinValue + svt;
00370                 if (newval <= minval  ||  newval >= mMaxValue)
00371                 {
00372                     // Stepping to the minimum or maximum value
00373                     if (svt  &&  newval <= mMinValue  &&  val == mMinValue)
00374                         newval = mMinValue;
00375                     else
00376                         newval = (newval <= minval) ? minval : mMaxValue;
00377                     QSpinBox::setValue(newval);
00378                     emit stepped(step);
00379                     return true;
00380                 }
00381 
00382                 // If the interim value will lie outside the spinbox's range,
00383                 // temporarily adjust the range to allow the value to be set.
00384                 int tempval = val + adjust;
00385                 if (tempval < mMinValue)
00386                 {
00387                     QSpinBox::setMinimum(tempval);
00388                     mShiftMinBound = true;
00389                 }
00390                 else if (tempval > mMaxValue)
00391                 {
00392                     QSpinBox::setMaximum(tempval);
00393                     mShiftMaxBound = true;
00394                 }
00395             }
00396 
00397             // Don't process changes since this new value will be stepped immediately
00398             mSuppressSignals = true;
00399             bool blocked = signalsBlocked();
00400             blockSignals(true);
00401             addValue(adjust, true);
00402             blockSignals(blocked);
00403             mSuppressSignals = false;
00404         }
00405         QSpinBox::setSingleStep(mLineShiftStep);
00406     }
00407     else if (!shift  &&  mShiftMouse)
00408     {
00409         // Reinstate to normal (non-shift) stepping
00410         QSpinBox::setSingleStep(mLineStep);
00411         QSpinBox::setMinimum(mMinValue);
00412         QSpinBox::setMaximum(mMaxValue);
00413         mShiftMinBound = mShiftMaxBound = false;
00414         mShiftMouse = false;
00415     }
00416     return false;
00417 }
00418 
00419 /******************************************************************************
00420 * Return the initial adjustment to the value for a shift step up or down.
00421 * The default is to step up or down to the nearest multiple of the shift
00422 * increment, so the adjustment returned is for stepping up the decrement
00423 * required to round down to a multiple of the shift increment <= current value,
00424 * or for stepping down the increment required to round up to a multiple of the
00425 * shift increment >= current value.
00426 * This method's caller then adjusts the resultant value if necessary to cater
00427 * for the widget's minimum/maximum value, and wrapping.
00428 * This should really be a static method, but it needs to be virtual...
00429 */
00430 int SpinBox::shiftStepAdjustment(int oldValue, int shiftStep)
00431 {
00432     if (oldValue == 0  ||  shiftStep == 0)
00433         return 0;
00434     if (shiftStep > 0)
00435     {
00436         if (oldValue >= 0)
00437             return -(oldValue % shiftStep);
00438         else
00439             return (-oldValue - 1) % shiftStep + 1 - shiftStep;
00440     }
00441     else
00442     {
00443         shiftStep = -shiftStep;
00444         if (oldValue >= 0)
00445             return shiftStep - ((oldValue - 1) % shiftStep + 1);
00446         else
00447             return (-oldValue) % shiftStep;
00448     }
00449 }
00450 
00451 /******************************************************************************
00452 *  Find which spin widget button a mouse event is in.
00453 */
00454 int SpinBox::whichButton(const QPoint& pos)
00455 {
00456     QStyleOptionSpinBox option;
00457     initStyleOption(option);
00458     if (style()->subControlRect(QStyle::CC_SpinBox, &option, QStyle::SC_SpinBoxUp).contains(pos))
00459         return UP;
00460     if (style()->subControlRect(QStyle::CC_SpinBox, &option, QStyle::SC_SpinBoxDown).contains(pos))
00461         return DOWN;
00462     return NO_BUTTON;
00463 }
00464 
00465 QRect SpinBox::upRect() const
00466 {
00467     QStyleOptionSpinBox option;
00468     initStyleOption(option);
00469     return style()->subControlRect(QStyle::CC_SpinBox, &option, QStyle::SC_SpinBoxUp);
00470 }
00471 
00472 QRect SpinBox::downRect() const
00473 {
00474     QStyleOptionSpinBox option;
00475     initStyleOption(option);
00476     return style()->subControlRect(QStyle::CC_SpinBox, &option, QStyle::SC_SpinBoxDown);
00477 }
00478 
00479 QRect SpinBox::upDownRect() const
00480 {
00481     QStyleOptionSpinBox option;
00482     initStyleOption(option);
00483     return style()->subControlRect(QStyle::CC_SpinBox, &option, QStyle::SC_SpinBoxUp)
00484          | style()->subControlRect(QStyle::CC_SpinBox, &option, QStyle::SC_SpinBoxDown);
00485 }
00486 
00487 void SpinBox::paintEvent(QPaintEvent* pe)
00488 {
00489     if (mUpDownOnly)
00490     {
00491         QStyleOptionSpinBox option;
00492         initStyleOption(option);
00493         QPainter painter(this);
00494         style()->drawComplexControl(QStyle::CC_SpinBox, &option, &painter, this);
00495     }
00496     else
00497         QSpinBox::paintEvent(pe);
00498 }
00499 
00500 void SpinBox::initStyleOption(QStyleOptionSpinBox& so) const
00501 {
00502     so.init(this);
00503 //  so.activeSubControls = ??;
00504     so.subControls   = mUpDownOnly ? QStyle::SC_SpinBoxUp | QStyle::SC_SpinBoxDown | QStyle::SC_SpinBoxFrame
00505                                    : QStyle::SC_SpinBoxUp | QStyle::SC_SpinBoxDown | QStyle::SC_SpinBoxFrame | QStyle::SC_SpinBoxEditField;
00506     so.buttonSymbols = buttonSymbols();
00507     so.frame         = hasFrame();
00508     so.stepEnabled   = stepEnabled();
00509 }

kalarm/lib

Skip menu "kalarm/lib"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members

kdepim

Skip menu "kdepim"
  • akonadi
  •   clients
  •   kabc
  •   kcal
  •   kcm
  • akregator
  • console
  •   kabcclient
  •   konsolekalendar
  • kaddressbook
  • kalarm
  •   lib
  • kdgantt
  • kdgantt1
  • kjots
  • kleopatra
  • kmail
  • kmobiletools
  • knode
  • knotes
  • kontact
  • kontactinterfaces
  • korganizer
  •   korgac
  • kpilot
  • ktimetracker
  • libkdepim
  • libkholidays
  • libkleo
  • libkpgp
  • maildir
Generated for kdepim by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal