TDE personal information management applications
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2044 lines
74KB

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