kalarm

editdlg.cpp
1/*
2 * editdlg.cpp - dialogue to create or modify an alarm or alarm template
3 * Program: kalarm
4 * Copyright © 2001-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 "kalarm.h"
22
23#include <limits.h>
24
25#include <tqlayout.h>
26#include <tqpopupmenu.h>
27#include <tqvbox.h>
28#include <tqgroupbox.h>
29#include <tqwidgetstack.h>
30#include <tqdragobject.h>
31#include <tqlabel.h>
32#include <tqmessagebox.h>
33#include <tqtabwidget.h>
34#include <tqvalidator.h>
35#include <tqwhatsthis.h>
36#include <tqtooltip.h>
37#include <tqdir.h>
38#include <tqstyle.h>
39
40#include <tdeglobal.h>
41#include <tdelocale.h>
42#include <tdeconfig.h>
43#include <tdefiledialog.h>
44#include <kiconloader.h>
45#include <tdeio/netaccess.h>
46#include <tdefileitem.h>
47#include <tdemessagebox.h>
48#include <kurldrag.h>
49#include <kurlcompletion.h>
50#include <twin.h>
51#include <twinmodule.h>
52#include <tdestandarddirs.h>
53#include <kstdguiitem.h>
54#include <tdeabc/addresseedialog.h>
55#include <kdebug.h>
56
57#include <libtdepim/maillistdrag.h>
58#include <libtdepim/kvcarddrag.h>
59#include <libkcal/icaldrag.h>
60
61#include "alarmcalendar.h"
62#include "alarmtimewidget.h"
63#include "checkbox.h"
64#include "colourcombo.h"
65#include "deferdlg.h"
66#include "emailidcombo.h"
67#include "fontcolourbutton.h"
68#include "functions.h"
69#include "kalarmapp.h"
70#include "kamail.h"
71#include "latecancel.h"
72#include "lineedit.h"
73#include "mainwindow.h"
74#include "pickfileradio.h"
75#include "preferences.h"
76#include "radiobutton.h"
77#include "recurrenceedit.h"
78#include "reminder.h"
79#include "shellprocess.h"
80#include "soundpicker.h"
81#include "specialactions.h"
82#include "spinbox.h"
83#include "templatepickdlg.h"
84#include "timeedit.h"
85#include "timespinbox.h"
86#include "editdlg.moc"
87#include "editdlgprivate.moc"
88
89using namespace KCal;
90
91static const char EDIT_DIALOG_NAME[] = "EditDialog";
92static const int maxDelayTime = 99*60 + 59; // < 100 hours
93
94/*=============================================================================
95= Class PickAlarmFileRadio
96=============================================================================*/
97class PickAlarmFileRadio : public PickFileRadio
98{
99 public:
100 PickAlarmFileRadio(const TQString& text, TQButtonGroup* parent, const char* name = 0)
101 : PickFileRadio(text, parent, name) { }
102 virtual TQString pickFile() // called when browse button is pressed to select a file to display
103 {
104 return KAlarm::browseFile(i18n("Choose Text or Image File to Display"), mDefaultDir, fileEdit()->text(),
105 TQString(), KFile::ExistingOnly, parentWidget(), "pickAlarmFile");
106 }
107 private:
108 TQString mDefaultDir; // default directory for file browse button
109};
110
111/*=============================================================================
112= Class PickLogFileRadio
113=============================================================================*/
114class PickLogFileRadio : public PickFileRadio
115{
116 public:
117 PickLogFileRadio(TQPushButton* b, LineEdit* e, const TQString& text, TQButtonGroup* parent, const char* name = 0)
118 : PickFileRadio(b, e, text, parent, name) { }
119 virtual TQString pickFile() // called when browse button is pressed to select a log file
120 {
121 return KAlarm::browseFile(i18n("Choose Log File"), mDefaultDir, fileEdit()->text(), TQString(),
122 KFile::LocalOnly, parentWidget(), "pickLogFile");
123 }
124 private:
125 TQString mDefaultDir; // default directory for log file browse button
126};
127
128inline TQString recurText(const KAEvent& event)
129{
130 TQString r;
131 if (event.repeatCount())
132 r = TQString::fromLatin1("%1 / %2").arg(event.recurrenceText()).arg(event.repetitionText());
133 else
134 r = event.recurrenceText();
135 return i18n("&Recurrence - [%1]").arg(r);
136}
137
138// Collect these widget labels together to ensure consistent wording and
139// translations across different modules.
140TQString EditAlarmDlg::i18n_ConfirmAck() { return i18n("Confirm acknowledgment"); }
141TQString EditAlarmDlg::i18n_k_ConfirmAck() { return i18n("Confirm ac&knowledgment"); }
142TQString EditAlarmDlg::i18n_SpecialActions() { return i18n("Special Actions..."); }
143TQString EditAlarmDlg::i18n_ShowInKOrganizer() { return i18n("Show in KOrganizer"); }
144TQString EditAlarmDlg::i18n_g_ShowInKOrganizer() { return i18n("Show in KOr&ganizer"); }
145TQString EditAlarmDlg::i18n_EnterScript() { return i18n("Enter a script"); }
146TQString EditAlarmDlg::i18n_p_EnterScript() { return i18n("Enter a scri&pt"); }
147TQString EditAlarmDlg::i18n_ExecInTermWindow() { return i18n("Execute in terminal window"); }
148TQString EditAlarmDlg::i18n_w_ExecInTermWindow() { return i18n("Execute in terminal &window"); }
149TQString EditAlarmDlg::i18n_u_ExecInTermWindow() { return i18n("Exec&ute in terminal window"); }
150TQString EditAlarmDlg::i18n_g_LogToFile() { return i18n("Lo&g to file"); }
151TQString EditAlarmDlg::i18n_CopyEmailToSelf() { return i18n("Copy email to self"); }
152TQString EditAlarmDlg::i18n_e_CopyEmailToSelf() { return i18n("Copy &email to self"); }
153TQString EditAlarmDlg::i18n_s_CopyEmailToSelf() { return i18n("Copy email to &self"); }
154TQString EditAlarmDlg::i18n_EmailFrom() { return i18n("'From' email address", "From:"); }
155TQString EditAlarmDlg::i18n_f_EmailFrom() { return i18n("'From' email address", "&From:"); }
156TQString EditAlarmDlg::i18n_EmailTo() { return i18n("Email addressee", "To:"); }
157TQString EditAlarmDlg::i18n_EmailSubject() { return i18n("Email subject", "Subject:"); }
158TQString EditAlarmDlg::i18n_j_EmailSubject() { return i18n("Email subject", "Sub&ject:"); }
159
160
161/******************************************************************************
162 * Constructor.
163 * Parameters:
164 * Template = true to edit/create an alarm template
165 * = false to edit/create an alarm.
166 * event != to initialise the dialogue to show the specified event's data.
167 */
168EditAlarmDlg::EditAlarmDlg(bool Template, const TQString& caption, TQWidget* parent, const char* name,
169 const KAEvent* event, bool readOnly)
170 : KDialogBase(parent, (name ? name : Template ? "TemplEditDlg" : "EditDlg"), true, caption,
171 (readOnly ? Cancel|Try : Template ? Ok|Cancel|Try : Ok|Cancel|Try|Default),
172 (readOnly ? Cancel : Ok)),
173 mMainPageShown(false),
174 mRecurPageShown(false),
175 mRecurSetDefaultEndDate(true),
176 mTemplateName(0),
177 mSpecialActionsButton(0),
178 mReminderDeferral(false),
179 mReminderArchived(false),
180 mEmailRemoveButton(0),
181 mDeferGroup(0),
182 mTimeWidget(0),
183 mShowInKorganizer(0),
184 mDeferGroupHeight(0),
185 mTemplate(Template),
186 mDesiredReadOnly(readOnly),
187 mReadOnly(readOnly),
188 mSavedEvent(0)
189{
190 setButtonText(Default, i18n("Load Template..."));
191 TQVBox* mainWidget = new TQVBox(this);
192 mainWidget->setSpacing(spacingHint());
193 setMainWidget(mainWidget);
194 if (mTemplate)
195 {
196 TQHBox* box = new TQHBox(mainWidget);
197 box->setSpacing(spacingHint());
198 TQLabel* label = new TQLabel(i18n("Template name:"), box);
199 label->setFixedSize(label->sizeHint());
200 mTemplateName = new TQLineEdit(box);
201 mTemplateName->setReadOnly(mReadOnly);
202 label->setBuddy(mTemplateName);
203 TQWhatsThis::add(box, i18n("Enter the name of the alarm template"));
204 box->setFixedHeight(box->sizeHint().height());
205 }
206 mTabs = new TQTabWidget(mainWidget);
207 mTabs->setMargin(marginHint());
208
209 TQVBox* mainPageBox = new TQVBox(mTabs);
210 mainPageBox->setSpacing(spacingHint());
211 mTabs->addTab(mainPageBox, i18n("&Alarm"));
212 mMainPageIndex = 0;
213 PageFrame* mainPage = new PageFrame(mainPageBox);
214 connect(mainPage, TQ_SIGNAL(shown()), TQ_SLOT(slotShowMainPage()));
215 TQVBoxLayout* topLayout = new TQVBoxLayout(mainPage, 0, spacingHint());
216
217 // Recurrence tab
218 TQVBox* recurTab = new TQVBox(mTabs);
219 mainPageBox->setSpacing(spacingHint());
220 mTabs->addTab(recurTab, TQString());
221 mRecurPageIndex = 1;
222 mRecurrenceEdit = new RecurrenceEdit(readOnly, recurTab, "recurPage");
223 connect(mRecurrenceEdit, TQ_SIGNAL(shown()), TQ_SLOT(slotShowRecurrenceEdit()));
224 connect(mRecurrenceEdit, TQ_SIGNAL(typeChanged(int)), TQ_SLOT(slotRecurTypeChange(int)));
225 connect(mRecurrenceEdit, TQ_SIGNAL(frequencyChanged()), TQ_SLOT(slotRecurFrequencyChange()));
226 connect(mRecurrenceEdit, TQ_SIGNAL(repeatNeedsInitialisation()), TQ_SLOT(slotSetSubRepetition()));
227
228 // Alarm action
229
230 mActionGroup = new ButtonGroup(i18n("Action"), mainPage, "actionGroup");
231 connect(mActionGroup, TQ_SIGNAL(buttonSet(int)), TQ_SLOT(slotAlarmTypeChanged(int)));
232 topLayout->addWidget(mActionGroup, 1);
233 TQBoxLayout* layout = new TQVBoxLayout(mActionGroup, marginHint(), spacingHint());
234 layout->addSpacing(fontMetrics().lineSpacing()/2);
235 TQGridLayout* grid = new TQGridLayout(layout, 1, 5);
236
237 // Message radio button
238 mMessageRadio = new RadioButton(i18n("Te&xt"), mActionGroup, "messageButton");
239 mMessageRadio->setFixedSize(mMessageRadio->sizeHint());
240 TQWhatsThis::add(mMessageRadio,
241 i18n("If checked, the alarm will display a text message."));
242 grid->addWidget(mMessageRadio, 1, 0);
243 grid->setColStretch(1, 1);
244
245 // File radio button
246 mFileRadio = new PickAlarmFileRadio(i18n("&File"), mActionGroup, "fileButton");
247 mFileRadio->setFixedSize(mFileRadio->sizeHint());
248 TQWhatsThis::add(mFileRadio,
249 i18n("If checked, the alarm will display the contents of a text or image file."));
250 grid->addWidget(mFileRadio, 1, 2);
251 grid->setColStretch(3, 1);
252
253 // Command radio button
254 mCommandRadio = new RadioButton(i18n("Co&mmand"), mActionGroup, "cmdButton");
255 mCommandRadio->setFixedSize(mCommandRadio->sizeHint());
256 TQWhatsThis::add(mCommandRadio,
257 i18n("If checked, the alarm will execute a shell command."));
258 grid->addWidget(mCommandRadio, 1, 4);
259 grid->setColStretch(5, 1);
260
261 // Email radio button
262 mEmailRadio = new RadioButton(i18n("&Email"), mActionGroup, "emailButton");
263 mEmailRadio->setFixedSize(mEmailRadio->sizeHint());
264 TQWhatsThis::add(mEmailRadio,
265 i18n("If checked, the alarm will send an email."));
266 grid->addWidget(mEmailRadio, 1, 6);
267
268 initDisplayAlarms(mActionGroup);
269 layout->addWidget(mDisplayAlarmsFrame);
270 initCommand(mActionGroup);
271 layout->addWidget(mCommandFrame);
272 initEmail(mActionGroup);
273 layout->addWidget(mEmailFrame);
274
275 // Deferred date/time: visible only for a deferred recurring event.
276 mDeferGroup = new TQGroupBox(1, TQt::Vertical, i18n("Deferred Alarm"), mainPage, "deferGroup");
277 topLayout->addWidget(mDeferGroup);
278 TQLabel* label = new TQLabel(i18n("Deferred to:"), mDeferGroup);
279 label->setFixedSize(label->sizeHint());
280 mDeferTimeLabel = new TQLabel(mDeferGroup);
281
282 mDeferChangeButton = new TQPushButton(i18n("C&hange..."), mDeferGroup);
283 mDeferChangeButton->setFixedSize(mDeferChangeButton->sizeHint());
284 connect(mDeferChangeButton, TQ_SIGNAL(clicked()), TQ_SLOT(slotEditDeferral()));
285 TQWhatsThis::add(mDeferChangeButton, i18n("Change the alarm's deferred time, or cancel the deferral"));
286 mDeferGroup->addSpace(0);
287
288 layout = new TQHBoxLayout(topLayout);
289
290 // Date and time entry
291 if (mTemplate)
292 {
293 mTemplateTimeGroup = new ButtonGroup(i18n("Time"), mainPage, "templateGroup");
294 connect(mTemplateTimeGroup, TQ_SIGNAL(buttonSet(int)), TQ_SLOT(slotTemplateTimeType(int)));
295 layout->addWidget(mTemplateTimeGroup);
296 grid = new TQGridLayout(mTemplateTimeGroup, 2, 2, marginHint(), spacingHint());
297 grid->addRowSpacing(0, fontMetrics().lineSpacing()/2);
298 // Get alignment to use in TQGridLayout (AlignAuto doesn't work correctly there)
299 int alignment = TQApplication::reverseLayout() ? TQt::AlignRight : TQt::AlignLeft;
300
301 mTemplateDefaultTime = new RadioButton(i18n("&Default time"), mTemplateTimeGroup, "templateDefTimeButton");
302 mTemplateDefaultTime->setFixedSize(mTemplateDefaultTime->sizeHint());
303 mTemplateDefaultTime->setReadOnly(mReadOnly);
304 TQWhatsThis::add(mTemplateDefaultTime,
305 i18n("Do not specify a start time for alarms based on this template. "
306 "The normal default start time will be used."));
307 grid->addWidget(mTemplateDefaultTime, 0, 0, alignment);
308
309 TQHBox* box = new TQHBox(mTemplateTimeGroup);
310 box->setSpacing(spacingHint());
311 mTemplateUseTime = new RadioButton(i18n("Time:"), box, "templateTimeButton");
312 mTemplateUseTime->setFixedSize(mTemplateUseTime->sizeHint());
313 mTemplateUseTime->setReadOnly(mReadOnly);
314 TQWhatsThis::add(mTemplateUseTime,
315 i18n("Specify a start time for alarms based on this template."));
316 mTemplateTimeGroup->insert(mTemplateUseTime);
317 mTemplateTime = new TimeEdit(box, "templateTimeEdit");
318 mTemplateTime->setFixedSize(mTemplateTime->sizeHint());
319 mTemplateTime->setReadOnly(mReadOnly);
320 TQWhatsThis::add(mTemplateTime,
321 TQString("%1\n\n%2").arg(i18n("Enter the start time for alarms based on this template."))
322 .arg(TimeSpinBox::shiftWhatsThis()));
323 box->setStretchFactor(new TQWidget(box), 1); // left adjust the controls
324 box->setFixedHeight(box->sizeHint().height());
325 grid->addWidget(box, 0, 1, alignment);
326
327 mTemplateAnyTime = new RadioButton(i18n("An&y time"), mTemplateTimeGroup, "templateAnyTimeButton");
328 mTemplateAnyTime->setFixedSize(mTemplateAnyTime->sizeHint());
329 mTemplateAnyTime->setReadOnly(mReadOnly);
330 TQWhatsThis::add(mTemplateAnyTime,
331 i18n("Set the '%1' option for alarms based on this template.").arg(i18n("Any time")));
332 grid->addWidget(mTemplateAnyTime, 1, 0, alignment);
333
334 box = new TQHBox(mTemplateTimeGroup);
335 box->setSpacing(spacingHint());
336 mTemplateUseTimeAfter = new RadioButton(AlarmTimeWidget::i18n_w_TimeFromNow(), box, "templateFromNowButton");
337 mTemplateUseTimeAfter->setFixedSize(mTemplateUseTimeAfter->sizeHint());
338 mTemplateUseTimeAfter->setReadOnly(mReadOnly);
339 TQWhatsThis::add(mTemplateUseTimeAfter,
340 i18n("Set alarms based on this template to start after the specified time "
341 "interval from when the alarm is created."));
342 mTemplateTimeGroup->insert(mTemplateUseTimeAfter);
343 mTemplateTimeAfter = new TimeSpinBox(1, maxDelayTime, box);
344 mTemplateTimeAfter->setValue(1439);
345 mTemplateTimeAfter->setFixedSize(mTemplateTimeAfter->sizeHint());
346 mTemplateTimeAfter->setReadOnly(mReadOnly);
347 TQWhatsThis::add(mTemplateTimeAfter,
348 TQString("%1\n\n%2").arg(AlarmTimeWidget::i18n_TimeAfterPeriod())
349 .arg(TimeSpinBox::shiftWhatsThis()));
350 box->setFixedHeight(box->sizeHint().height());
351 grid->addWidget(box, 1, 1, alignment);
352
353 layout->addStretch();
354 }
355 else
356 {
357 mTimeWidget = new AlarmTimeWidget(i18n("Time"), AlarmTimeWidget::AT_TIME, mainPage, "timeGroup");
358 connect(mTimeWidget, TQ_SIGNAL(anyTimeToggled(bool)), TQ_SLOT(slotAnyTimeToggled(bool)));
359 topLayout->addWidget(mTimeWidget);
360 }
361
362 // Reminder
363 static const TQString reminderText = i18n("Enter how long in advance of the main alarm to display a reminder alarm.");
364 mReminder = new Reminder(i18n("Rem&inder:"),
365 i18n("Check to additionally display a reminder in advance of the main alarm time(s)."),
366 TQString("%1\n\n%2").arg(reminderText).arg(TimeSpinBox::shiftWhatsThis()),
367 true, true, mainPage);
368 mReminder->setFixedSize(mReminder->sizeHint());
369 topLayout->addWidget(mReminder, 0, TQt::AlignAuto);
370
371 // Late cancel selector - default = allow late display
372 mLateCancel = new LateCancelSelector(true, mainPage);
373 topLayout->addWidget(mLateCancel, 0, TQt::AlignAuto);
374
375 // Acknowledgement confirmation required - default = no confirmation
376 layout = new TQHBoxLayout(topLayout, 0);
377 mConfirmAck = createConfirmAckCheckbox(mainPage);
378 mConfirmAck->setFixedSize(mConfirmAck->sizeHint());
379 layout->addWidget(mConfirmAck);
380 layout->addSpacing(2*spacingHint());
381 layout->addStretch();
382
383 if (theApp()->korganizerEnabled())
384 {
385 // Show in KOrganizer checkbox
386 mShowInKorganizer = new CheckBox(i18n_ShowInKOrganizer(), mainPage);
387 mShowInKorganizer->setFixedSize(mShowInKorganizer->sizeHint());
388 TQWhatsThis::add(mShowInKorganizer, i18n("Check to copy the alarm into KOrganizer's calendar"));
389 layout->addWidget(mShowInKorganizer);
390 }
391
392 setButtonWhatsThis(Ok, i18n("Schedule the alarm at the specified time."));
393
394 // Initialise the state of all controls according to the specified event, if any
395 initialise(event);
396 if (mTemplateName)
397 mTemplateName->setFocus();
398
399 // Save the initial state of all controls so that we can later tell if they have changed
400 saveState((event && (mTemplate || !event->isTemplate())) ? event : 0);
401
402 // Note the current desktop so that the dialog can be shown on it.
403 // If a main window is visible, the dialog will by KDE default always appear on its
404 // desktop. If the user invokes the dialog via the system tray on a different desktop,
405 // that can cause confusion.
406 mDesktop = KWin::currentDesktop();
407}
408
409EditAlarmDlg::~EditAlarmDlg()
410{
411 delete mSavedEvent;
412}
413
414/******************************************************************************
415 * Set up the dialog controls common to display alarms.
416 */
417void EditAlarmDlg::initDisplayAlarms(TQWidget* parent)
418{
419 mDisplayAlarmsFrame = new TQFrame(parent);
420 mDisplayAlarmsFrame->setFrameStyle(TQFrame::NoFrame);
421 TQBoxLayout* frameLayout = new TQVBoxLayout(mDisplayAlarmsFrame, 0, spacingHint());
422
423 // Text message edit box
424 mTextMessageEdit = new TextEdit(mDisplayAlarmsFrame);
425 mTextMessageEdit->setWordWrap(KTextEdit::NoWrap);
426 TQWhatsThis::add(mTextMessageEdit, i18n("Enter the text of the alarm message. It may be multi-line."));
427 frameLayout->addWidget(mTextMessageEdit);
428
429 // File name edit box
430 mFileBox = new TQHBox(mDisplayAlarmsFrame);
431 frameLayout->addWidget(mFileBox);
432 mFileMessageEdit = new LineEdit(LineEdit::Url, mFileBox);
433 mFileMessageEdit->setAcceptDrops(true);
434 TQWhatsThis::add(mFileMessageEdit, i18n("Enter the name or URL of a text or image file to display."));
435
436 // File browse button
437 mFileBrowseButton = new TQPushButton(mFileBox);
438 mFileBrowseButton->setPixmap(SmallIcon("document-open"));
439 mFileBrowseButton->setFixedSize(mFileBrowseButton->sizeHint());
440 TQToolTip::add(mFileBrowseButton, i18n("Choose a file"));
441 TQWhatsThis::add(mFileBrowseButton, i18n("Select a text or image file to display."));
442 mFileRadio->init(mFileBrowseButton, mFileMessageEdit);
443
444 // Font and colour choice button and sample text
445 mFontColourButton = new FontColourButton(mDisplayAlarmsFrame);
446 mFontColourButton->setMaximumHeight(mFontColourButton->sizeHint().height());
447 frameLayout->addWidget(mFontColourButton);
448
449 TQHBoxLayout* layout = new TQHBoxLayout(frameLayout, 0, 0);
450 mBgColourBox = new TQHBox(mDisplayAlarmsFrame);
451 mBgColourBox->setSpacing(spacingHint());
452 layout->addWidget(mBgColourBox);
453 layout->addStretch();
454 TQLabel* label = new TQLabel(i18n("&Background color:"), mBgColourBox);
455 mBgColourButton = new ColourCombo(mBgColourBox);
456 label->setBuddy(mBgColourButton);
457 TQWhatsThis::add(mBgColourBox, i18n("Select the alarm message background color"));
458
459 // Sound checkbox and file selector
460 layout = new TQHBoxLayout(frameLayout);
461 mSoundPicker = new SoundPicker(mDisplayAlarmsFrame);
462 mSoundPicker->setFixedSize(mSoundPicker->sizeHint());
463 layout->addWidget(mSoundPicker);
464 layout->addSpacing(2*spacingHint());
465 layout->addStretch();
466
467 if (ShellProcess::authorised()) // don't display if shell commands not allowed (e.g. kiosk mode)
468 {
469 // Special actions button
470 mSpecialActionsButton = new SpecialActionsButton(i18n_SpecialActions(), mDisplayAlarmsFrame);
471 mSpecialActionsButton->setFixedSize(mSpecialActionsButton->sizeHint());
472 layout->addWidget(mSpecialActionsButton);
473 }
474
475 // Top-adjust the controls
476 mFilePadding = new TQHBox(mDisplayAlarmsFrame);
477 frameLayout->addWidget(mFilePadding);
478 frameLayout->setStretchFactor(mFilePadding, 1);
479}
480
481/******************************************************************************
482 * Set up the command alarm dialog controls.
483 */
484void EditAlarmDlg::initCommand(TQWidget* parent)
485{
486 mCommandFrame = new TQFrame(parent);
487 mCommandFrame->setFrameStyle(TQFrame::NoFrame);
488 TQBoxLayout* frameLayout = new TQVBoxLayout(mCommandFrame, 0, spacingHint());
489
490 mCmdTypeScript = new CheckBox(i18n_p_EnterScript(), mCommandFrame);
491 mCmdTypeScript->setFixedSize(mCmdTypeScript->sizeHint());
492 connect(mCmdTypeScript, TQ_SIGNAL(toggled(bool)), TQ_SLOT(slotCmdScriptToggled(bool)));
493 TQWhatsThis::add(mCmdTypeScript, i18n("Check to enter the contents of a script instead of a shell command line"));
494 frameLayout->addWidget(mCmdTypeScript, 0, TQt::AlignAuto);
495
496 mCmdCommandEdit = new LineEdit(LineEdit::Url, mCommandFrame);
497 TQWhatsThis::add(mCmdCommandEdit, i18n("Enter a shell command to execute."));
498 frameLayout->addWidget(mCmdCommandEdit);
499
500 mCmdScriptEdit = new TextEdit(mCommandFrame);
501 TQWhatsThis::add(mCmdScriptEdit, i18n("Enter the contents of a script to execute"));
502 frameLayout->addWidget(mCmdScriptEdit);
503
504 // What to do with command output
505
506 mCmdOutputGroup = new ButtonGroup(i18n("Command Output"), mCommandFrame);
507 frameLayout->addWidget(mCmdOutputGroup);
508 TQBoxLayout* layout = new TQVBoxLayout(mCmdOutputGroup, marginHint(), spacingHint());
509 layout->addSpacing(fontMetrics().lineSpacing()/2);
510
511 // Execute in terminal window
512 RadioButton* button = new RadioButton(i18n_u_ExecInTermWindow(), mCmdOutputGroup, "execInTerm");
513 button->setFixedSize(button->sizeHint());
514 TQWhatsThis::add(button, i18n("Check to execute the command in a terminal window"));
515 mCmdOutputGroup->insert(button, EXEC_IN_TERMINAL);
516 layout->addWidget(button, 0, TQt::AlignAuto);
517
518 // Log file name edit box
519 TQHBox* box = new TQHBox(mCmdOutputGroup);
520 (new TQWidget(box))->setFixedWidth(button->style().subRect(TQStyle::SR_RadioButtonIndicator, button).width()); // indent the edit box
521// (new TQWidget(box))->setFixedWidth(button->style().pixelMetric(TQStyle::PM_ExclusiveIndicatorWidth)); // indent the edit box
522 mCmdLogFileEdit = new LineEdit(LineEdit::Url, box);
523 mCmdLogFileEdit->setAcceptDrops(true);
524 TQWhatsThis::add(mCmdLogFileEdit, i18n("Enter the name or path of the log file."));
525
526 // Log file browse button.
527 // The file browser dialogue is activated by the PickLogFileRadio class.
528 TQPushButton* browseButton = new TQPushButton(box);
529 browseButton->setPixmap(SmallIcon("document-open"));
530 browseButton->setFixedSize(browseButton->sizeHint());
531 TQToolTip::add(browseButton, i18n("Choose a file"));
532 TQWhatsThis::add(browseButton, i18n("Select a log file."));
533
534 // Log output to file
535 button = new PickLogFileRadio(browseButton, mCmdLogFileEdit, i18n_g_LogToFile(), mCmdOutputGroup, "cmdLog");
536 button->setFixedSize(button->sizeHint());
537 TQWhatsThis::add(button,
538 i18n("Check to log the command output to a local file. The output will be appended to any existing contents of the file."));
539 mCmdOutputGroup->insert(button, LOG_TO_FILE);
540 layout->addWidget(button, 0, TQt::AlignAuto);
541 layout->addWidget(box);
542
543 // Discard output
544 button = new RadioButton(i18n("Discard"), mCmdOutputGroup, "cmdDiscard");
545 button->setFixedSize(button->sizeHint());
546 TQWhatsThis::add(button, i18n("Check to discard command output."));
547 mCmdOutputGroup->insert(button, DISCARD_OUTPUT);
548 layout->addWidget(button, 0, TQt::AlignAuto);
549
550 // Top-adjust the controls
551 mCmdPadding = new TQHBox(mCommandFrame);
552 frameLayout->addWidget(mCmdPadding);
553 frameLayout->setStretchFactor(mCmdPadding, 1);
554}
555
556/******************************************************************************
557 * Set up the email alarm dialog controls.
558 */
559void EditAlarmDlg::initEmail(TQWidget* parent)
560{
561 mEmailFrame = new TQFrame(parent);
562 mEmailFrame->setFrameStyle(TQFrame::NoFrame);
563 TQBoxLayout* layout = new TQVBoxLayout(mEmailFrame, 0, spacingHint());
564 TQGridLayout* grid = new TQGridLayout(layout, 3, 3, spacingHint());
565 grid->setColStretch(1, 1);
566
567 mEmailFromList = 0;
568 if (Preferences::emailFrom() == Preferences::MAIL_FROM_KMAIL)
569 {
570 // Email sender identity
571 TQLabel* label = new TQLabel(i18n_EmailFrom(), mEmailFrame);
572 label->setFixedSize(label->sizeHint());
573 grid->addWidget(label, 0, 0);
574
575 mEmailFromList = new EmailIdCombo(KAMail::identityManager(), mEmailFrame);
576 mEmailFromList->setMinimumSize(mEmailFromList->sizeHint());
577 label->setBuddy(mEmailFromList);
578 TQWhatsThis::add(mEmailFromList,
579 i18n("Your email identity, used to identify you as the sender when sending email alarms."));
580 grid->addMultiCellWidget(mEmailFromList, 0, 0, 1, 2);
581 }
582
583 // Email recipients
584 TQLabel* label = new TQLabel(i18n_EmailTo(), mEmailFrame);
585 label->setFixedSize(label->sizeHint());
586 grid->addWidget(label, 1, 0);
587
588 mEmailToEdit = new LineEdit(LineEdit::Emails, mEmailFrame);
589 mEmailToEdit->setMinimumSize(mEmailToEdit->sizeHint());
590 TQWhatsThis::add(mEmailToEdit,
591 i18n("Enter the addresses of the email recipients. Separate multiple addresses by "
592 "commas or semicolons."));
593 grid->addWidget(mEmailToEdit, 1, 1);
594
595 mEmailAddressButton = new TQPushButton(mEmailFrame);
596 mEmailAddressButton->setPixmap(SmallIcon("contents"));
597 mEmailAddressButton->setFixedSize(mEmailAddressButton->sizeHint());
598 connect(mEmailAddressButton, TQ_SIGNAL(clicked()), TQ_SLOT(openAddressBook()));
599 TQToolTip::add(mEmailAddressButton, i18n("Open address book"));
600 TQWhatsThis::add(mEmailAddressButton, i18n("Select email addresses from your address book."));
601 grid->addWidget(mEmailAddressButton, 1, 2);
602
603 // Email subject
604 label = new TQLabel(i18n_j_EmailSubject(), mEmailFrame);
605 label->setFixedSize(label->sizeHint());
606 grid->addWidget(label, 2, 0);
607
608 mEmailSubjectEdit = new LineEdit(mEmailFrame);
609 mEmailSubjectEdit->setMinimumSize(mEmailSubjectEdit->sizeHint());
610 label->setBuddy(mEmailSubjectEdit);
611 TQWhatsThis::add(mEmailSubjectEdit, i18n("Enter the email subject."));
612 grid->addMultiCellWidget(mEmailSubjectEdit, 2, 2, 1, 2);
613
614 // Email body
615 mEmailMessageEdit = new TextEdit(mEmailFrame);
616 TQWhatsThis::add(mEmailMessageEdit, i18n("Enter the email message."));
617 layout->addWidget(mEmailMessageEdit);
618
619 // Email attachments
620 grid = new TQGridLayout(layout, 2, 3, spacingHint());
621 label = new TQLabel(i18n("Attachment&s:"), mEmailFrame);
622 label->setFixedSize(label->sizeHint());
623 grid->addWidget(label, 0, 0);
624
625 mEmailAttachList = new TQComboBox(true, mEmailFrame);
626 mEmailAttachList->setMinimumSize(mEmailAttachList->sizeHint());
627 mEmailAttachList->lineEdit()->setReadOnly(true);
628TQListBox* list = mEmailAttachList->listBox();
629TQRect rect = list->geometry();
630list->setGeometry(rect.left() - 50, rect.top(), rect.width(), rect.height());
631 label->setBuddy(mEmailAttachList);
632 TQWhatsThis::add(mEmailAttachList,
633 i18n("Files to send as attachments to the email."));
634 grid->addWidget(mEmailAttachList, 0, 1);
635 grid->setColStretch(1, 1);
636
637 mEmailAddAttachButton = new TQPushButton(i18n("Add..."), mEmailFrame);
638 connect(mEmailAddAttachButton, TQ_SIGNAL(clicked()), TQ_SLOT(slotAddAttachment()));
639 TQWhatsThis::add(mEmailAddAttachButton, i18n("Add an attachment to the email."));
640 grid->addWidget(mEmailAddAttachButton, 0, 2);
641
642 mEmailRemoveButton = new TQPushButton(i18n("Remo&ve"), mEmailFrame);
643 connect(mEmailRemoveButton, TQ_SIGNAL(clicked()), TQ_SLOT(slotRemoveAttachment()));
644 TQWhatsThis::add(mEmailRemoveButton, i18n("Remove the highlighted attachment from the email."));
645 grid->addWidget(mEmailRemoveButton, 1, 2);
646
647 // BCC email to sender
648 mEmailBcc = new CheckBox(i18n_s_CopyEmailToSelf(), mEmailFrame);
649 mEmailBcc->setFixedSize(mEmailBcc->sizeHint());
650 TQWhatsThis::add(mEmailBcc,
651 i18n("If checked, the email will be blind copied to you."));
652 grid->addMultiCellWidget(mEmailBcc, 1, 1, 0, 1, TQt::AlignAuto);
653}
654
655/******************************************************************************
656 * Initialise the dialogue controls from the specified event.
657 */
658void EditAlarmDlg::initialise(const KAEvent* event)
659{
660 mReadOnly = mDesiredReadOnly;
661 if (!mTemplate && event && event->action() == KAEvent::COMMAND && !ShellProcess::authorised())
662 mReadOnly = true; // don't allow editing of existing command alarms in kiosk mode
663 setReadOnly();
664
665 mChanged = false;
666 mOnlyDeferred = false;
667 mExpiredRecurrence = false;
668 mKMailSerialNumber = 0;
669 bool deferGroupVisible = false;
670 if (event)
671 {
672 // Set the values to those for the specified event
673 if (mTemplate)
674 mTemplateName->setText(event->templateName());
675 bool recurs = event->recurs();
676 if ((recurs || event->repeatCount()) && !mTemplate && event->deferred())
677 {
678 deferGroupVisible = true;
679 mDeferDateTime = event->deferDateTime();
680 mDeferTimeLabel->setText(mDeferDateTime.formatLocale());
681 mDeferGroup->show();
682 }
683 if (event->defaultFont())
684 mFontColourButton->setDefaultFont();
685 else
686 mFontColourButton->setFont(event->font());
687 mFontColourButton->setBgColour(event->bgColour());
688 mFontColourButton->setFgColour(event->fgColour());
689 mBgColourButton->setColour(event->bgColour());
690 if (mTemplate)
691 {
692 // Editing a template
693 int afterTime = event->isTemplate() ? event->templateAfterTime() : -1;
694 bool noTime = !afterTime;
695 bool useTime = !event->mainDateTime().isDateOnly();
696 int button = mTemplateTimeGroup->id(noTime ? mTemplateDefaultTime :
697 (afterTime > 0) ? mTemplateUseTimeAfter :
698 useTime ? mTemplateUseTime : mTemplateAnyTime);
699 mTemplateTimeGroup->setButton(button);
700 mTemplateTimeAfter->setValue(afterTime > 0 ? afterTime : 1);
701 if (!noTime && useTime)
702 mTemplateTime->setValue(event->mainDateTime().time());
703 else
704 mTemplateTime->setValue(0);
705 }
706 else
707 {
708 if (event->isTemplate())
709 {
710 // Initialising from an alarm template: use current date
711 TQDateTime now = TQDateTime::currentDateTime();
712 int afterTime = event->templateAfterTime();
713 if (afterTime >= 0)
714 {
715 mTimeWidget->setDateTime(TQDateTime(now.addSecs(afterTime * 60)));
716 mTimeWidget->selectTimeFromNow();
717 }
718 else
719 {
720 TQDate d = now.date();
721 TQTime t = event->startDateTime().time();
722 bool dateOnly = event->startDateTime().isDateOnly();
723 if (!dateOnly && now.time() >= t)
724 d = d.addDays(1); // alarm time has already passed, so use tomorrow
725 mTimeWidget->setDateTime(DateTime(TQDateTime(d, t), dateOnly));
726 }
727 }
728 else
729 {
730 mExpiredRecurrence = recurs && event->mainExpired();
731 mTimeWidget->setDateTime(recurs || event->uidStatus() == KAEvent::EXPIRED ? event->startDateTime()
732 : event->mainExpired() ? event->deferDateTime() : event->mainDateTime());
733 }
734 }
735
736 KAEvent::Action action = event->action();
737 AlarmText altext;
738 if (event->commandScript())
739 altext.setScript(event->cleanText());
740 else
741 altext.setText(event->cleanText());
742 setAction(action, altext);
743 if (action == KAEvent::MESSAGE && event->kmailSerialNumber()
744 && AlarmText::checkIfEmail(event->cleanText()))
745 mKMailSerialNumber = event->kmailSerialNumber();
746 if (action == KAEvent::EMAIL)
747 mEmailAttachList->insertStringList(event->emailAttachments());
748
749 mLateCancel->setMinutes(event->lateCancel(), event->startDateTime().isDateOnly(),
750 TimePeriod::HOURS_MINUTES);
751 mLateCancel->showAutoClose(action == KAEvent::MESSAGE || action == KAEvent::FILE);
752 mLateCancel->setAutoClose(event->autoClose());
753 mLateCancel->setFixedSize(mLateCancel->sizeHint());
754 if (mShowInKorganizer)
755 mShowInKorganizer->setChecked(event->copyToKOrganizer());
756 mConfirmAck->setChecked(event->confirmAck());
757 int reminder = event->reminder();
758 if (!reminder && event->reminderDeferral() && !recurs)
759 {
760 reminder = event->reminderDeferral();
761 mReminderDeferral = true;
762 }
763 if (!reminder && event->reminderArchived() && recurs)
764 {
765 reminder = event->reminderArchived();
766 mReminderArchived = true;
767 }
768 mReminder->setMinutes(reminder, (mTimeWidget ? mTimeWidget->anyTime() : mTemplateAnyTime->isOn()));
769 mReminder->setOnceOnly(event->reminderOnceOnly());
770 mReminder->enableOnceOnly(event->recurs());
771 if (mSpecialActionsButton)
772 mSpecialActionsButton->setActions(event->preAction(), event->postAction());
773 mRecurrenceEdit->set(*event, (mTemplate || event->isTemplate())); // must be called after mTimeWidget is set up, to ensure correct date-only enabling
774 mTabs->setTabLabel(mTabs->page(mRecurPageIndex), recurText(*event));
775 SoundPicker::Type soundType = event->speak() ? SoundPicker::SPEAK
776 : event->beep() ? SoundPicker::BEEP
777 : !event->audioFile().isEmpty() ? SoundPicker::PLAY_FILE
778 : SoundPicker::NONE;
779 mSoundPicker->set(soundType, event->audioFile(), event->soundVolume(),
780 event->fadeVolume(), event->fadeSeconds(), event->repeatSound());
781 CmdLogType logType = event->commandXterm() ? EXEC_IN_TERMINAL
782 : !event->logFile().isEmpty() ? LOG_TO_FILE
783 : DISCARD_OUTPUT;
784 if (logType == LOG_TO_FILE)
785 mCmdLogFileEdit->setText(event->logFile()); // set file name before setting radio button
786 mCmdOutputGroup->setButton(logType);
787 mEmailToEdit->setText(event->emailAddresses(", "));
788 mEmailSubjectEdit->setText(event->emailSubject());
789 mEmailBcc->setChecked(event->emailBcc());
790 if (mEmailFromList)
791 mEmailFromList->setCurrentIdentity(event->emailFromId());
792 }
793 else
794 {
795 // Set the values to their defaults
796 if (!ShellProcess::authorised())
797 {
798 // Don't allow shell commands in kiosk mode
799 mCommandRadio->setEnabled(false);
800 if (mSpecialActionsButton)
801 mSpecialActionsButton->setEnabled(false);
802 }
803 mFontColourButton->setDefaultFont();
804 mFontColourButton->setBgColour(Preferences::defaultBgColour());
805 mFontColourButton->setFgColour(Preferences::defaultFgColour());
806 mBgColourButton->setColour(Preferences::defaultBgColour());
807 TQDateTime defaultTime = TQDateTime::currentDateTime().addSecs(60);
808 if (mTemplate)
809 {
810 mTemplateTimeGroup->setButton(mTemplateTimeGroup->id(mTemplateDefaultTime));
811 mTemplateTime->setValue(0);
812 mTemplateTimeAfter->setValue(1);
813 }
814 else
815 mTimeWidget->setDateTime(defaultTime);
816 mActionGroup->setButton(mActionGroup->id(mMessageRadio));
817 mLateCancel->setMinutes((Preferences::defaultLateCancel() ? 1 : 0), false, TimePeriod::HOURS_MINUTES);
818 mLateCancel->showAutoClose(true);
819 mLateCancel->setAutoClose(Preferences::defaultAutoClose());
820 mLateCancel->setFixedSize(mLateCancel->sizeHint());
821 if (mShowInKorganizer)
822 mShowInKorganizer->setChecked(Preferences::defaultCopyToKOrganizer());
823 mConfirmAck->setChecked(Preferences::defaultConfirmAck());
824 if (mSpecialActionsButton)
825 mSpecialActionsButton->setActions(Preferences::defaultPreAction(), Preferences::defaultPostAction());
826 mRecurrenceEdit->setDefaults(defaultTime); // must be called after mTimeWidget is set up, to ensure correct date-only enabling
827 slotRecurFrequencyChange(); // update the Recurrence text
828 mReminder->setMinutes(0, false);
829 mReminder->enableOnceOnly(mRecurrenceEdit->isTimedRepeatType()); // must be called after mRecurrenceEdit is set up
830 mSoundPicker->set(Preferences::defaultSoundType(), Preferences::defaultSoundFile(),
831 Preferences::defaultSoundVolume(), -1, 0, Preferences::defaultSoundRepeat());
832 mCmdTypeScript->setChecked(Preferences::defaultCmdScript());
833 mCmdLogFileEdit->setText(Preferences::defaultCmdLogFile()); // set file name before setting radio button
834 mCmdOutputGroup->setButton(Preferences::defaultCmdLogType());
835 mEmailBcc->setChecked(Preferences::defaultEmailBcc());
836 }
837 slotCmdScriptToggled(mCmdTypeScript->isChecked());
838
839 if (!deferGroupVisible)
840 mDeferGroup->hide();
841
842 bool enable = !!mEmailAttachList->count();
843 mEmailAttachList->setEnabled(enable);
844 if (mEmailRemoveButton)
845 mEmailRemoveButton->setEnabled(enable);
846 AlarmCalendar* cal = AlarmCalendar::templateCalendar();
847 bool empty = cal->isOpen() && !cal->events().count();
848 enableButton(Default, !empty);
849}
850
851/******************************************************************************
852 * Set the read-only status of all non-template controls.
853 */
854void EditAlarmDlg::setReadOnly()
855{
856 // Common controls
857 mMessageRadio->setReadOnly(mReadOnly);
858 mFileRadio->setReadOnly(mReadOnly);
859 mCommandRadio->setReadOnly(mReadOnly);
860 mEmailRadio->setReadOnly(mReadOnly);
861 if (mTimeWidget)
862 mTimeWidget->setReadOnly(mReadOnly);
863 mLateCancel->setReadOnly(mReadOnly);
864 if (mReadOnly)
865 mDeferChangeButton->hide();
866 else
867 mDeferChangeButton->show();
868 if (mShowInKorganizer)
869 mShowInKorganizer->setReadOnly(mReadOnly);
870
871 // Message alarm controls
872 mTextMessageEdit->setReadOnly(mReadOnly);
873 mFileMessageEdit->setReadOnly(mReadOnly);
874 mFontColourButton->setReadOnly(mReadOnly);
875 mBgColourButton->setReadOnly(mReadOnly);
876 mSoundPicker->setReadOnly(mReadOnly);
877 mConfirmAck->setReadOnly(mReadOnly);
878 mReminder->setReadOnly(mReadOnly);
879 if (mSpecialActionsButton)
880 mSpecialActionsButton->setReadOnly(mReadOnly);
881 if (mReadOnly)
882 {
883 mFileBrowseButton->hide();
884 mFontColourButton->hide();
885 }
886 else
887 {
888 mFileBrowseButton->show();
889 mFontColourButton->show();
890 }
891
892 // Command alarm controls
893 mCmdTypeScript->setReadOnly(mReadOnly);
894 mCmdCommandEdit->setReadOnly(mReadOnly);
895 mCmdScriptEdit->setReadOnly(mReadOnly);
896 for (int id = DISCARD_OUTPUT; id < EXEC_IN_TERMINAL; ++id)
897 ((RadioButton*)mCmdOutputGroup->find(id))->setReadOnly(mReadOnly);
898
899 // Email alarm controls
900 mEmailToEdit->setReadOnly(mReadOnly);
901 mEmailSubjectEdit->setReadOnly(mReadOnly);
902 mEmailMessageEdit->setReadOnly(mReadOnly);
903 mEmailBcc->setReadOnly(mReadOnly);
904 if (mEmailFromList)
905 mEmailFromList->setReadOnly(mReadOnly);
906 if (mReadOnly)
907 {
908 mEmailAddressButton->hide();
909 mEmailAddAttachButton->hide();
910 mEmailRemoveButton->hide();
911 }
912 else
913 {
914 mEmailAddressButton->show();
915 mEmailAddAttachButton->show();
916 mEmailRemoveButton->show();
917 }
918}
919
920/******************************************************************************
921 * Set the dialog's action and the action's text.
922 */
923void EditAlarmDlg::setAction(KAEvent::Action action, const AlarmText& alarmText)
924{
925 TQString text = alarmText.displayText();
926 bool script;
927 TQRadioButton* radio;
928 switch (action)
929 {
930 case KAEvent::FILE:
931 radio = mFileRadio;
932 mFileMessageEdit->setText(text);
933 break;
934 case KAEvent::COMMAND:
935 radio = mCommandRadio;
936 script = alarmText.isScript();
937 mCmdTypeScript->setChecked(script);
938 if (script)
939 mCmdScriptEdit->setText(text);
940 else
941 mCmdCommandEdit->setText(text);
942 break;
943 case KAEvent::EMAIL:
944 radio = mEmailRadio;
945 mEmailMessageEdit->setText(text);
946 break;
947 case KAEvent::MESSAGE:
948 default:
949 radio = mMessageRadio;
950 mTextMessageEdit->setText(text);
951 mKMailSerialNumber = 0;
952 if (alarmText.isEmail())
953 {
954 mKMailSerialNumber = alarmText.kmailSerialNumber();
955
956 // Set up email fields also, in case the user wants an email alarm
957 mEmailToEdit->setText(alarmText.to());
958 mEmailSubjectEdit->setText(alarmText.subject());
959 mEmailMessageEdit->setText(alarmText.body());
960 }
961 else if (alarmText.isScript())
962 {
963 // Set up command script field also, in case the user wants a command alarm
964 mCmdScriptEdit->setText(text);
965 mCmdTypeScript->setChecked(true);
966 }
967 break;
968 }
969 mActionGroup->setButton(mActionGroup->id(radio));
970}
971
972/******************************************************************************
973 * Create an "acknowledgement confirmation required" checkbox.
974 */
975CheckBox* EditAlarmDlg::createConfirmAckCheckbox(TQWidget* parent, const char* name)
976{
977 CheckBox* widget = new CheckBox(i18n_k_ConfirmAck(), parent, name);
978 TQWhatsThis::add(widget,
979 i18n("Check to be prompted for confirmation when you acknowledge the alarm."));
980 return widget;
981}
982
983/******************************************************************************
984 * Save the state of all controls.
985 */
986void EditAlarmDlg::saveState(const KAEvent* event)
987{
988 delete mSavedEvent;
989 mSavedEvent = 0;
990 if (event)
991 mSavedEvent = new KAEvent(*event);
992 if (mTemplate)
993 {
994 mSavedTemplateName = mTemplateName->text();
995 mSavedTemplateTimeType = mTemplateTimeGroup->selected();
996 mSavedTemplateTime = mTemplateTime->time();
997 mSavedTemplateAfterTime = mTemplateTimeAfter->value();
998 }
999 mSavedTypeRadio = mActionGroup->selected();
1000 mSavedSoundType = mSoundPicker->sound();
1001 mSavedSoundFile = mSoundPicker->file();
1002 mSavedSoundVolume = mSoundPicker->volume(mSavedSoundFadeVolume, mSavedSoundFadeSeconds);
1003 mSavedRepeatSound = mSoundPicker->repeat();
1004 mSavedConfirmAck = mConfirmAck->isChecked();
1005 mSavedFont = mFontColourButton->font();
1006 mSavedFgColour = mFontColourButton->fgColour();
1007 mSavedBgColour = mFileRadio->isOn() ? mBgColourButton->colour() : mFontColourButton->bgColour();
1008 mSavedReminder = mReminder->minutes();
1009 mSavedOnceOnly = mReminder->isOnceOnly();
1010 if (mSpecialActionsButton)
1011 {
1012 mSavedPreAction = mSpecialActionsButton->preAction();
1013 mSavedPostAction = mSpecialActionsButton->postAction();
1014 }
1015 checkText(mSavedTextFileCommandMessage, false);
1016 mSavedCmdScript = mCmdTypeScript->isChecked();
1017 mSavedCmdOutputRadio = mCmdOutputGroup->selected();
1018 mSavedCmdLogFile = mCmdLogFileEdit->text();
1019 if (mEmailFromList)
1020 mSavedEmailFrom = mEmailFromList->currentIdentityName();
1021 mSavedEmailTo = mEmailToEdit->text();
1022 mSavedEmailSubject = mEmailSubjectEdit->text();
1023 mSavedEmailAttach.clear();
1024 for (int i = 0; i < mEmailAttachList->count(); ++i)
1025 mSavedEmailAttach += mEmailAttachList->text(i);
1026 mSavedEmailBcc = mEmailBcc->isChecked();
1027 if (mTimeWidget)
1028 mSavedDateTime = mTimeWidget->getDateTime(0, false, false);
1029 mSavedLateCancel = mLateCancel->minutes();
1030 mSavedAutoClose = mLateCancel->isAutoClose();
1031 if (mShowInKorganizer)
1032 mSavedShowInKorganizer = mShowInKorganizer->isChecked();
1033 mSavedRecurrenceType = mRecurrenceEdit->repeatType();
1034}
1035
1036/******************************************************************************
1037 * Check whether any of the controls has changed state since the dialog was
1038 * first displayed.
1039 * Reply = true if any non-deferral controls have changed, or if it's a new event.
1040 * = false if no non-deferral controls have changed. In this case,
1041 * mOnlyDeferred indicates whether deferral controls may have changed.
1042 */
1043bool EditAlarmDlg::stateChanged() const
1044{
1045 mChanged = true;
1046 mOnlyDeferred = false;
1047 if (!mSavedEvent)
1048 return true;
1049 TQString textFileCommandMessage;
1050 checkText(textFileCommandMessage, false);
1051 if (mTemplate)
1052 {
1053 if (mSavedTemplateName != mTemplateName->text()
1054 || mSavedTemplateTimeType != mTemplateTimeGroup->selected()
1055 || (mTemplateUseTime->isOn() && mSavedTemplateTime != mTemplateTime->time())
1056 || (mTemplateUseTimeAfter->isOn() && mSavedTemplateAfterTime != mTemplateTimeAfter->value()))
1057 return true;
1058 }
1059 else
1060 if (mSavedDateTime != mTimeWidget->getDateTime(0, false, false))
1061 return true;
1062 if (mSavedTypeRadio != mActionGroup->selected()
1063 || mSavedLateCancel != mLateCancel->minutes()
1064 || (mShowInKorganizer && mSavedShowInKorganizer != mShowInKorganizer->isChecked())
1065 || textFileCommandMessage != mSavedTextFileCommandMessage
1066 || mSavedRecurrenceType != mRecurrenceEdit->repeatType())
1067 return true;
1068 if (mMessageRadio->isOn() || mFileRadio->isOn())
1069 {
1070 if (mSavedSoundType != mSoundPicker->sound()
1071 || mSavedConfirmAck != mConfirmAck->isChecked()
1072 || mSavedFont != mFontColourButton->font()
1073 || mSavedFgColour != mFontColourButton->fgColour()
1074 || mSavedBgColour != (mFileRadio->isOn() ? mBgColourButton->colour() : mFontColourButton->bgColour())
1075 || mSavedReminder != mReminder->minutes()
1076 || mSavedOnceOnly != mReminder->isOnceOnly()
1077 || mSavedAutoClose != mLateCancel->isAutoClose())
1078 return true;
1079 if (mSpecialActionsButton)
1080 {
1081 if (mSavedPreAction != mSpecialActionsButton->preAction()
1082 || mSavedPostAction != mSpecialActionsButton->postAction())
1083 return true;
1084 }
1085 if (mSavedSoundType == SoundPicker::PLAY_FILE)
1086 {
1087 if (mSavedSoundFile != mSoundPicker->file())
1088 return true;
1089 if (!mSavedSoundFile.isEmpty())
1090 {
1091 float fadeVolume;
1092 int fadeSecs;
1093 if (mSavedRepeatSound != mSoundPicker->repeat()
1094 || mSavedSoundVolume != mSoundPicker->volume(fadeVolume, fadeSecs)
1095 || mSavedSoundFadeVolume != fadeVolume
1096 || mSavedSoundFadeSeconds != fadeSecs)
1097 return true;
1098 }
1099 }
1100 }
1101 else if (mCommandRadio->isOn())
1102 {
1103 if (mSavedCmdScript != mCmdTypeScript->isChecked()
1104 || mSavedCmdOutputRadio != mCmdOutputGroup->selected())
1105 return true;
1106 if (mCmdOutputGroup->selectedId() == LOG_TO_FILE)
1107 {
1108 if (mSavedCmdLogFile != mCmdLogFileEdit->text())
1109 return true;
1110 }
1111 }
1112 else if (mEmailRadio->isOn())
1113 {
1114 TQStringList emailAttach;
1115 for (int i = 0; i < mEmailAttachList->count(); ++i)
1116 emailAttach += mEmailAttachList->text(i);
1117 if ((mEmailFromList && mSavedEmailFrom != mEmailFromList->currentIdentityName())
1118 || mSavedEmailTo != mEmailToEdit->text()
1119 || mSavedEmailSubject != mEmailSubjectEdit->text()
1120 || mSavedEmailAttach != emailAttach
1121 || mSavedEmailBcc != mEmailBcc->isChecked())
1122 return true;
1123 }
1124 if (mRecurrenceEdit->stateChanged())
1125 return true;
1126 if (mSavedEvent && mSavedEvent->deferred())
1127 mOnlyDeferred = true;
1128 mChanged = false;
1129 return false;
1130}
1131
1132/******************************************************************************
1133 * Get the currently entered dialogue data.
1134 * The data is returned in the supplied KAEvent instance.
1135 * Reply = false if the only change has been to an existing deferral.
1136 */
1137bool EditAlarmDlg::getEvent(KAEvent& event)
1138{
1139 if (mChanged)
1140 {
1141 // It's a new event, or the edit controls have changed
1142 setEvent(event, mAlarmMessage, false);
1143 return true;
1144 }
1145
1146 // Only the deferral time may have changed
1147 event = *mSavedEvent;
1148 if (mOnlyDeferred)
1149 {
1150 // Just modify the original event, to avoid expired recurring events
1151 // being returned as rubbish.
1152 if (mDeferDateTime.isValid())
1153 event.defer(mDeferDateTime, event.reminderDeferral(), false);
1154 else
1155 event.cancelDefer();
1156 }
1157 return false;
1158}
1159
1160/******************************************************************************
1161* Extract the data in the dialogue and set up a KAEvent from it.
1162* If 'trial' is true, the event is set up for a simple one-off test, ignoring
1163* recurrence, reminder, template etc. data.
1164*/
1165void EditAlarmDlg::setEvent(KAEvent& event, const TQString& text, bool trial)
1166{
1167 TQDateTime dt;
1168 if (!trial)
1169 {
1170 if (!mTemplate)
1171 dt = mAlarmDateTime.dateTime();
1172 else if (mTemplateUseTime->isOn())
1173 dt = TQDateTime(TQDate(2000,1,1), mTemplateTime->time());
1174 }
1175 KAEvent::Action type = getAlarmType();
1176 event.set(dt, text, (mFileRadio->isOn() ? mBgColourButton->colour() : mFontColourButton->bgColour()),
1177 mFontColourButton->fgColour(), mFontColourButton->font(),
1178 type, (trial ? 0 : mLateCancel->minutes()), getAlarmFlags());
1179 switch (type)
1180 {
1181 case KAEvent::MESSAGE:
1182 if (AlarmText::checkIfEmail(text))
1183 event.setKMailSerialNumber(mKMailSerialNumber);
1184 // fall through to FILE
1185 case KAEvent::FILE:
1186 {
1187 float fadeVolume;
1188 int fadeSecs;
1189 float volume = mSoundPicker->volume(fadeVolume, fadeSecs);
1190 event.setAudioFile(mSoundPicker->file(), volume, fadeVolume, fadeSecs);
1191 if (!trial)
1192 event.setReminder(mReminder->minutes(), mReminder->isOnceOnly());
1193 if (mSpecialActionsButton)
1194 event.setActions(mSpecialActionsButton->preAction(), mSpecialActionsButton->postAction());
1195 break;
1196 }
1197 case KAEvent::EMAIL:
1198 {
1199 uint from = mEmailFromList ? mEmailFromList->currentIdentity() : 0;
1200 event.setEmail(from, mEmailAddresses, mEmailSubjectEdit->text(), mEmailAttachments);
1201 break;
1202 }
1203 case KAEvent::COMMAND:
1204 if (mCmdOutputGroup->selectedId() == LOG_TO_FILE)
1205 event.setLogFile(mCmdLogFileEdit->text());
1206 break;
1207 default:
1208 break;
1209 }
1210 if (!trial)
1211 {
1212 if (mRecurrenceEdit->repeatType() != RecurrenceEdit::NO_RECUR)
1213 {
1214 mRecurrenceEdit->updateEvent(event, !mTemplate);
1215 TQDateTime now = TQDateTime::currentDateTime();
1216 bool dateOnly = mAlarmDateTime.isDateOnly();
1217 if ((dateOnly && mAlarmDateTime.date() < now.date())
1218 || (!dateOnly && mAlarmDateTime.rawDateTime() < now))
1219 {
1220 // A timed recurrence has an entered start date which has
1221 // already expired, so we must adjust the next repetition.
1222 event.setNextOccurrence(now);
1223 }
1224 mAlarmDateTime = event.startDateTime();
1225 if (mDeferDateTime.isValid() && mDeferDateTime < mAlarmDateTime)
1226 {
1227 bool deferral = true;
1228 bool deferReminder = false;
1229 int reminder = mReminder->minutes();
1230 if (reminder)
1231 {
1232 DateTime remindTime = mAlarmDateTime.addMins(-reminder);
1233 if (mDeferDateTime >= remindTime)
1234 {
1235 if (remindTime > TQDateTime::currentDateTime())
1236 deferral = false; // ignore deferral if it's after next reminder
1237 else if (mDeferDateTime > remindTime)
1238 deferReminder = true; // it's the reminder which is being deferred
1239 }
1240 }
1241 if (deferral)
1242 event.defer(mDeferDateTime, deferReminder, false);
1243 }
1244 }
1245 if (mTemplate)
1246 {
1247 int afterTime = mTemplateDefaultTime->isOn() ? 0
1248 : mTemplateUseTimeAfter->isOn() ? mTemplateTimeAfter->value() : -1;
1249 event.setTemplate(mTemplateName->text(), afterTime);
1250 }
1251 }
1252}
1253
1254/******************************************************************************
1255 * Get the currently specified alarm flag bits.
1256 */
1257int EditAlarmDlg::getAlarmFlags() const
1258{
1259 bool displayAlarm = mMessageRadio->isOn() || mFileRadio->isOn();
1260 bool cmdAlarm = mCommandRadio->isOn();
1261 bool emailAlarm = mEmailRadio->isOn();
1262 return (displayAlarm && mSoundPicker->sound() == SoundPicker::BEEP ? KAEvent::BEEP : 0)
1263 | (displayAlarm && mSoundPicker->sound() == SoundPicker::SPEAK ? KAEvent::SPEAK : 0)
1264 | (displayAlarm && mSoundPicker->repeat() ? KAEvent::REPEAT_SOUND : 0)
1265 | (displayAlarm && mConfirmAck->isChecked() ? KAEvent::CONFIRM_ACK : 0)
1266 | (displayAlarm && mLateCancel->isAutoClose() ? KAEvent::AUTO_CLOSE : 0)
1267 | (cmdAlarm && mCmdTypeScript->isChecked() ? KAEvent::SCRIPT : 0)
1268 | (cmdAlarm && mCmdOutputGroup->selectedId() == EXEC_IN_TERMINAL ? KAEvent::EXEC_IN_XTERM : 0)
1269 | (emailAlarm && mEmailBcc->isChecked() ? KAEvent::EMAIL_BCC : 0)
1270 | (mShowInKorganizer && mShowInKorganizer->isChecked() ? KAEvent::COPY_KORGANIZER : 0)
1271 | (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN ? KAEvent::REPEAT_AT_LOGIN : 0)
1272 | ((mTemplate ? mTemplateAnyTime->isOn() : mAlarmDateTime.isDateOnly()) ? KAEvent::ANY_TIME : 0)
1273 | (mFontColourButton->defaultFont() ? KAEvent::DEFAULT_FONT : 0);
1274}
1275
1276/******************************************************************************
1277 * Get the currently selected alarm type.
1278 */
1279KAEvent::Action EditAlarmDlg::getAlarmType() const
1280{
1281 return mFileRadio->isOn() ? KAEvent::FILE
1282 : mCommandRadio->isOn() ? KAEvent::COMMAND
1283 : mEmailRadio->isOn() ? KAEvent::EMAIL
1284 : KAEvent::MESSAGE;
1285}
1286
1287/******************************************************************************
1288* Called when the dialog is displayed.
1289* The first time through, sets the size to the same as the last time it was
1290* displayed.
1291*/
1292void EditAlarmDlg::showEvent(TQShowEvent* se)
1293{
1294 if (!mDeferGroupHeight)
1295 {
1296 mDeferGroupHeight = mDeferGroup->height() + spacingHint();
1297 TQSize s;
1298 if (KAlarm::readConfigWindowSize(EDIT_DIALOG_NAME, s))
1299 s.setHeight(s.height() + (mDeferGroup->isHidden() ? 0 : mDeferGroupHeight));
1300 else
1301 s = minimumSize();
1302 resize(s);
1303 }
1304 KWin::setOnDesktop(winId(), mDesktop); // ensure it displays on the desktop expected by the user
1305 KDialog::showEvent(se);
1306}
1307
1308/******************************************************************************
1309* Called when the dialog's size has changed.
1310* Records the new size (adjusted to ignore the optional height of the deferred
1311* time edit widget) in the config file.
1312*/
1313void EditAlarmDlg::resizeEvent(TQResizeEvent* re)
1314{
1315 if (isVisible())
1316 {
1317 TQSize s = re->size();
1318 s.setHeight(s.height() - (mDeferGroup->isHidden() ? 0 : mDeferGroupHeight));
1319 KAlarm::writeConfigWindowSize(EDIT_DIALOG_NAME, s);
1320 }
1321 KDialog::resizeEvent(re);
1322}
1323
1324/******************************************************************************
1325* Called when the OK button is clicked.
1326* Validate the input data.
1327*/
1328void EditAlarmDlg::slotOk()
1329{
1330 if (!stateChanged())
1331 {
1332 // No changes have been made except possibly to an existing deferral
1333 if (!mOnlyDeferred)
1334 reject();
1335 else
1336 accept();
1337 return;
1338 }
1339 RecurrenceEdit::RepeatType recurType = mRecurrenceEdit->repeatType();
1340 if (mTimeWidget
1341 && mTabs->currentPageIndex() == mRecurPageIndex && recurType == RecurrenceEdit::AT_LOGIN)
1342 mTimeWidget->setDateTime(mRecurrenceEdit->endDateTime());
1343 bool timedRecurrence = mRecurrenceEdit->isTimedRepeatType(); // does it recur other than at login?
1344 if (mTemplate)
1345 {
1346 // Check that the template name is not blank and is unique
1347 TQString errmsg;
1348 TQString name = mTemplateName->text();
1349 if (name.isEmpty())
1350 errmsg = i18n("You must enter a name for the alarm template");
1351 else if (name != mSavedTemplateName)
1352 {
1353 AlarmCalendar* cal = AlarmCalendar::templateCalendarOpen();
1354 if (cal && KAEvent::findTemplateName(*cal, name).valid())
1355 errmsg = i18n("Template name is already in use");
1356 }
1357 if (!errmsg.isEmpty())
1358 {
1359 mTemplateName->setFocus();
1360 KMessageBox::sorry(this, errmsg);
1361 return;
1362 }
1363 }
1364 else
1365 {
1366 TQWidget* errWidget;
1367 mAlarmDateTime = mTimeWidget->getDateTime(0, !timedRecurrence, false, &errWidget);
1368 if (errWidget)
1369 {
1370 // It's more than just an existing deferral being changed, so the time matters
1371 mTabs->setCurrentPage(mMainPageIndex);
1372 errWidget->setFocus();
1373 mTimeWidget->getDateTime(); // display the error message now
1374 return;
1375 }
1376 }
1377 if (!checkCommandData() || !checkEmailData())
1378 return;
1379 if (!mTemplate)
1380 {
1381 if (timedRecurrence)
1382 {
1383 // For daily, weekly, monthly and yearly recurrences, check that the
1384 // specified date matches the allowed days of the week
1385 if (!mRecurrenceEdit->validateDate(mAlarmDateTime))
1386 {
1387 KMessageBox::sorry(this, i18n("The date/time in the Alarm tab does not "
1388 "match the recurrence settings specified in the Recurrence tab."));
1389 return;
1390 }
1391 TQDateTime now = TQDateTime::currentDateTime();
1392 if (mAlarmDateTime.date() < now.date()
1393 || (mAlarmDateTime.date() == now.date()
1394 && !mAlarmDateTime.isDateOnly() && mAlarmDateTime.time() < now.time()))
1395 {
1396 // A timed recurrence has an entered start date which
1397 // has already expired, so we must adjust it.
1398 KAEvent event;
1399 getEvent(event); // this may adjust mAlarmDateTime
1400 if (( mAlarmDateTime.date() < now.date()
1401 || (mAlarmDateTime.date() == now.date()
1402 && !mAlarmDateTime.isDateOnly() && mAlarmDateTime.time() < now.time()))
1403 && event.nextOccurrence(now, mAlarmDateTime, KAEvent::ALLOW_FOR_REPETITION) == KAEvent::NO_OCCURRENCE)
1404 {
1405 KMessageBox::sorry(this, i18n("Recurrence has already expired"));
1406 return;
1407 }
1408 }
1409 }
1410 TQString errmsg;
1411 TQWidget* errWidget = mRecurrenceEdit->checkData(mAlarmDateTime.dateTime(), errmsg);
1412 if (errWidget)
1413 {
1414 mTabs->setCurrentPage(mRecurPageIndex);
1415 errWidget->setFocus();
1416 KMessageBox::sorry(this, errmsg);
1417 return;
1418 }
1419 }
1420 if (recurType != RecurrenceEdit::NO_RECUR)
1421 {
1422 KAEvent recurEvent;
1423 int longestRecurInterval = -1;
1424 int reminder = mReminder->minutes();
1425 if (reminder && !mReminder->isOnceOnly())
1426 {
1427 mRecurrenceEdit->updateEvent(recurEvent, false);
1428 longestRecurInterval = recurEvent.longestRecurrenceInterval();
1429 if (longestRecurInterval && reminder >= longestRecurInterval)
1430 {
1431 mTabs->setCurrentPage(mMainPageIndex);
1432 mReminder->setFocusOnCount();
1433 KMessageBox::sorry(this, i18n("Reminder period must be less than the recurrence interval, unless '%1' is checked.").arg(Reminder::i18n_first_recurrence_only()));
1434 return;
1435 }
1436 }
1437 if (mRecurrenceEdit->subRepeatCount())
1438 {
1439 if (longestRecurInterval < 0)
1440 {
1441 mRecurrenceEdit->updateEvent(recurEvent, false);
1442 longestRecurInterval = recurEvent.longestRecurrenceInterval();
1443 }
1444 if (longestRecurInterval > 0
1445 && recurEvent.repeatInterval() * recurEvent.repeatCount() >= longestRecurInterval - reminder)
1446 {
1447 KMessageBox::sorry(this, i18n("The duration of a repetition within the recurrence must be less than the recurrence interval minus any reminder period"));
1448 mRecurrenceEdit->activateSubRepetition(); // display the alarm repetition dialog again
1449 return;
1450 }
1451 if (recurEvent.repeatInterval() % 1440
1452 && ((mTemplate && mTemplateAnyTime->isOn()) || (!mTemplate && mAlarmDateTime.isDateOnly())))
1453 {
1454 KMessageBox::sorry(this, i18n("For a repetition within the recurrence, its period must be in units of days or weeks for a date-only alarm"));
1455 mRecurrenceEdit->activateSubRepetition(); // display the alarm repetition dialog again
1456 return;
1457 }
1458 }
1459 }
1460 if (checkText(mAlarmMessage))
1461 accept();
1462}
1463
1464/******************************************************************************
1465* Called when the Try button is clicked.
1466* Display/execute the alarm immediately for the user to check its configuration.
1467*/
1468void EditAlarmDlg::slotTry()
1469{
1470 TQString text;
1471 if (checkText(text))
1472 {
1473 if (mEmailRadio->isOn())
1474 {
1475 if (!checkEmailData()
1476 || KMessageBox::warningContinueCancel(this, i18n("Do you really want to send the email now to the specified recipient(s)?"),
1477 i18n("Confirm Email"), i18n("&Send")) != KMessageBox::Continue)
1478 return;
1479 }
1480 KAEvent event;
1481 setEvent(event, text, true);
1482 void* proc = theApp()->execAlarm(event, event.firstAlarm(), false, false);
1483 if (proc)
1484 {
1485 if (mCommandRadio->isOn() && mCmdOutputGroup->selectedId() != EXEC_IN_TERMINAL)
1486 {
1487 theApp()->commandMessage((ShellProcess*)proc, this);
1488 KMessageBox::information(this, i18n("Command executed:\n%1").arg(text));
1489 theApp()->commandMessage((ShellProcess*)proc, 0);
1490 }
1491 else if (mEmailRadio->isOn())
1492 {
1493 TQString bcc;
1494 if (mEmailBcc->isChecked())
1495 bcc = i18n("\nBcc: %1").arg(Preferences::emailBccAddress());
1496 KMessageBox::information(this, i18n("Email sent to:\n%1%2").arg(mEmailAddresses.join("\n")).arg(bcc));
1497 }
1498 }
1499 }
1500}
1501
1502/******************************************************************************
1503* Called when the Cancel button is clicked.
1504*/
1505void EditAlarmDlg::slotCancel()
1506{
1507 reject();
1508}
1509
1510/******************************************************************************
1511* Called when the Load Template button is clicked.
1512* Prompt to select a template and initialise the dialogue with its contents.
1513*/
1514void EditAlarmDlg::slotDefault()
1515{
1516 TemplatePickDlg dlg(this, "templPickDlg");
1517 if (dlg.exec() == TQDialog::Accepted)
1518 initialise(dlg.selectedTemplate());
1519}
1520
1521/******************************************************************************
1522 * Called when the Change deferral button is clicked.
1523 */
1524void EditAlarmDlg::slotEditDeferral()
1525{
1526 if (!mTimeWidget)
1527 return;
1528 bool limit = true;
1529 int repeatInterval;
1530 int repeatCount = mRecurrenceEdit->subRepeatCount(&repeatInterval);
1531 DateTime start = mSavedEvent->recurs() ? (mExpiredRecurrence ? DateTime() : mSavedEvent->mainDateTime())
1532 : mTimeWidget->getDateTime(0, !repeatCount, !mExpiredRecurrence);
1533 if (!start.isValid())
1534 {
1535 if (!mExpiredRecurrence)
1536 return;
1537 limit = false;
1538 }
1539 TQDateTime now = TQDateTime::currentDateTime();
1540 if (limit)
1541 {
1542 if (repeatCount && start < now)
1543 {
1544 // Sub-repetition - find the time of the next one
1545 repeatInterval *= 60;
1546 int repetition = (start.secsTo(now) + repeatInterval - 1) / repeatInterval;
1547 if (repetition > repeatCount)
1548 {
1549 mTimeWidget->getDateTime(); // output the appropriate error message
1550 return;
1551 }
1552 start = start.addSecs(repetition * repeatInterval);
1553 }
1554 }
1555
1556 bool deferred = mDeferDateTime.isValid();
1557 DeferAlarmDlg deferDlg(i18n("Defer Alarm"), (deferred ? mDeferDateTime : DateTime(now.addSecs(60))),
1558 deferred, this, "EditDeferDlg");
1559 if (limit)
1560 {
1561 // Don't allow deferral past the next recurrence
1562 int reminder = mReminder->minutes();
1563 if (reminder)
1564 {
1565 DateTime remindTime = start.addMins(-reminder);
1566 if (TQDateTime::currentDateTime() < remindTime)
1567 start = remindTime;
1568 }
1569 deferDlg.setLimit(start.addSecs(-60));
1570 }
1571 if (deferDlg.exec() == TQDialog::Accepted)
1572 {
1573 mDeferDateTime = deferDlg.getDateTime();
1574 mDeferTimeLabel->setText(mDeferDateTime.isValid() ? mDeferDateTime.formatLocale() : TQString());
1575 }
1576}
1577
1578/******************************************************************************
1579* Called when the main page is shown.
1580* Sets the focus widget to the first edit field.
1581*/
1582void EditAlarmDlg::slotShowMainPage()
1583{
1584 slotAlarmTypeChanged(-1);
1585 if (!mMainPageShown)
1586 {
1587 if (mTemplateName)
1588 mTemplateName->setFocus();
1589 mMainPageShown = true;
1590 }
1591 if (mTimeWidget)
1592 {
1593 if (!mReadOnly && mRecurPageShown && mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN)
1594 mTimeWidget->setDateTime(mRecurrenceEdit->endDateTime());
1595 if (mReadOnly || mRecurrenceEdit->isTimedRepeatType())
1596 mTimeWidget->setMinDateTime(); // don't set a minimum date/time
1597 else
1598 mTimeWidget->setMinDateTimeIsCurrent(); // set the minimum date/time to track the clock
1599 }
1600}
1601
1602/******************************************************************************
1603* Called when the recurrence edit page is shown.
1604* The recurrence defaults are set to correspond to the start date.
1605* The first time, for a new alarm, the recurrence end date is set according to
1606* the alarm start time.
1607*/
1608void EditAlarmDlg::slotShowRecurrenceEdit()
1609{
1610 mRecurPageIndex = mTabs->currentPageIndex();
1611 if (!mReadOnly && !mTemplate)
1612 {
1613 TQDateTime now = TQDateTime::currentDateTime();
1614 mAlarmDateTime = mTimeWidget->getDateTime(0, false, false);
1615 bool expired = (mAlarmDateTime.dateTime() < now);
1616 if (mRecurSetDefaultEndDate)
1617 {
1618 mRecurrenceEdit->setDefaultEndDate(expired ? now.date() : mAlarmDateTime.date());
1619 mRecurSetDefaultEndDate = false;
1620 }
1621 mRecurrenceEdit->setStartDate(mAlarmDateTime.date(), now.date());
1622 if (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN)
1623 mRecurrenceEdit->setEndDateTime(expired ? now : mAlarmDateTime);
1624 }
1625 mRecurPageShown = true;
1626}
1627
1628/******************************************************************************
1629* Called when the recurrence type selection changes.
1630* Enables/disables date-only alarms as appropriate.
1631* Enables/disables controls depending on at-login setting.
1632*/
1633void EditAlarmDlg::slotRecurTypeChange(int repeatType)
1634{
1635 bool atLogin = (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN);
1636 if (!mTemplate)
1637 {
1638 bool recurs = (mRecurrenceEdit->repeatType() != RecurrenceEdit::NO_RECUR);
1639 if (mDeferGroup)
1640 mDeferGroup->setEnabled(recurs);
1641 mTimeWidget->enableAnyTime(!recurs || repeatType != RecurrenceEdit::SUBDAILY);
1642 if (atLogin)
1643 {
1644 mAlarmDateTime = mTimeWidget->getDateTime(0, false, false);
1645 mRecurrenceEdit->setEndDateTime(mAlarmDateTime.dateTime());
1646 }
1647 mReminder->enableOnceOnly(recurs && !atLogin);
1648 }
1649 mReminder->setEnabled(!atLogin);
1650 mLateCancel->setEnabled(!atLogin);
1651 if (mShowInKorganizer)
1652 mShowInKorganizer->setEnabled(!atLogin);
1653 slotRecurFrequencyChange();
1654}
1655
1656/******************************************************************************
1657* Called when the recurrence frequency selection changes, or the sub-
1658* repetition interval changes.
1659* Updates the recurrence frequency text.
1660*/
1661void EditAlarmDlg::slotRecurFrequencyChange()
1662{
1663 slotSetSubRepetition();
1664 KAEvent event;
1665 mRecurrenceEdit->updateEvent(event, false);
1666 mTabs->setTabLabel(mTabs->page(mRecurPageIndex), recurText(event));
1667}
1668
1669/******************************************************************************
1670* Called when the Repetition within Recurrence button has been pressed to
1671* display the sub-repetition dialog.
1672* Alarm repetition has the following restrictions:
1673* 1) Not allowed for a repeat-at-login alarm
1674* 2) For a date-only alarm, the repeat interval must be a whole number of days.
1675* 3) The overall repeat duration must be less than the recurrence interval.
1676*/
1677void EditAlarmDlg::slotSetSubRepetition()
1678{
1679 bool dateOnly = mTemplate ? mTemplateAnyTime->isOn() : mTimeWidget->anyTime();
1680 mRecurrenceEdit->setSubRepetition(mReminder->minutes(), dateOnly);
1681}
1682
1683/******************************************************************************
1684* Validate and convert command alarm data.
1685*/
1686bool EditAlarmDlg::checkCommandData()
1687{
1688 if (mCommandRadio->isOn() && mCmdOutputGroup->selectedId() == LOG_TO_FILE)
1689 {
1690 // Validate the log file name
1691 TQString file = mCmdLogFileEdit->text();
1692 TQFileInfo info(file);
1693 TQDir::setCurrent(TQDir::homeDirPath());
1694 bool err = file.isEmpty() || info.isDir();
1695 if (!err)
1696 {
1697 if (info.exists())
1698 {
1699 err = !info.isWritable();
1700 }
1701 else
1702 {
1703 TQFileInfo dirinfo(info.dirPath(true)); // get absolute directory path
1704 err = (!dirinfo.isDir() || !dirinfo.isWritable());
1705 }
1706 }
1707 if (err)
1708 {
1709 mTabs->setCurrentPage(mMainPageIndex);
1710 mCmdLogFileEdit->setFocus();
1711 KMessageBox::sorry(this, i18n("Log file must be the name or path of a local file, with write permission."));
1712 return false;
1713 }
1714 // Convert the log file to an absolute path
1715 mCmdLogFileEdit->setText(info.absFilePath());
1716 }
1717 return true;
1718}
1719
1720/******************************************************************************
1721* Convert the email addresses to a list, and validate them. Convert the email
1722* attachments to a list.
1723*/
1724bool EditAlarmDlg::checkEmailData()
1725{
1726 if (mEmailRadio->isOn())
1727 {
1728 TQString addrs = mEmailToEdit->text();
1729 if (addrs.isEmpty())
1730 mEmailAddresses.clear();
1731 else
1732 {
1733 TQString bad = KAMail::convertAddresses(addrs, mEmailAddresses);
1734 if (!bad.isEmpty())
1735 {
1736 mEmailToEdit->setFocus();
1737 KMessageBox::error(this, i18n("Invalid email address:\n%1").arg(bad));
1738 return false;
1739 }
1740 }
1741 if (mEmailAddresses.isEmpty())
1742 {
1743 mEmailToEdit->setFocus();
1744 KMessageBox::error(this, i18n("No email address specified"));
1745 return false;
1746 }
1747
1748 mEmailAttachments.clear();
1749 for (int i = 0; i < mEmailAttachList->count(); ++i)
1750 {
1751 TQString att = mEmailAttachList->text(i);
1752 switch (KAMail::checkAttachment(att))
1753 {
1754 case 1:
1755 mEmailAttachments.append(att);
1756 break;
1757 case 0:
1758 break; // empty
1759 case -1:
1760 mEmailAttachList->setFocus();
1761 KMessageBox::error(this, i18n("Invalid email attachment:\n%1").arg(att));
1762 return false;
1763 }
1764 }
1765 }
1766 return true;
1767}
1768
1769/******************************************************************************
1770* Called when one of the alarm action type radio buttons is clicked,
1771* to display the appropriate set of controls for that action type.
1772*/
1773void EditAlarmDlg::slotAlarmTypeChanged(int)
1774{
1775 bool displayAlarm = false;
1776 TQWidget* focus = 0;
1777 if (mMessageRadio->isOn())
1778 {
1779 mFileBox->hide();
1780 mFilePadding->hide();
1781 mTextMessageEdit->show();
1782 mFontColourButton->show();
1783 mBgColourBox->hide();
1784 mSoundPicker->showSpeak(true);
1785 mDisplayAlarmsFrame->show();
1786 mCommandFrame->hide();
1787 mEmailFrame->hide();
1788 mReminder->show();
1789 mConfirmAck->show();
1790 setButtonWhatsThis(Try, i18n("Display the alarm message now"));
1791 focus = mTextMessageEdit;
1792 displayAlarm = true;
1793 }
1794 else if (mFileRadio->isOn())
1795 {
1796 mTextMessageEdit->hide();
1797 mFileBox->show();
1798 mFilePadding->show();
1799 mFontColourButton->hide();
1800 mBgColourBox->show();
1801 mSoundPicker->showSpeak(false);
1802 mDisplayAlarmsFrame->show();
1803 mCommandFrame->hide();
1804 mEmailFrame->hide();
1805 mReminder->show();
1806 mConfirmAck->show();
1807 setButtonWhatsThis(Try, i18n("Display the file now"));
1808 mFileMessageEdit->setNoSelect();
1809 focus = mFileMessageEdit;
1810 displayAlarm = true;
1811 }
1812 else if (mCommandRadio->isOn())
1813 {
1814 mDisplayAlarmsFrame->hide();
1815 mCommandFrame->show();
1816 mEmailFrame->hide();
1817 mReminder->hide();
1818 mConfirmAck->hide();
1819 setButtonWhatsThis(Try, i18n("Execute the specified command now"));
1820 mCmdCommandEdit->setNoSelect();
1821 focus = mCmdCommandEdit;
1822 }
1823 else if (mEmailRadio->isOn())
1824 {
1825 mDisplayAlarmsFrame->hide();
1826 mCommandFrame->hide();
1827 mEmailFrame->show();
1828 mReminder->hide();
1829 mConfirmAck->hide();
1830 setButtonWhatsThis(Try, i18n("Send the email to the specified addressees now"));
1831 mEmailToEdit->setNoSelect();
1832 focus = mEmailToEdit;
1833 }
1834 mLateCancel->showAutoClose(displayAlarm);
1835 mLateCancel->setFixedSize(mLateCancel->sizeHint());
1836 if (focus)
1837 focus->setFocus();
1838}
1839
1840/******************************************************************************
1841* Called when one of the command type radio buttons is clicked,
1842* to display the appropriate edit field.
1843*/
1844void EditAlarmDlg::slotCmdScriptToggled(bool on)
1845{
1846 if (on)
1847 {
1848 mCmdCommandEdit->hide();
1849 mCmdPadding->hide();
1850 mCmdScriptEdit->show();
1851 mCmdScriptEdit->setFocus();
1852 }
1853 else
1854 {
1855 mCmdScriptEdit->hide();
1856 mCmdCommandEdit->show();
1857 mCmdPadding->show();
1858 mCmdCommandEdit->setFocus();
1859 }
1860}
1861
1862/******************************************************************************
1863* Called when one of the template time radio buttons is clicked,
1864* to enable or disable the template time entry spin boxes.
1865*/
1866void EditAlarmDlg::slotTemplateTimeType(int)
1867{
1868 mTemplateTime->setEnabled(mTemplateUseTime->isOn());
1869 mTemplateTimeAfter->setEnabled(mTemplateUseTimeAfter->isOn());
1870}
1871
1872/******************************************************************************
1873* Called when the "Any time" checkbox is toggled in the date/time widget.
1874* Sets the advance reminder and late cancel units to days if any time is checked.
1875*/
1876void EditAlarmDlg::slotAnyTimeToggled(bool anyTime)
1877{
1878 if (mReminder->isReminder())
1879 mReminder->setDateOnly(anyTime);
1880 mLateCancel->setDateOnly(anyTime);
1881}
1882
1883/******************************************************************************
1884 * Get a selection from the Address Book.
1885 */
1886void EditAlarmDlg::openAddressBook()
1887{
1888 TDEABC::Addressee a = TDEABC::AddresseeDialog::getAddressee(this);
1889 if (a.isEmpty())
1890 return;
1891 Person person(a.realName(), a.preferredEmail());
1892 TQString addrs = mEmailToEdit->text().stripWhiteSpace();
1893 if (!addrs.isEmpty())
1894 addrs += ", ";
1895 addrs += person.fullName();
1896 mEmailToEdit->setText(addrs);
1897}
1898
1899/******************************************************************************
1900 * Select a file to attach to the email.
1901 */
1902void EditAlarmDlg::slotAddAttachment()
1903{
1904 TQString url = KAlarm::browseFile(i18n("Choose File to Attach"), mAttachDefaultDir, TQString(),
1905 TQString(), KFile::ExistingOnly, this, "pickAttachFile");
1906 if (!url.isEmpty())
1907 {
1908 mEmailAttachList->insertItem(url);
1909 mEmailAttachList->setCurrentItem(mEmailAttachList->count() - 1); // select the new item
1910 mEmailRemoveButton->setEnabled(true);
1911 mEmailAttachList->setEnabled(true);
1912 }
1913}
1914
1915/******************************************************************************
1916 * Remove the currently selected attachment from the email.
1917 */
1918void EditAlarmDlg::slotRemoveAttachment()
1919{
1920 int item = mEmailAttachList->currentItem();
1921 mEmailAttachList->removeItem(item);
1922 int count = mEmailAttachList->count();
1923 if (item >= count)
1924 mEmailAttachList->setCurrentItem(count - 1);
1925 if (!count)
1926 {
1927 mEmailRemoveButton->setEnabled(false);
1928 mEmailAttachList->setEnabled(false);
1929 }
1930}
1931
1932/******************************************************************************
1933* Clean up the alarm text, and if it's a file, check whether it's valid.
1934*/
1935bool EditAlarmDlg::checkText(TQString& result, bool showErrorMessage) const
1936{
1937 if (mMessageRadio->isOn())
1938 result = mTextMessageEdit->text();
1939 else if (mEmailRadio->isOn())
1940 result = mEmailMessageEdit->text();
1941 else if (mCommandRadio->isOn())
1942 {
1943 if (mCmdTypeScript->isChecked())
1944 result = mCmdScriptEdit->text();
1945 else
1946 result = mCmdCommandEdit->text();
1947 result = result.stripWhiteSpace();
1948 }
1949 else if (mFileRadio->isOn())
1950 {
1951 TQString alarmtext = mFileMessageEdit->text().stripWhiteSpace();
1952 // Convert any relative file path to absolute
1953 // (using home directory as the default)
1954 enum Err { NONE = 0, BLANK, NONEXISTENT, DIRECTORY, UNREADABLE, NOT_TEXT_IMAGE };
1955 Err err = NONE;
1956 KURL url;
1957 int i = alarmtext.find(TQString::fromLatin1("/"));
1958 if (i > 0 && alarmtext[i - 1] == ':')
1959 {
1960 url = alarmtext;
1961 url.cleanPath();
1962 alarmtext = url.prettyURL();
1963 TDEIO::UDSEntry uds;
1964 if (!TDEIO::NetAccess::stat(url, uds, MainWindow::mainMainWindow()))
1965 err = NONEXISTENT;
1966 else
1967 {
1968 KFileItem fi(uds, url);
1969 if (fi.isDir()) err = DIRECTORY;
1970 else if (!fi.isReadable()) err = UNREADABLE;
1971 }
1972 }
1973 else if (alarmtext.isEmpty())
1974 err = BLANK; // blank file name
1975 else
1976 {
1977 // It's a local file - convert to absolute path & check validity
1978 TQFileInfo info(alarmtext);
1979 TQDir::setCurrent(TQDir::homeDirPath());
1980 alarmtext = info.absFilePath();
1981 url.setPath(alarmtext);
1982 alarmtext = TQString::fromLatin1("file:") + alarmtext;
1983 if (!err)
1984 {
1985 if (info.isDir()) err = DIRECTORY;
1986 else if (!info.exists()) err = NONEXISTENT;
1987 else if (!info.isReadable()) err = UNREADABLE;
1988 }
1989 }
1990 if (!err)
1991 {
1992 switch (KAlarm::fileType(KFileItem(KFileItem::Unknown, KFileItem::Unknown, url).mimetype()))
1993 {
1994 case KAlarm::TextFormatted:
1995 case KAlarm::TextPlain:
1996 case KAlarm::TextApplication:
1997 case KAlarm::Image:
1998 break;
1999 default:
2000 err = NOT_TEXT_IMAGE;
2001 break;
2002 }
2003 }
2004 if (err && showErrorMessage)
2005 {
2006 mFileMessageEdit->setFocus();
2007 TQString errmsg;
2008 switch (err)
2009 {
2010 case BLANK:
2011 KMessageBox::sorry(const_cast<EditAlarmDlg*>(this), i18n("Please select a file to display"));
2012 return false;
2013 case NONEXISTENT: errmsg = i18n("%1\nnot found"); break;
2014 case DIRECTORY: errmsg = i18n("%1\nis a folder"); break;
2015 case UNREADABLE: errmsg = i18n("%1\nis not readable"); break;
2016 case NOT_TEXT_IMAGE: errmsg = i18n("%1\nappears not to be a text or image file"); break;
2017 case NONE:
2018 default:
2019 break;
2020 }
2021 if (KMessageBox::warningContinueCancel(const_cast<EditAlarmDlg*>(this), errmsg.arg(alarmtext))
2022 == KMessageBox::Cancel)
2023 return false;
2024 }
2025 result = alarmtext;
2026 }
2027 return true;
2028}
2029
2030
2031/*=============================================================================
2032= Class TextEdit
2033= A text edit field with a minimum height of 3 text lines.
2034= Provides KDE 2 compatibility.
2035=============================================================================*/
2036TextEdit::TextEdit(TQWidget* parent, const char* name)
2037 : KTextEdit(parent, name)
2038{
2039 TQSize tsize = sizeHint();
2040 tsize.setHeight(fontMetrics().lineSpacing()*13/4 + 2*frameWidth());
2041 setMinimumSize(tsize);
2042}
2043
2044void TextEdit::dragEnterEvent(TQDragEnterEvent* e)
2045{
2047 e->accept(false); // don't accept "text/calendar" objects
2048 KTextEdit::dragEnterEvent(e);
2049}
Provides read and write access to calendar files.
Definition: alarmcalendar.h:37
KAEvent corresponds to a KCal::Event instance.
Definition: alarmevent.h:232
static bool canDecode(TQMimeSource *)
Radio button with associated file picker controls.
Definition: pickfileradio.h:51
virtual TQString pickFile()=0
Chooses a file, for example by displaying a file selection dialogue.
LineEdit * fileEdit() const
Returns the associated file name edit widget, or null if none.
Definition: pickfileradio.h:95
miscellaneous functions
the KAlarm application object
main application window
radio button with an associated file picker