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 <kstandarddirs.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 
89 using namespace KCal;
90 
91 static const char EDIT_DIALOG_NAME[] = "EditDialog";
92 static const int maxDelayTime = 99*60 + 59; // < 100 hours
93 
94 /*=============================================================================
95 = Class PickAlarmFileRadio
96 =============================================================================*/
97 class 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 =============================================================================*/
114 class 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 
128 inline 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.
140 TQString EditAlarmDlg::i18n_ConfirmAck() { return i18n("Confirm acknowledgment"); }
141 TQString EditAlarmDlg::i18n_k_ConfirmAck() { return i18n("Confirm ac&knowledgment"); }
142 TQString EditAlarmDlg::i18n_SpecialActions() { return i18n("Special Actions..."); }
143 TQString EditAlarmDlg::i18n_ShowInKOrganizer() { return i18n("Show in KOrganizer"); }
144 TQString EditAlarmDlg::i18n_g_ShowInKOrganizer() { return i18n("Show in KOr&ganizer"); }
145 TQString EditAlarmDlg::i18n_EnterScript() { return i18n("Enter a script"); }
146 TQString EditAlarmDlg::i18n_p_EnterScript() { return i18n("Enter a scri&pt"); }
147 TQString EditAlarmDlg::i18n_ExecInTermWindow() { return i18n("Execute in terminal window"); }
148 TQString EditAlarmDlg::i18n_w_ExecInTermWindow() { return i18n("Execute in terminal &window"); }
149 TQString EditAlarmDlg::i18n_u_ExecInTermWindow() { return i18n("Exec&ute in terminal window"); }
150 TQString EditAlarmDlg::i18n_g_LogToFile() { return i18n("Lo&g to file"); }
151 TQString EditAlarmDlg::i18n_CopyEmailToSelf() { return i18n("Copy email to self"); }
152 TQString EditAlarmDlg::i18n_e_CopyEmailToSelf() { return i18n("Copy &email to self"); }
153 TQString EditAlarmDlg::i18n_s_CopyEmailToSelf() { return i18n("Copy email to &self"); }
154 TQString EditAlarmDlg::i18n_EmailFrom() { return i18n("'From' email address", "From:"); }
155 TQString EditAlarmDlg::i18n_f_EmailFrom() { return i18n("'From' email address", "&From:"); }
156 TQString EditAlarmDlg::i18n_EmailTo() { return i18n("Email addressee", "To:"); }
157 TQString EditAlarmDlg::i18n_EmailSubject() { return i18n("Email subject", "Subject:"); }
158 TQString 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  */
168 EditAlarmDlg::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, TQT_SIGNAL(shown()), TQT_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, TQT_SIGNAL(shown()), TQT_SLOT(slotShowRecurrenceEdit()));
224  connect(mRecurrenceEdit, TQT_SIGNAL(typeChanged(int)), TQT_SLOT(slotRecurTypeChange(int)));
225  connect(mRecurrenceEdit, TQT_SIGNAL(frequencyChanged()), TQT_SLOT(slotRecurFrequencyChange()));
226  connect(mRecurrenceEdit, TQT_SIGNAL(repeatNeedsInitialisation()), TQT_SLOT(slotSetSubRepetition()));
227 
228  // Alarm action
229 
230  mActionGroup = new ButtonGroup(i18n("Action"), mainPage, "actionGroup");
231  connect(mActionGroup, TQT_SIGNAL(buttonSet(int)), TQT_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, Qt::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, TQT_SIGNAL(clicked()), TQT_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, TQT_SIGNAL(buttonSet(int)), TQT_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, TQT_SIGNAL(anyTimeToggled(bool)), TQT_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 
409 EditAlarmDlg::~EditAlarmDlg()
410 {
411  delete mSavedEvent;
412 }
413 
414 /******************************************************************************
415  * Set up the dialog controls common to display alarms.
416  */
417 void 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  */
484 void 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, TQT_SIGNAL(toggled(bool)), TQT_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  */
559 void 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, TQT_SIGNAL(clicked()), TQT_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);
628 TQListBox* list = mEmailAttachList->listBox();
629 TQRect rect = list->geometry();
630 list->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, TQT_SIGNAL(clicked()), TQT_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, TQT_SIGNAL(clicked()), TQT_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  */
658 void 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  */
854 void 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  */
923 void 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  */
975 CheckBox* 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  */
986 void 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  */
1043 bool 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  */
1137 bool 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 */
1165 void 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  */
1257 int 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  */
1279 KAEvent::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 */
1292 void 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 */
1313 void 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 */
1328 void 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()
1378  || !checkEmailData())
1379  return;
1380  if (!mTemplate)
1381  {
1382  if (timedRecurrence)
1383  {
1384  TQDateTime now = TQDateTime::currentDateTime();
1385  if (mAlarmDateTime.date() < now.date()
1386  || (mAlarmDateTime.date() == now.date()
1387  && !mAlarmDateTime.isDateOnly() && mAlarmDateTime.time() < now.time()))
1388  {
1389  // A timed recurrence has an entered start date which
1390  // has already expired, so we must adjust it.
1391  KAEvent event;
1392  getEvent(event); // this may adjust mAlarmDateTime
1393  if (( mAlarmDateTime.date() < now.date()
1394  || (mAlarmDateTime.date() == now.date()
1395  && !mAlarmDateTime.isDateOnly() && mAlarmDateTime.time() < now.time()))
1396  && event.nextOccurrence(now, mAlarmDateTime, KAEvent::ALLOW_FOR_REPETITION) == KAEvent::NO_OCCURRENCE)
1397  {
1398  KMessageBox::sorry(this, i18n("Recurrence has already expired"));
1399  return;
1400  }
1401  }
1402  }
1403  TQString errmsg;
1404  TQWidget* errWidget = mRecurrenceEdit->checkData(mAlarmDateTime.dateTime(), errmsg);
1405  if (errWidget)
1406  {
1407  mTabs->setCurrentPage(mRecurPageIndex);
1408  errWidget->setFocus();
1409  KMessageBox::sorry(this, errmsg);
1410  return;
1411  }
1412  }
1413  if (recurType != RecurrenceEdit::NO_RECUR)
1414  {
1415  KAEvent recurEvent;
1416  int longestRecurInterval = -1;
1417  int reminder = mReminder->minutes();
1418  if (reminder && !mReminder->isOnceOnly())
1419  {
1420  mRecurrenceEdit->updateEvent(recurEvent, false);
1421  longestRecurInterval = recurEvent.longestRecurrenceInterval();
1422  if (longestRecurInterval && reminder >= longestRecurInterval)
1423  {
1424  mTabs->setCurrentPage(mMainPageIndex);
1425  mReminder->setFocusOnCount();
1426  KMessageBox::sorry(this, i18n("Reminder period must be less than the recurrence interval, unless '%1' is checked."
1427  ).arg(Reminder::i18n_first_recurrence_only()));
1428  return;
1429  }
1430  }
1431  if (mRecurrenceEdit->subRepeatCount())
1432  {
1433  if (longestRecurInterval < 0)
1434  {
1435  mRecurrenceEdit->updateEvent(recurEvent, false);
1436  longestRecurInterval = recurEvent.longestRecurrenceInterval();
1437  }
1438  if (longestRecurInterval > 0
1439  && recurEvent.repeatInterval() * recurEvent.repeatCount() >= longestRecurInterval - reminder)
1440  {
1441  KMessageBox::sorry(this, i18n("The duration of a repetition within the recurrence must be less than the recurrence interval minus any reminder period"));
1442  mRecurrenceEdit->activateSubRepetition(); // display the alarm repetition dialog again
1443  return;
1444  }
1445  if (recurEvent.repeatInterval() % 1440
1446  && ((mTemplate && mTemplateAnyTime->isOn()) || (!mTemplate && mAlarmDateTime.isDateOnly())))
1447  {
1448  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"));
1449  mRecurrenceEdit->activateSubRepetition(); // display the alarm repetition dialog again
1450  return;
1451  }
1452  }
1453  }
1454  if (checkText(mAlarmMessage))
1455  accept();
1456 }
1457 
1458 /******************************************************************************
1459 * Called when the Try button is clicked.
1460 * Display/execute the alarm immediately for the user to check its configuration.
1461 */
1462 void EditAlarmDlg::slotTry()
1463 {
1464  TQString text;
1465  if (checkText(text))
1466  {
1467  if (mEmailRadio->isOn())
1468  {
1469  if (!checkEmailData()
1470  || KMessageBox::warningContinueCancel(this, i18n("Do you really want to send the email now to the specified recipient(s)?"),
1471  i18n("Confirm Email"), i18n("&Send")) != KMessageBox::Continue)
1472  return;
1473  }
1474  KAEvent event;
1475  setEvent(event, text, true);
1476  void* proc = theApp()->execAlarm(event, event.firstAlarm(), false, false);
1477  if (proc)
1478  {
1479  if (mCommandRadio->isOn() && mCmdOutputGroup->selectedId() != EXEC_IN_TERMINAL)
1480  {
1481  theApp()->commandMessage((ShellProcess*)proc, this);
1482  KMessageBox::information(this, i18n("Command executed:\n%1").arg(text));
1483  theApp()->commandMessage((ShellProcess*)proc, 0);
1484  }
1485  else if (mEmailRadio->isOn())
1486  {
1487  TQString bcc;
1488  if (mEmailBcc->isChecked())
1489  bcc = i18n("\nBcc: %1").arg(Preferences::emailBccAddress());
1490  KMessageBox::information(this, i18n("Email sent to:\n%1%2").arg(mEmailAddresses.join("\n")).arg(bcc));
1491  }
1492  }
1493  }
1494 }
1495 
1496 /******************************************************************************
1497 * Called when the Cancel button is clicked.
1498 */
1499 void EditAlarmDlg::slotCancel()
1500 {
1501  reject();
1502 }
1503 
1504 /******************************************************************************
1505 * Called when the Load Template button is clicked.
1506 * Prompt to select a template and initialise the dialogue with its contents.
1507 */
1508 void EditAlarmDlg::slotDefault()
1509 {
1510  TemplatePickDlg dlg(this, "templPickDlg");
1511  if (dlg.exec() == TQDialog::Accepted)
1512  initialise(dlg.selectedTemplate());
1513 }
1514 
1515 /******************************************************************************
1516  * Called when the Change deferral button is clicked.
1517  */
1518 void EditAlarmDlg::slotEditDeferral()
1519 {
1520  if (!mTimeWidget)
1521  return;
1522  bool limit = true;
1523  int repeatInterval;
1524  int repeatCount = mRecurrenceEdit->subRepeatCount(&repeatInterval);
1525  DateTime start = mSavedEvent->recurs() ? (mExpiredRecurrence ? DateTime() : mSavedEvent->mainDateTime())
1526  : mTimeWidget->getDateTime(0, !repeatCount, !mExpiredRecurrence);
1527  if (!start.isValid())
1528  {
1529  if (!mExpiredRecurrence)
1530  return;
1531  limit = false;
1532  }
1533  TQDateTime now = TQDateTime::currentDateTime();
1534  if (limit)
1535  {
1536  if (repeatCount && start < now)
1537  {
1538  // Sub-repetition - find the time of the next one
1539  repeatInterval *= 60;
1540  int repetition = (start.secsTo(now) + repeatInterval - 1) / repeatInterval;
1541  if (repetition > repeatCount)
1542  {
1543  mTimeWidget->getDateTime(); // output the appropriate error message
1544  return;
1545  }
1546  start = start.addSecs(repetition * repeatInterval);
1547  }
1548  }
1549 
1550  bool deferred = mDeferDateTime.isValid();
1551  DeferAlarmDlg deferDlg(i18n("Defer Alarm"), (deferred ? mDeferDateTime : DateTime(now.addSecs(60))),
1552  deferred, this, "EditDeferDlg");
1553  if (limit)
1554  {
1555  // Don't allow deferral past the next recurrence
1556  int reminder = mReminder->minutes();
1557  if (reminder)
1558  {
1559  DateTime remindTime = start.addMins(-reminder);
1560  if (TQDateTime::currentDateTime() < remindTime)
1561  start = remindTime;
1562  }
1563  deferDlg.setLimit(start.addSecs(-60));
1564  }
1565  if (deferDlg.exec() == TQDialog::Accepted)
1566  {
1567  mDeferDateTime = deferDlg.getDateTime();
1568  mDeferTimeLabel->setText(mDeferDateTime.isValid() ? mDeferDateTime.formatLocale() : TQString());
1569  }
1570 }
1571 
1572 /******************************************************************************
1573 * Called when the main page is shown.
1574 * Sets the focus widget to the first edit field.
1575 */
1576 void EditAlarmDlg::slotShowMainPage()
1577 {
1578  slotAlarmTypeChanged(-1);
1579  if (!mMainPageShown)
1580  {
1581  if (mTemplateName)
1582  mTemplateName->setFocus();
1583  mMainPageShown = true;
1584  }
1585  if (mTimeWidget)
1586  {
1587  if (!mReadOnly && mRecurPageShown && mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN)
1588  mTimeWidget->setDateTime(mRecurrenceEdit->endDateTime());
1589  if (mReadOnly || mRecurrenceEdit->isTimedRepeatType())
1590  mTimeWidget->setMinDateTime(); // don't set a minimum date/time
1591  else
1592  mTimeWidget->setMinDateTimeIsCurrent(); // set the minimum date/time to track the clock
1593  }
1594 }
1595 
1596 /******************************************************************************
1597 * Called when the recurrence edit page is shown.
1598 * The recurrence defaults are set to correspond to the start date.
1599 * The first time, for a new alarm, the recurrence end date is set according to
1600 * the alarm start time.
1601 */
1602 void EditAlarmDlg::slotShowRecurrenceEdit()
1603 {
1604  mRecurPageIndex = mTabs->currentPageIndex();
1605  if (!mReadOnly && !mTemplate)
1606  {
1607  TQDateTime now = TQDateTime::currentDateTime();
1608  mAlarmDateTime = mTimeWidget->getDateTime(0, false, false);
1609  bool expired = (mAlarmDateTime.dateTime() < now);
1610  if (mRecurSetDefaultEndDate)
1611  {
1612  mRecurrenceEdit->setDefaultEndDate(expired ? now.date() : mAlarmDateTime.date());
1613  mRecurSetDefaultEndDate = false;
1614  }
1615  mRecurrenceEdit->setStartDate(mAlarmDateTime.date(), now.date());
1616  if (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN)
1617  mRecurrenceEdit->setEndDateTime(expired ? now : mAlarmDateTime);
1618  }
1619  mRecurPageShown = true;
1620 }
1621 
1622 /******************************************************************************
1623 * Called when the recurrence type selection changes.
1624 * Enables/disables date-only alarms as appropriate.
1625 * Enables/disables controls depending on at-login setting.
1626 */
1627 void EditAlarmDlg::slotRecurTypeChange(int repeatType)
1628 {
1629  bool atLogin = (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN);
1630  if (!mTemplate)
1631  {
1632  bool recurs = (mRecurrenceEdit->repeatType() != RecurrenceEdit::NO_RECUR);
1633  if (mDeferGroup)
1634  mDeferGroup->setEnabled(recurs);
1635  mTimeWidget->enableAnyTime(!recurs || repeatType != RecurrenceEdit::SUBDAILY);
1636  if (atLogin)
1637  {
1638  mAlarmDateTime = mTimeWidget->getDateTime(0, false, false);
1639  mRecurrenceEdit->setEndDateTime(mAlarmDateTime.dateTime());
1640  }
1641  mReminder->enableOnceOnly(recurs && !atLogin);
1642  }
1643  mReminder->setEnabled(!atLogin);
1644  mLateCancel->setEnabled(!atLogin);
1645  if (mShowInKorganizer)
1646  mShowInKorganizer->setEnabled(!atLogin);
1647  slotRecurFrequencyChange();
1648 }
1649 
1650 /******************************************************************************
1651 * Called when the recurrence frequency selection changes, or the sub-
1652 * repetition interval changes.
1653 * Updates the recurrence frequency text.
1654 */
1655 void EditAlarmDlg::slotRecurFrequencyChange()
1656 {
1657  slotSetSubRepetition();
1658  KAEvent event;
1659  mRecurrenceEdit->updateEvent(event, false);
1660  mTabs->setTabLabel(mTabs->page(mRecurPageIndex), recurText(event));
1661 }
1662 
1663 /******************************************************************************
1664 * Called when the Repetition within Recurrence button has been pressed to
1665 * display the sub-repetition dialog.
1666 * Alarm repetition has the following restrictions:
1667 * 1) Not allowed for a repeat-at-login alarm
1668 * 2) For a date-only alarm, the repeat interval must be a whole number of days.
1669 * 3) The overall repeat duration must be less than the recurrence interval.
1670 */
1671 void EditAlarmDlg::slotSetSubRepetition()
1672 {
1673  bool dateOnly = mTemplate ? mTemplateAnyTime->isOn() : mTimeWidget->anyTime();
1674  mRecurrenceEdit->setSubRepetition(mReminder->minutes(), dateOnly);
1675 }
1676 
1677 /******************************************************************************
1678 * Validate and convert command alarm data.
1679 */
1680 bool EditAlarmDlg::checkCommandData()
1681 {
1682  if (mCommandRadio->isOn() && mCmdOutputGroup->selectedId() == LOG_TO_FILE)
1683  {
1684  // Validate the log file name
1685  TQString file = mCmdLogFileEdit->text();
1686  TQFileInfo info(file);
1687  TQDir::setCurrent(TQDir::homeDirPath());
1688  bool err = file.isEmpty() || info.isDir();
1689  if (!err)
1690  {
1691  if (info.exists())
1692  {
1693  err = !info.isWritable();
1694  }
1695  else
1696  {
1697  TQFileInfo dirinfo(info.dirPath(true)); // get absolute directory path
1698  err = (!dirinfo.isDir() || !dirinfo.isWritable());
1699  }
1700  }
1701  if (err)
1702  {
1703  mTabs->setCurrentPage(mMainPageIndex);
1704  mCmdLogFileEdit->setFocus();
1705  KMessageBox::sorry(this, i18n("Log file must be the name or path of a local file, with write permission."));
1706  return false;
1707  }
1708  // Convert the log file to an absolute path
1709  mCmdLogFileEdit->setText(info.absFilePath());
1710  }
1711  return true;
1712 }
1713 
1714 /******************************************************************************
1715 * Convert the email addresses to a list, and validate them. Convert the email
1716 * attachments to a list.
1717 */
1718 bool EditAlarmDlg::checkEmailData()
1719 {
1720  if (mEmailRadio->isOn())
1721  {
1722  TQString addrs = mEmailToEdit->text();
1723  if (addrs.isEmpty())
1724  mEmailAddresses.clear();
1725  else
1726  {
1727  TQString bad = KAMail::convertAddresses(addrs, mEmailAddresses);
1728  if (!bad.isEmpty())
1729  {
1730  mEmailToEdit->setFocus();
1731  KMessageBox::error(this, i18n("Invalid email address:\n%1").arg(bad));
1732  return false;
1733  }
1734  }
1735  if (mEmailAddresses.isEmpty())
1736  {
1737  mEmailToEdit->setFocus();
1738  KMessageBox::error(this, i18n("No email address specified"));
1739  return false;
1740  }
1741 
1742  mEmailAttachments.clear();
1743  for (int i = 0; i < mEmailAttachList->count(); ++i)
1744  {
1745  TQString att = mEmailAttachList->text(i);
1746  switch (KAMail::checkAttachment(att))
1747  {
1748  case 1:
1749  mEmailAttachments.append(att);
1750  break;
1751  case 0:
1752  break; // empty
1753  case -1:
1754  mEmailAttachList->setFocus();
1755  KMessageBox::error(this, i18n("Invalid email attachment:\n%1").arg(att));
1756  return false;
1757  }
1758  }
1759  }
1760  return true;
1761 }
1762 
1763 /******************************************************************************
1764 * Called when one of the alarm action type radio buttons is clicked,
1765 * to display the appropriate set of controls for that action type.
1766 */
1767 void EditAlarmDlg::slotAlarmTypeChanged(int)
1768 {
1769  bool displayAlarm = false;
1770  TQWidget* focus = 0;
1771  if (mMessageRadio->isOn())
1772  {
1773  mFileBox->hide();
1774  mFilePadding->hide();
1775  mTextMessageEdit->show();
1776  mFontColourButton->show();
1777  mBgColourBox->hide();
1778  mSoundPicker->showSpeak(true);
1779  mDisplayAlarmsFrame->show();
1780  mCommandFrame->hide();
1781  mEmailFrame->hide();
1782  mReminder->show();
1783  mConfirmAck->show();
1784  setButtonWhatsThis(Try, i18n("Display the alarm message now"));
1785  focus = mTextMessageEdit;
1786  displayAlarm = true;
1787  }
1788  else if (mFileRadio->isOn())
1789  {
1790  mTextMessageEdit->hide();
1791  mFileBox->show();
1792  mFilePadding->show();
1793  mFontColourButton->hide();
1794  mBgColourBox->show();
1795  mSoundPicker->showSpeak(false);
1796  mDisplayAlarmsFrame->show();
1797  mCommandFrame->hide();
1798  mEmailFrame->hide();
1799  mReminder->show();
1800  mConfirmAck->show();
1801  setButtonWhatsThis(Try, i18n("Display the file now"));
1802  mFileMessageEdit->setNoSelect();
1803  focus = mFileMessageEdit;
1804  displayAlarm = true;
1805  }
1806  else if (mCommandRadio->isOn())
1807  {
1808  mDisplayAlarmsFrame->hide();
1809  mCommandFrame->show();
1810  mEmailFrame->hide();
1811  mReminder->hide();
1812  mConfirmAck->hide();
1813  setButtonWhatsThis(Try, i18n("Execute the specified command now"));
1814  mCmdCommandEdit->setNoSelect();
1815  focus = mCmdCommandEdit;
1816  }
1817  else if (mEmailRadio->isOn())
1818  {
1819  mDisplayAlarmsFrame->hide();
1820  mCommandFrame->hide();
1821  mEmailFrame->show();
1822  mReminder->hide();
1823  mConfirmAck->hide();
1824  setButtonWhatsThis(Try, i18n("Send the email to the specified addressees now"));
1825  mEmailToEdit->setNoSelect();
1826  focus = mEmailToEdit;
1827  }
1828  mLateCancel->showAutoClose(displayAlarm);
1829  mLateCancel->setFixedSize(mLateCancel->sizeHint());
1830  if (focus)
1831  focus->setFocus();
1832 }
1833 
1834 /******************************************************************************
1835 * Called when one of the command type radio buttons is clicked,
1836 * to display the appropriate edit field.
1837 */
1838 void EditAlarmDlg::slotCmdScriptToggled(bool on)
1839 {
1840  if (on)
1841  {
1842  mCmdCommandEdit->hide();
1843  mCmdPadding->hide();
1844  mCmdScriptEdit->show();
1845  mCmdScriptEdit->setFocus();
1846  }
1847  else
1848  {
1849  mCmdScriptEdit->hide();
1850  mCmdCommandEdit->show();
1851  mCmdPadding->show();
1852  mCmdCommandEdit->setFocus();
1853  }
1854 }
1855 
1856 /******************************************************************************
1857 * Called when one of the template time radio buttons is clicked,
1858 * to enable or disable the template time entry spin boxes.
1859 */
1860 void EditAlarmDlg::slotTemplateTimeType(int)
1861 {
1862  mTemplateTime->setEnabled(mTemplateUseTime->isOn());
1863  mTemplateTimeAfter->setEnabled(mTemplateUseTimeAfter->isOn());
1864 }
1865 
1866 /******************************************************************************
1867 * Called when the "Any time" checkbox is toggled in the date/time widget.
1868 * Sets the advance reminder and late cancel units to days if any time is checked.
1869 */
1870 void EditAlarmDlg::slotAnyTimeToggled(bool anyTime)
1871 {
1872  if (mReminder->isReminder())
1873  mReminder->setDateOnly(anyTime);
1874  mLateCancel->setDateOnly(anyTime);
1875 }
1876 
1877 /******************************************************************************
1878  * Get a selection from the Address Book.
1879  */
1880 void EditAlarmDlg::openAddressBook()
1881 {
1882  TDEABC::Addressee a = TDEABC::AddresseeDialog::getAddressee(this);
1883  if (a.isEmpty())
1884  return;
1885  Person person(a.realName(), a.preferredEmail());
1886  TQString addrs = mEmailToEdit->text().stripWhiteSpace();
1887  if (!addrs.isEmpty())
1888  addrs += ", ";
1889  addrs += person.fullName();
1890  mEmailToEdit->setText(addrs);
1891 }
1892 
1893 /******************************************************************************
1894  * Select a file to attach to the email.
1895  */
1896 void EditAlarmDlg::slotAddAttachment()
1897 {
1898  TQString url = KAlarm::browseFile(i18n("Choose File to Attach"), mAttachDefaultDir, TQString(),
1899  TQString(), KFile::ExistingOnly, this, "pickAttachFile");
1900  if (!url.isEmpty())
1901  {
1902  mEmailAttachList->insertItem(url);
1903  mEmailAttachList->setCurrentItem(mEmailAttachList->count() - 1); // select the new item
1904  mEmailRemoveButton->setEnabled(true);
1905  mEmailAttachList->setEnabled(true);
1906  }
1907 }
1908 
1909 /******************************************************************************
1910  * Remove the currently selected attachment from the email.
1911  */
1912 void EditAlarmDlg::slotRemoveAttachment()
1913 {
1914  int item = mEmailAttachList->currentItem();
1915  mEmailAttachList->removeItem(item);
1916  int count = mEmailAttachList->count();
1917  if (item >= count)
1918  mEmailAttachList->setCurrentItem(count - 1);
1919  if (!count)
1920  {
1921  mEmailRemoveButton->setEnabled(false);
1922  mEmailAttachList->setEnabled(false);
1923  }
1924 }
1925 
1926 /******************************************************************************
1927 * Clean up the alarm text, and if it's a file, check whether it's valid.
1928 */
1929 bool EditAlarmDlg::checkText(TQString& result, bool showErrorMessage) const
1930 {
1931  if (mMessageRadio->isOn())
1932  result = mTextMessageEdit->text();
1933  else if (mEmailRadio->isOn())
1934  result = mEmailMessageEdit->text();
1935  else if (mCommandRadio->isOn())
1936  {
1937  if (mCmdTypeScript->isChecked())
1938  result = mCmdScriptEdit->text();
1939  else
1940  result = mCmdCommandEdit->text();
1941  result = result.stripWhiteSpace();
1942  }
1943  else if (mFileRadio->isOn())
1944  {
1945  TQString alarmtext = mFileMessageEdit->text().stripWhiteSpace();
1946  // Convert any relative file path to absolute
1947  // (using home directory as the default)
1948  enum Err { NONE = 0, BLANK, NONEXISTENT, DIRECTORY, UNREADABLE, NOT_TEXT_IMAGE };
1949  Err err = NONE;
1950  KURL url;
1951  int i = alarmtext.find(TQString::fromLatin1("/"));
1952  if (i > 0 && alarmtext[i - 1] == ':')
1953  {
1954  url = alarmtext;
1955  url.cleanPath();
1956  alarmtext = url.prettyURL();
1957  TDEIO::UDSEntry uds;
1958  if (!TDEIO::NetAccess::stat(url, uds, MainWindow::mainMainWindow()))
1959  err = NONEXISTENT;
1960  else
1961  {
1962  KFileItem fi(uds, url);
1963  if (fi.isDir()) err = DIRECTORY;
1964  else if (!fi.isReadable()) err = UNREADABLE;
1965  }
1966  }
1967  else if (alarmtext.isEmpty())
1968  err = BLANK; // blank file name
1969  else
1970  {
1971  // It's a local file - convert to absolute path & check validity
1972  TQFileInfo info(alarmtext);
1973  TQDir::setCurrent(TQDir::homeDirPath());
1974  alarmtext = info.absFilePath();
1975  url.setPath(alarmtext);
1976  alarmtext = TQString::fromLatin1("file:") + alarmtext;
1977  if (!err)
1978  {
1979  if (info.isDir()) err = DIRECTORY;
1980  else if (!info.exists()) err = NONEXISTENT;
1981  else if (!info.isReadable()) err = UNREADABLE;
1982  }
1983  }
1984  if (!err)
1985  {
1986  switch (KAlarm::fileType(KFileItem(KFileItem::Unknown, KFileItem::Unknown, url).mimetype()))
1987  {
1988  case KAlarm::TextFormatted:
1989  case KAlarm::TextPlain:
1990  case KAlarm::TextApplication:
1991  case KAlarm::Image:
1992  break;
1993  default:
1994  err = NOT_TEXT_IMAGE;
1995  break;
1996  }
1997  }
1998  if (err && showErrorMessage)
1999  {
2000  mFileMessageEdit->setFocus();
2001  TQString errmsg;
2002  switch (err)
2003  {
2004  case BLANK:
2005  KMessageBox::sorry(const_cast<EditAlarmDlg*>(this), i18n("Please select a file to display"));
2006  return false;
2007  case NONEXISTENT: errmsg = i18n("%1\nnot found"); break;
2008  case DIRECTORY: errmsg = i18n("%1\nis a folder"); break;
2009  case UNREADABLE: errmsg = i18n("%1\nis not readable"); break;
2010  case NOT_TEXT_IMAGE: errmsg = i18n("%1\nappears not to be a text or image file"); break;
2011  case NONE:
2012  default:
2013  break;
2014  }
2015  if (KMessageBox::warningContinueCancel(const_cast<EditAlarmDlg*>(this), errmsg.arg(alarmtext))
2016  == KMessageBox::Cancel)
2017  return false;
2018  }
2019  result = alarmtext;
2020  }
2021  return true;
2022 }
2023 
2024 
2025 /*=============================================================================
2026 = Class TextEdit
2027 = A text edit field with a minimum height of 3 text lines.
2028 = Provides KDE 2 compatibility.
2029 =============================================================================*/
2030 TextEdit::TextEdit(TQWidget* parent, const char* name)
2031  : KTextEdit(parent, name)
2032 {
2033  TQSize tsize = sizeHint();
2034  tsize.setHeight(fontMetrics().lineSpacing()*13/4 + 2*frameWidth());
2035  setMinimumSize(tsize);
2036 }
2037 
2038 void TextEdit::dragEnterEvent(TQDragEnterEvent* e)
2039 {
2041  e->accept(false); // don't accept "text/calendar" objects
2042  KTextEdit::dragEnterEvent(e);
2043 }