Waste less space in nested layouts.
[kdepim.git] / kalarm / repetitionbutton.cpp
blob610bd9af5941e4bb4ec3af3609975d5244e9eb9f
1 /*
2 * repetitionbutton.cpp - pushbutton and dialog to specify alarm repetition
3 * Program: kalarm
4 * Copyright © 2004-2009 by David Jarvie <djarvie@kde.org>
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.
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.
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.
21 #include "kalarm.h"
22 #include "repetitionbutton.moc"
24 #include "buttongroup.h"
25 #include "radiobutton.h"
26 #include "spinbox.h"
27 #include "timeperiod.h"
28 #include "timeselector.h"
30 #include <kdialog.h>
31 #include <klocale.h>
33 #include <QGroupBox>
34 #include <QVBoxLayout>
35 #include <QHBoxLayout>
37 using namespace KCal;
40 /*=============================================================================
41 = Class RepetitionButton
42 = Button to display the Simple Alarm Repetition dialog.
43 =============================================================================*/
45 RepetitionButton::RepetitionButton(const QString& caption, bool waitForInitialisation, QWidget* parent)
46 : QPushButton(caption, parent),
47 mDialog(0),
48 mMaxDuration(-1),
49 mDateOnly(false),
50 mWaitForInit(waitForInitialisation),
51 mReadOnly(false)
53 setCheckable(true);
54 setChecked(false);
55 connect(this, SIGNAL(clicked()), SLOT(slotPressed()));
58 void RepetitionButton::set(const Repetition& repetition)
60 mRepetition = repetition;
61 setChecked(mRepetition);
64 /******************************************************************************
65 * Set the data for the dialog.
67 void RepetitionButton::set(const Repetition& repetition, bool dateOnly, int maxDuration)
69 mRepetition = repetition;
70 mMaxDuration = maxDuration;
71 mDateOnly = dateOnly;
72 setChecked(mRepetition);
75 /******************************************************************************
76 * Create the alarm repetition dialog.
77 * If 'waitForInitialisation' is true, the dialog won't be displayed until set()
78 * is called to initialise its data.
80 void RepetitionButton::activate(bool waitForInitialisation)
82 if (!mDialog)
83 mDialog = new RepetitionDlg(i18nc("@title:window", "Alarm Sub-Repetition"), mReadOnly, this);
84 mDialog->set(mRepetition, mDateOnly, mMaxDuration);
85 if (waitForInitialisation)
86 emit needsInitialisation(); // request dialog initialisation
87 else
88 displayDialog(); // display the dialog now
91 /******************************************************************************
92 * Set the data for the dialog and display it.
93 * To be called only after needsInitialisation() has been emitted.
95 void RepetitionButton::initialise(const Repetition& repetition, bool dateOnly, int maxDuration)
97 mRepetition = (maxDuration > 0 && repetition.intervalMinutes() > maxDuration)
98 ? Repetition() : repetition;
99 mMaxDuration = maxDuration;
100 mDateOnly = dateOnly;
101 if (mDialog)
103 mDialog->set(mRepetition, dateOnly, maxDuration);
104 displayDialog(); // display the dialog now
106 else
107 setChecked(mRepetition);
110 /******************************************************************************
111 * Display the alarm sub-repetition dialog.
112 * Alarm repetition has the following restrictions:
113 * 1) Not allowed for a repeat-at-login alarm
114 * 2) For a date-only alarm, the repeat interval must be a whole number of days.
115 * 3) The overall repeat duration must be less than the recurrence interval.
117 void RepetitionButton::displayDialog()
119 bool change = false;
120 if (mReadOnly)
122 mDialog->setReadOnly(true);
123 mDialog->exec();
125 else if (mDialog->exec() == QDialog::Accepted)
127 mRepetition = mDialog->repetition();
128 change = true;
130 setChecked(mRepetition);
131 delete mDialog;
132 mDialog = 0;
133 if (change)
134 emit changed(); // delete dialog first, or initialise() will redisplay dialog
138 /*=============================================================================
139 = Class RepetitionDlg
140 = Simple alarm repetition dialog.
141 =============================================================================*/
143 static const int MAX_COUNT = 9999; // maximum range for count spinbox
146 RepetitionDlg::RepetitionDlg(const QString& caption, bool readOnly, QWidget* parent)
147 : KDialog(parent),
148 mMaxDuration(-1),
149 mDateOnly(false),
150 mReadOnly(readOnly)
152 setCaption(caption);
153 setButtons(Ok|Cancel);
154 int spacing = spacingHint();
155 QWidget* page = new QWidget(this);
156 setMainWidget(page);
157 QVBoxLayout* topLayout = new QVBoxLayout(page);
158 topLayout->setMargin(0);
159 topLayout->setSpacing(spacing);
161 mTimeSelector = new TimeSelector(i18nc("@option:check Repeat every 10 minutes", "Repeat every"), QString(),
162 i18nc("@info:whatsthis", "Instead of the alarm triggering just once at each recurrence, "
163 "checking this option makes the alarm trigger multiple times at each recurrence."),
164 i18nc("@info:whatsthis", "Enter the time between repetitions of the alarm"),
165 true, page);
166 mTimeSelector->setFixedSize(mTimeSelector->sizeHint());
167 connect(mTimeSelector, SIGNAL(valueChanged(const KCal::Duration&)), SLOT(intervalChanged(const KCal::Duration&)));
168 connect(mTimeSelector, SIGNAL(toggled(bool)), SLOT(repetitionToggled(bool)));
169 topLayout->addWidget(mTimeSelector, 0, Qt::AlignLeft);
171 mButtonBox = new QGroupBox(page);
172 topLayout->addWidget(mButtonBox);
173 mButtonGroup = new ButtonGroup(mButtonBox);
174 connect(mButtonGroup, SIGNAL(buttonSet(QAbstractButton*)), SLOT(typeClicked()));
176 QVBoxLayout* vlayout = new QVBoxLayout(mButtonBox);
177 vlayout->setMargin(marginHint());
178 vlayout->setSpacing(spacing);
179 QHBoxLayout* layout = new QHBoxLayout();
180 layout->setMargin(0);
181 vlayout->addLayout(layout);
182 mCountButton = new RadioButton(i18nc("@option:radio", "Number of repetitions:"), mButtonBox);
183 mCountButton->setFixedSize(mCountButton->sizeHint());
184 mCountButton->setWhatsThis(i18nc("@info:whatsthis", "Check to specify the number of times the alarm should repeat after each recurrence"));
185 mButtonGroup->addButton(mCountButton);
186 layout->addWidget(mCountButton);
187 mCount = new SpinBox(1, MAX_COUNT, mButtonBox);
188 mCount->setFixedSize(mCount->sizeHint());
189 mCount->setSingleShiftStep(10);
190 mCount->setSelectOnStep(false);
191 connect(mCount, SIGNAL(valueChanged(int)), SLOT(countChanged(int)));
192 mCount->setWhatsThis(i18nc("@info:whatsthis", "Enter the number of times to trigger the alarm after its initial occurrence"));
193 layout->addWidget(mCount);
194 mCountButton->setFocusWidget(mCount);
195 layout->addStretch();
197 layout = new QHBoxLayout();
198 layout->setMargin(0);
199 vlayout->addLayout(layout);
200 mDurationButton = new RadioButton(i18nc("@option:radio", "Duration:"), mButtonBox);
201 mDurationButton->setFixedSize(mDurationButton->sizeHint());
202 mDurationButton->setWhatsThis(i18nc("@info:whatsthis", "Check to specify how long the alarm is to be repeated"));
203 mButtonGroup->addButton(mDurationButton);
204 layout->addWidget(mDurationButton);
205 mDuration = new TimePeriod(true, mButtonBox);
206 mDuration->setFixedSize(mDuration->sizeHint());
207 connect(mDuration, SIGNAL(valueChanged(const KCal::Duration&)), SLOT(durationChanged(const KCal::Duration&)));
208 mDuration->setWhatsThis(i18nc("@info:whatsthis", "Enter the length of time to repeat the alarm"));
209 layout->addWidget(mDuration);
210 mDurationButton->setFocusWidget(mDuration);
211 layout->addStretch();
213 mCountButton->setChecked(true);
214 repetitionToggled(false);
215 setReadOnly(mReadOnly);
218 /******************************************************************************
219 * Set the state of all controls to reflect the data in the specified alarm.
221 void RepetitionDlg::set(const Repetition& repetition, bool dateOnly, int maxDuration)
223 if (dateOnly != mDateOnly)
225 mDateOnly = dateOnly;
226 mTimeSelector->setDateOnly(mDateOnly);
227 mDuration->setDateOnly(mDateOnly);
229 mMaxDuration = maxDuration;
230 if (mMaxDuration)
232 int maxhm = (mMaxDuration > 0) ? mMaxDuration : 9999;
233 int maxdw = (mMaxDuration > 0) ? mMaxDuration / 1440 : 9999;
234 mTimeSelector->setMaximum(maxhm, maxdw);
235 mDuration->setMaximum(maxhm, maxdw);
237 // Set the units - needed later if the control is unchecked initially.
238 TimePeriod::Units units = mDateOnly ? TimePeriod::Days : TimePeriod::HoursMinutes;
239 mTimeSelector->setPeriod(repetition.interval(), mDateOnly, units);
240 if (!mMaxDuration || !repetition)
241 mTimeSelector->setChecked(false);
242 else
244 bool on = mTimeSelector->isChecked();
245 repetitionToggled(on); // enable/disable controls
246 if (on)
247 intervalChanged(repetition.interval()); // ensure mCount range is set
248 mCount->setValue(repetition.count());
249 mDuration->setPeriod(repetition.duration(), mDateOnly, units);
250 mCountButton->setChecked(true);
252 mTimeSelector->setEnabled(mMaxDuration);
255 /******************************************************************************
256 * Set the read-only status.
258 void RepetitionDlg::setReadOnly(bool ro)
260 ro = ro || mReadOnly;
261 mTimeSelector->setReadOnly(ro);
262 mCountButton->setReadOnly(ro);
263 mCount->setReadOnly(ro);
264 mDurationButton->setReadOnly(ro);
265 mDuration->setReadOnly(ro);
268 /******************************************************************************
269 * Get the entered interval and repeat count.
271 Repetition RepetitionDlg::repetition() const
273 int count = 0;
274 Duration interval = mTimeSelector->period();
275 if (interval)
277 if (mCountButton->isChecked())
278 count = mCount->value();
279 else if (mDurationButton->isChecked())
280 count = mDuration->period().asSeconds() / interval.asSeconds();
282 return Repetition(interval, count);
285 /******************************************************************************
286 * Called when the time interval widget has changed value.
287 * Adjust the maximum repetition count accordingly.
289 void RepetitionDlg::intervalChanged(const KCal::Duration& interval)
291 if (mTimeSelector->isChecked() && interval.asSeconds() > 0)
293 mCount->setRange(1, (mMaxDuration >= 0 ? mMaxDuration / (interval.asSeconds()/60) : MAX_COUNT));
294 if (mCountButton->isChecked())
295 countChanged(mCount->value());
296 else
297 durationChanged(mDuration->period());
301 /******************************************************************************
302 * Called when the count spinbox has changed value.
303 * Adjust the duration accordingly.
305 void RepetitionDlg::countChanged(int count)
307 Duration interval = mTimeSelector->period();
308 if (interval)
310 bool blocked = mDuration->signalsBlocked();
311 mDuration->blockSignals(true);
312 mDuration->setPeriod(interval * count, mDateOnly,
313 (mDateOnly ? TimePeriod::Days : TimePeriod::HoursMinutes));
314 mDuration->blockSignals(blocked);
318 /******************************************************************************
319 * Called when the duration widget has changed value.
320 * Adjust the count accordingly.
322 void RepetitionDlg::durationChanged(const KCal::Duration& duration)
324 Duration interval = mTimeSelector->period();
325 if (interval)
327 bool blocked = mCount->signalsBlocked();
328 mCount->blockSignals(true);
329 mCount->setValue(duration.asSeconds() / interval.asSeconds());
330 mCount->blockSignals(blocked);
334 /******************************************************************************
335 * Called when the time period widget is toggled on or off.
337 void RepetitionDlg::repetitionToggled(bool on)
339 if (mMaxDuration == 0)
340 on = false;
341 mButtonBox->setEnabled(on);
342 mCount->setEnabled(on && mCountButton->isChecked());
343 mDuration->setEnabled(on && mDurationButton->isChecked());
346 /******************************************************************************
347 * Called when one of the count or duration radio buttons is toggled.
349 void RepetitionDlg::typeClicked()
351 if (mTimeSelector->isChecked())
353 mCount->setEnabled(mCountButton->isChecked());
354 mDuration->setEnabled(mDurationButton->isChecked());