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, 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 
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, 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  */
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, 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);
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, 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  */
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() || !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 */
1468 void 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 */
1505 void 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 */
1514 void 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  */
1524 void 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 */
1582 void 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 */
1608 void 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 */
1633 void 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 */
1661 void 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 */
1677 void 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 */
1686 bool 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 */
1724 bool 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 */
1773 void 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 */
1844 void 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 */
1866 void 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 */
1876 void 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  */
1886 void 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  */
1902 void 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  */
1918 void 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 */
1935 bool 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 =============================================================================*/
2036 TextEdit::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 
2044 void 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
miscellaneous functions
the KAlarm application object
main application window
radio button with an associated file picker