kalarm/lib

timeperiod.cpp

00001 /*
00002  *  timeperiod.h  -  time period data entry widget
00003  *  Program:  kalarm
00004  *  Copyright (C) 2003, 2004 by David Jarvie <software@astrojar.org.uk>
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 "kalarm.h"
00022 
00023 #include <qwidgetstack.h>
00024 #include <qwhatsthis.h>
00025 
00026 #include <klocale.h>
00027 #include <kdialog.h>
00028 
00029 #include "combobox.h"
00030 #include "spinbox.h"
00031 #include "timespinbox.h"
00032 #include "timeperiod.moc"
00033 
00034 
00035 // Collect these widget labels together to ensure consistent wording and
00036 // translations across different modules.
00037 QString TimePeriod::i18n_hours_mins()   { return i18n("hours/minutes"); }
00038 QString TimePeriod::i18n_Hours_Mins()   { return i18n("Hours/Minutes"); }
00039 QString TimePeriod::i18n_days()         { return i18n("days"); }
00040 QString TimePeriod::i18n_Days()         { return i18n("Days"); }
00041 QString TimePeriod::i18n_weeks()        { return i18n("weeks"); }
00042 QString TimePeriod::i18n_Weeks()        { return i18n("Weeks"); }
00043 
00044 static const int maxMinutes = 100*60-1;   // absolute maximum value for hours:minutes = 99H59M
00045 
00046 /*=============================================================================
00047 = Class TimePeriod
00048 = Contains a time unit combo box, plus a time spinbox, to select a time period.
00049 =============================================================================*/
00050 
00051 TimePeriod::TimePeriod(bool allowHourMinute, QWidget* parent, const char* name)
00052     : QHBox(parent, name),
00053       mMaxDays(9999),
00054       mNoHourMinute(!allowHourMinute),
00055       mReadOnly(false)
00056 {
00057     setSpacing(KDialog::spacingHint());
00058 
00059     mSpinStack = new QWidgetStack(this);
00060     mSpinBox = new SpinBox(mSpinStack);
00061     mSpinBox->setLineStep(1);
00062     mSpinBox->setLineShiftStep(10);
00063     mSpinBox->setRange(1, mMaxDays);
00064     connect(mSpinBox, SIGNAL(valueChanged(int)), SLOT(slotDaysChanged(int)));
00065     mSpinStack->addWidget(mSpinBox, 0);
00066 
00067     mTimeSpinBox = new TimeSpinBox(0, 99999, mSpinStack);
00068     mTimeSpinBox->setRange(1, maxMinutes);    // max 99H59M
00069     connect(mTimeSpinBox, SIGNAL(valueChanged(int)), SLOT(slotTimeChanged(int)));
00070     mSpinStack->addWidget(mTimeSpinBox, 1);
00071 
00072     mSpinStack->setFixedSize(mSpinBox->sizeHint().expandedTo(mTimeSpinBox->sizeHint()));
00073     mHourMinuteRaised = mNoHourMinute;
00074     showHourMin(!mNoHourMinute);
00075 
00076     mUnitsCombo = new ComboBox(false, this);
00077     if (mNoHourMinute)
00078         mDateOnlyOffset = 1;
00079     else
00080     {
00081         mDateOnlyOffset = 0;
00082         mUnitsCombo->insertItem(i18n_hours_mins());
00083     }
00084     mUnitsCombo->insertItem(i18n_days());
00085     mUnitsCombo->insertItem(i18n_weeks());
00086     mMaxUnitShown = WEEKS;
00087     mUnitsCombo->setFixedSize(mUnitsCombo->sizeHint());
00088     connect(mUnitsCombo, SIGNAL(activated(int)), SLOT(slotUnitsSelected(int)));
00089 
00090     setFocusProxy(mUnitsCombo);
00091     setTabOrder(mUnitsCombo, mSpinStack);
00092 }
00093 
00094 void TimePeriod::setReadOnly(bool ro)
00095 {
00096     if (ro != mReadOnly)
00097     {
00098         mReadOnly = ro;
00099         mSpinBox->setReadOnly(ro);
00100         mTimeSpinBox->setReadOnly(ro);
00101         mUnitsCombo->setReadOnly(ro);
00102     }
00103 }
00104 
00105 /******************************************************************************
00106 *  Set whether the editor text is to be selected whenever spin buttons are
00107 *  clicked. Default is to select them.
00108 */
00109 void TimePeriod::setSelectOnStep(bool sel)
00110 {
00111     mSpinBox->setSelectOnStep(sel);
00112     mTimeSpinBox->setSelectOnStep(sel);
00113 }
00114 
00115 /******************************************************************************
00116 *  Set the input focus on the count field.
00117 */
00118 void TimePeriod::setFocusOnCount()
00119 {
00120     mSpinStack->setFocus();
00121 }
00122 
00123 /******************************************************************************
00124 *  Set the maximum values for the hours:minutes and days/weeks spinboxes.
00125 *  If 'hourmin' = 0, the hours:minutes maximum is left unchanged.
00126 */
00127 void TimePeriod::setMaximum(int hourmin, int days)
00128 {
00129     int oldmins = minutes();
00130     if (hourmin > 0)
00131     {
00132         if (hourmin > maxMinutes)
00133             hourmin = maxMinutes;
00134         mTimeSpinBox->setRange(1, hourmin);
00135     }
00136     mMaxDays = (days >= 0) ? days : 0;
00137     adjustDayWeekShown();
00138     setUnitRange();
00139     int mins = minutes();
00140     if (mins != oldmins)
00141         emit valueChanged(mins);
00142 }
00143 
00144 /******************************************************************************
00145  * Get the specified number of minutes.
00146  * Reply = 0 if error.
00147  */
00148 int TimePeriod::minutes() const
00149 {
00150     switch (mUnitsCombo->currentItem() + mDateOnlyOffset)
00151     {
00152         case HOURS_MINUTES:
00153             return mTimeSpinBox->value();
00154         case DAYS:
00155             return mSpinBox->value() * 24*60;
00156         case WEEKS:
00157             return mSpinBox->value() * 7*24*60;
00158     }
00159     return 0;
00160 }
00161 
00162 /******************************************************************************
00163 *  Initialise the controls with a specified time period.
00164 *  The time unit combo-box is initialised to 'defaultUnits', but if 'dateOnly'
00165 *  is true, it will never be initialised to hours/minutes.
00166 */
00167 void TimePeriod::setMinutes(int mins, bool dateOnly, TimePeriod::Units defaultUnits)
00168 {
00169     int oldmins = minutes();
00170     if (!dateOnly  &&  mNoHourMinute)
00171         dateOnly = true;
00172     int item;
00173     if (mins)
00174     {
00175         int count = mins;
00176         if (mins % (24*60))
00177             item = HOURS_MINUTES;
00178         else if (mins % (7*24*60))
00179         {
00180             item = DAYS;
00181             count = mins / (24*60);
00182         }
00183         else
00184         {
00185             item = WEEKS;
00186             count = mins / (7*24*60);
00187         }
00188         if (item < mDateOnlyOffset)
00189             item = mDateOnlyOffset;
00190         else if (item > mMaxUnitShown)
00191             item = mMaxUnitShown;
00192         mUnitsCombo->setCurrentItem(item - mDateOnlyOffset);
00193         if (item == HOURS_MINUTES)
00194             mTimeSpinBox->setValue(count);
00195         else
00196             mSpinBox->setValue(count);
00197         item = setDateOnly(mins, dateOnly, false);
00198     }
00199     else
00200     {
00201         item = defaultUnits;
00202         if (item < mDateOnlyOffset)
00203             item = mDateOnlyOffset;
00204         else if (item > mMaxUnitShown)
00205             item = mMaxUnitShown;
00206         mUnitsCombo->setCurrentItem(item - mDateOnlyOffset);
00207         if (dateOnly && !mDateOnlyOffset  ||  !dateOnly && mDateOnlyOffset)
00208             item = setDateOnly(mins, dateOnly, false);
00209     }
00210     showHourMin(item == HOURS_MINUTES  &&  !mNoHourMinute);
00211 
00212     int newmins = minutes();
00213     if (newmins != oldmins)
00214         emit valueChanged(newmins);
00215 }
00216 
00217 /******************************************************************************
00218 *  Enable/disable hours/minutes units (if hours/minutes were permitted in the
00219 *  constructor).
00220 */
00221 TimePeriod::Units TimePeriod::setDateOnly(int mins, bool dateOnly, bool signal)
00222 {
00223     int oldmins = 0;
00224     if (signal)
00225         oldmins = minutes();
00226     int index = mUnitsCombo->currentItem();
00227     Units units = static_cast<Units>(index + mDateOnlyOffset);
00228     if (!mNoHourMinute)
00229     {
00230         if (!dateOnly  &&  mDateOnlyOffset)
00231         {
00232             // Change from date-only to allow hours/minutes
00233             mUnitsCombo->insertItem(i18n_hours_mins(), 0);
00234             mDateOnlyOffset = 0;
00235             adjustDayWeekShown();
00236             mUnitsCombo->setCurrentItem(++index);
00237         }
00238         else if (dateOnly  &&  !mDateOnlyOffset)
00239         {
00240             // Change from allowing hours/minutes to date-only
00241             mUnitsCombo->removeItem(0);
00242             mDateOnlyOffset = 1;
00243             if (index)
00244                 --index;
00245             adjustDayWeekShown();
00246             mUnitsCombo->setCurrentItem(index);
00247             if (units == HOURS_MINUTES)
00248             {
00249                 // Set units to days and round up the warning period
00250                 units = DAYS;
00251                 mUnitsCombo->setCurrentItem(DAYS - mDateOnlyOffset);
00252                 mSpinBox->setValue((mins + 1439) / 1440);
00253             }
00254             showHourMin(false);
00255         }
00256     }
00257 
00258     if (signal)
00259     {
00260         int newmins = minutes();
00261         if (newmins != oldmins)
00262             emit valueChanged(newmins);
00263     }
00264     return units;
00265 }
00266 
00267 /******************************************************************************
00268 *  Adjust the days/weeks units shown to suit the maximum days limit.
00269 */
00270 void TimePeriod::adjustDayWeekShown()
00271 {
00272     Units newMaxUnitShown = (mMaxDays >= 7) ? WEEKS : (mMaxDays || mDateOnlyOffset) ? DAYS : HOURS_MINUTES;
00273     if (newMaxUnitShown > mMaxUnitShown)
00274     {
00275         if (mMaxUnitShown < DAYS)
00276             mUnitsCombo->insertItem(i18n_days());
00277         if (newMaxUnitShown == WEEKS)
00278             mUnitsCombo->insertItem(i18n_weeks());
00279     }
00280     else if (newMaxUnitShown < mMaxUnitShown)
00281     {
00282         if (mMaxUnitShown == WEEKS)
00283             mUnitsCombo->removeItem(WEEKS - mDateOnlyOffset);
00284         if (newMaxUnitShown < DAYS)
00285             mUnitsCombo->removeItem(DAYS - mDateOnlyOffset);
00286     }
00287     mMaxUnitShown = newMaxUnitShown;
00288 }
00289 
00290 /******************************************************************************
00291 *  Set the maximum value which may be entered into the day/week count field,
00292 *  depending on the current unit selection.
00293 */
00294 void TimePeriod::setUnitRange()
00295 {
00296     int maxval;
00297     switch (static_cast<Units>(mUnitsCombo->currentItem() + mDateOnlyOffset))
00298     {
00299         case WEEKS:
00300             maxval = mMaxDays / 7;
00301             if (maxval)
00302                 break;
00303             mUnitsCombo->setCurrentItem(DAYS - mDateOnlyOffset);
00304             // fall through to DAYS
00305         case DAYS:
00306             maxval = mMaxDays ? mMaxDays : 1;
00307             break;
00308         case HOURS_MINUTES:
00309         default:
00310             return;
00311     }
00312     mSpinBox->setRange(1, maxval);
00313 }
00314 
00315 /******************************************************************************
00316 *  Called when a new item is made current in the time units combo box.
00317 */
00318 void TimePeriod::slotUnitsSelected(int index)
00319 {
00320     setUnitRange();
00321     showHourMin(index + mDateOnlyOffset == HOURS_MINUTES);
00322     emit valueChanged(minutes());
00323 }
00324 
00325 /******************************************************************************
00326 *  Called when the value of the days/weeks spin box changes.
00327 */
00328 void TimePeriod::slotDaysChanged(int)
00329 {
00330     if (!mHourMinuteRaised)
00331         emit valueChanged(minutes());
00332 }
00333 
00334 /******************************************************************************
00335 *  Called when the value of the time spin box changes.
00336 */
00337 void TimePeriod::slotTimeChanged(int value)
00338 {
00339     if (mHourMinuteRaised)
00340         emit valueChanged(value);
00341 }
00342 
00343 /******************************************************************************
00344  * Set the currently displayed count widget.
00345  */
00346 void TimePeriod::showHourMin(bool hourMinute)
00347 {
00348     if (hourMinute != mHourMinuteRaised)
00349     {
00350         mHourMinuteRaised = hourMinute;
00351         if (hourMinute)
00352         {
00353             mSpinStack->raiseWidget(mTimeSpinBox);
00354             mSpinStack->setFocusProxy(mTimeSpinBox);
00355         }
00356         else
00357         {
00358             mSpinStack->raiseWidget(mSpinBox);
00359             mSpinStack->setFocusProxy(mSpinBox);
00360         }
00361     }
00362 }
00363 
00364 /******************************************************************************
00365  * Set separate WhatsThis texts for the count spinboxes and the units combobox.
00366  * If the hours:minutes text is omitted, both spinboxes are set to the same
00367  * WhatsThis text.
00368  */
00369 void TimePeriod::setWhatsThis(const QString& units, const QString& dayWeek, const QString& hourMin)
00370 {
00371     QWhatsThis::add(mUnitsCombo, units);
00372     QWhatsThis::add(mSpinBox, dayWeek);
00373     QWhatsThis::add(mTimeSpinBox, (hourMin.isNull() ? dayWeek : hourMin));
00374 }
KDE Home | KDE Accessibility Home | Description of Access Keys