editdlg.cpp
00001 /* 00002 * editdlg.cpp - dialogue to create or modify an alarm or alarm template 00003 * Program: kalarm 00004 * Copyright © 2001-2008 by David Jarvie <djarvie@kde.org> 00005 * 00006 * This program is free software; you can redistribute it and/or modify 00007 * it under the terms of the GNU General Public License as published by 00008 * the Free Software Foundation; either version 2 of the License, or 00009 * (at your option) any later version. 00010 * 00011 * This program is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 * GNU General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU General Public License along 00017 * with this program; if not, write to the Free Software Foundation, Inc., 00018 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00019 */ 00020 00021 #include "kalarm.h" 00022 00023 #include <limits.h> 00024 00025 #include <tqlayout.h> 00026 #include <tqpopupmenu.h> 00027 #include <tqvbox.h> 00028 #include <tqgroupbox.h> 00029 #include <tqwidgetstack.h> 00030 #include <tqdragobject.h> 00031 #include <tqlabel.h> 00032 #include <tqmessagebox.h> 00033 #include <tqtabwidget.h> 00034 #include <tqvalidator.h> 00035 #include <tqwhatsthis.h> 00036 #include <tqtooltip.h> 00037 #include <tqdir.h> 00038 #include <tqstyle.h> 00039 00040 #include <kglobal.h> 00041 #include <klocale.h> 00042 #include <kconfig.h> 00043 #include <kfiledialog.h> 00044 #include <kiconloader.h> 00045 #include <kio/netaccess.h> 00046 #include <kfileitem.h> 00047 #include <kmessagebox.h> 00048 #include <kurldrag.h> 00049 #include <kurlcompletion.h> 00050 #include <kwin.h> 00051 #include <kwinmodule.h> 00052 #include <kstandarddirs.h> 00053 #include <kstdguiitem.h> 00054 #include <kabc/addresseedialog.h> 00055 #include <kdebug.h> 00056 00057 #include <libkdepim/maillistdrag.h> 00058 #include <libkdepim/kvcarddrag.h> 00059 #include <libkcal/icaldrag.h> 00060 00061 #include "alarmcalendar.h" 00062 #include "alarmtimewidget.h" 00063 #include "checkbox.h" 00064 #include "colourcombo.h" 00065 #include "deferdlg.h" 00066 #include "emailidcombo.h" 00067 #include "fontcolourbutton.h" 00068 #include "functions.h" 00069 #include "kalarmapp.h" 00070 #include "kamail.h" 00071 #include "latecancel.h" 00072 #include "lineedit.h" 00073 #include "mainwindow.h" 00074 #include "pickfileradio.h" 00075 #include "preferences.h" 00076 #include "radiobutton.h" 00077 #include "recurrenceedit.h" 00078 #include "reminder.h" 00079 #include "shellprocess.h" 00080 #include "soundpicker.h" 00081 #include "specialactions.h" 00082 #include "spinbox.h" 00083 #include "templatepickdlg.h" 00084 #include "timeedit.h" 00085 #include "timespinbox.h" 00086 #include "editdlg.moc" 00087 #include "editdlgprivate.moc" 00088 00089 using namespace KCal; 00090 00091 static const char EDIT_DIALOG_NAME[] = "EditDialog"; 00092 static const int maxDelayTime = 99*60 + 59; // < 100 hours 00093 00094 /*============================================================================= 00095 = Class PickAlarmFileRadio 00096 =============================================================================*/ 00097 class PickAlarmFileRadio : public PickFileRadio 00098 { 00099 public: 00100 PickAlarmFileRadio(const TQString& text, TQButtonGroup* parent, const char* name = 0) 00101 : PickFileRadio(text, parent, name) { } 00102 virtual TQString pickFile() // called when browse button is pressed to select a file to display 00103 { 00104 return KAlarm::browseFile(i18n("Choose Text or Image File to Display"), mDefaultDir, fileEdit()->text(), 00105 TQString(), KFile::ExistingOnly, parentWidget(), "pickAlarmFile"); 00106 } 00107 private: 00108 TQString mDefaultDir; // default directory for file browse button 00109 }; 00110 00111 /*============================================================================= 00112 = Class PickLogFileRadio 00113 =============================================================================*/ 00114 class PickLogFileRadio : public PickFileRadio 00115 { 00116 public: 00117 PickLogFileRadio(TQPushButton* b, LineEdit* e, const TQString& text, TQButtonGroup* parent, const char* name = 0) 00118 : PickFileRadio(b, e, text, parent, name) { } 00119 virtual TQString pickFile() // called when browse button is pressed to select a log file 00120 { 00121 return KAlarm::browseFile(i18n("Choose Log File"), mDefaultDir, fileEdit()->text(), TQString(), 00122 KFile::LocalOnly, parentWidget(), "pickLogFile"); 00123 } 00124 private: 00125 TQString mDefaultDir; // default directory for log file browse button 00126 }; 00127 00128 inline TQString recurText(const KAEvent& event) 00129 { 00130 TQString r; 00131 if (event.repeatCount()) 00132 r = TQString::fromLatin1("%1 / %2").arg(event.recurrenceText()).arg(event.repetitionText()); 00133 else 00134 r = event.recurrenceText(); 00135 return i18n("&Recurrence - [%1]").arg(r); 00136 } 00137 00138 // Collect these widget labels together to ensure consistent wording and 00139 // translations across different modules. 00140 TQString EditAlarmDlg::i18n_ConfirmAck() { return i18n("Confirm acknowledgment"); } 00141 TQString EditAlarmDlg::i18n_k_ConfirmAck() { return i18n("Confirm ac&knowledgment"); } 00142 TQString EditAlarmDlg::i18n_SpecialActions() { return i18n("Special Actions..."); } 00143 TQString EditAlarmDlg::i18n_ShowInKOrganizer() { return i18n("Show in KOrganizer"); } 00144 TQString EditAlarmDlg::i18n_g_ShowInKOrganizer() { return i18n("Show in KOr&ganizer"); } 00145 TQString EditAlarmDlg::i18n_EnterScript() { return i18n("Enter a script"); } 00146 TQString EditAlarmDlg::i18n_p_EnterScript() { return i18n("Enter a scri&pt"); } 00147 TQString EditAlarmDlg::i18n_ExecInTermWindow() { return i18n("Execute in terminal window"); } 00148 TQString EditAlarmDlg::i18n_w_ExecInTermWindow() { return i18n("Execute in terminal &window"); } 00149 TQString EditAlarmDlg::i18n_u_ExecInTermWindow() { return i18n("Exec&ute in terminal window"); } 00150 TQString EditAlarmDlg::i18n_g_LogToFile() { return i18n("Lo&g to file"); } 00151 TQString EditAlarmDlg::i18n_CopyEmailToSelf() { return i18n("Copy email to self"); } 00152 TQString EditAlarmDlg::i18n_e_CopyEmailToSelf() { return i18n("Copy &email to self"); } 00153 TQString EditAlarmDlg::i18n_s_CopyEmailToSelf() { return i18n("Copy email to &self"); } 00154 TQString EditAlarmDlg::i18n_EmailFrom() { return i18n("'From' email address", "From:"); } 00155 TQString EditAlarmDlg::i18n_f_EmailFrom() { return i18n("'From' email address", "&From:"); } 00156 TQString EditAlarmDlg::i18n_EmailTo() { return i18n("Email addressee", "To:"); } 00157 TQString EditAlarmDlg::i18n_EmailSubject() { return i18n("Email subject", "Subject:"); } 00158 TQString EditAlarmDlg::i18n_j_EmailSubject() { return i18n("Email subject", "Sub&ject:"); } 00159 00160 00161 /****************************************************************************** 00162 * Constructor. 00163 * Parameters: 00164 * Template = true to edit/create an alarm template 00165 * = false to edit/create an alarm. 00166 * event != to initialise the dialogue to show the specified event's data. 00167 */ 00168 EditAlarmDlg::EditAlarmDlg(bool Template, const TQString& caption, TQWidget* parent, const char* name, 00169 const KAEvent* event, bool readOnly) 00170 : KDialogBase(parent, (name ? name : Template ? "TemplEditDlg" : "EditDlg"), true, caption, 00171 (readOnly ? Cancel|Try : Template ? Ok|Cancel|Try : Ok|Cancel|Try|Default), 00172 (readOnly ? Cancel : Ok)), 00173 mMainPageShown(false), 00174 mRecurPageShown(false), 00175 mRecurSetDefaultEndDate(true), 00176 mTemplateName(0), 00177 mSpecialActionsButton(0), 00178 mReminderDeferral(false), 00179 mReminderArchived(false), 00180 mEmailRemoveButton(0), 00181 mDeferGroup(0), 00182 mTimeWidget(0), 00183 mShowInKorganizer(0), 00184 mDeferGroupHeight(0), 00185 mTemplate(Template), 00186 mDesiredReadOnly(readOnly), 00187 mReadOnly(readOnly), 00188 mSavedEvent(0) 00189 { 00190 setButtonText(Default, i18n("Load Template...")); 00191 TQVBox* mainWidget = new TQVBox(this); 00192 mainWidget->setSpacing(spacingHint()); 00193 setMainWidget(mainWidget); 00194 if (mTemplate) 00195 { 00196 TQHBox* box = new TQHBox(mainWidget); 00197 box->setSpacing(spacingHint()); 00198 TQLabel* label = new TQLabel(i18n("Template name:"), box); 00199 label->setFixedSize(label->sizeHint()); 00200 mTemplateName = new TQLineEdit(box); 00201 mTemplateName->setReadOnly(mReadOnly); 00202 label->setBuddy(mTemplateName); 00203 TQWhatsThis::add(box, i18n("Enter the name of the alarm template")); 00204 box->setFixedHeight(box->sizeHint().height()); 00205 } 00206 mTabs = new TQTabWidget(mainWidget); 00207 mTabs->setMargin(marginHint()); 00208 00209 TQVBox* mainPageBox = new TQVBox(mTabs); 00210 mainPageBox->setSpacing(spacingHint()); 00211 mTabs->addTab(mainPageBox, i18n("&Alarm")); 00212 mMainPageIndex = 0; 00213 PageFrame* mainPage = new PageFrame(mainPageBox); 00214 connect(mainPage, TQT_SIGNAL(shown()), TQT_SLOT(slotShowMainPage())); 00215 TQVBoxLayout* topLayout = new TQVBoxLayout(mainPage, 0, spacingHint()); 00216 00217 // Recurrence tab 00218 TQVBox* recurTab = new TQVBox(mTabs); 00219 mainPageBox->setSpacing(spacingHint()); 00220 mTabs->addTab(recurTab, TQString()); 00221 mRecurPageIndex = 1; 00222 mRecurrenceEdit = new RecurrenceEdit(readOnly, recurTab, "recurPage"); 00223 connect(mRecurrenceEdit, TQT_SIGNAL(shown()), TQT_SLOT(slotShowRecurrenceEdit())); 00224 connect(mRecurrenceEdit, TQT_SIGNAL(typeChanged(int)), TQT_SLOT(slotRecurTypeChange(int))); 00225 connect(mRecurrenceEdit, TQT_SIGNAL(frequencyChanged()), TQT_SLOT(slotRecurFrequencyChange())); 00226 connect(mRecurrenceEdit, TQT_SIGNAL(repeatNeedsInitialisation()), TQT_SLOT(slotSetSubRepetition())); 00227 00228 // Alarm action 00229 00230 mActionGroup = new ButtonGroup(i18n("Action"), mainPage, "actionGroup"); 00231 connect(mActionGroup, TQT_SIGNAL(buttonSet(int)), TQT_SLOT(slotAlarmTypeChanged(int))); 00232 topLayout->addWidget(mActionGroup, 1); 00233 TQBoxLayout* layout = new TQVBoxLayout(mActionGroup, marginHint(), spacingHint()); 00234 layout->addSpacing(fontMetrics().lineSpacing()/2); 00235 TQGridLayout* grid = new TQGridLayout(layout, 1, 5); 00236 00237 // Message radio button 00238 mMessageRadio = new RadioButton(i18n("Te&xt"), mActionGroup, "messageButton"); 00239 mMessageRadio->setFixedSize(mMessageRadio->sizeHint()); 00240 TQWhatsThis::add(mMessageRadio, 00241 i18n("If checked, the alarm will display a text message.")); 00242 grid->addWidget(mMessageRadio, 1, 0); 00243 grid->setColStretch(1, 1); 00244 00245 // File radio button 00246 mFileRadio = new PickAlarmFileRadio(i18n("&File"), mActionGroup, "fileButton"); 00247 mFileRadio->setFixedSize(mFileRadio->sizeHint()); 00248 TQWhatsThis::add(mFileRadio, 00249 i18n("If checked, the alarm will display the contents of a text or image file.")); 00250 grid->addWidget(mFileRadio, 1, 2); 00251 grid->setColStretch(3, 1); 00252 00253 // Command radio button 00254 mCommandRadio = new RadioButton(i18n("Co&mmand"), mActionGroup, "cmdButton"); 00255 mCommandRadio->setFixedSize(mCommandRadio->sizeHint()); 00256 TQWhatsThis::add(mCommandRadio, 00257 i18n("If checked, the alarm will execute a shell command.")); 00258 grid->addWidget(mCommandRadio, 1, 4); 00259 grid->setColStretch(5, 1); 00260 00261 // Email radio button 00262 mEmailRadio = new RadioButton(i18n("&Email"), mActionGroup, "emailButton"); 00263 mEmailRadio->setFixedSize(mEmailRadio->sizeHint()); 00264 TQWhatsThis::add(mEmailRadio, 00265 i18n("If checked, the alarm will send an email.")); 00266 grid->addWidget(mEmailRadio, 1, 6); 00267 00268 initDisplayAlarms(mActionGroup); 00269 layout->addWidget(mDisplayAlarmsFrame); 00270 initCommand(mActionGroup); 00271 layout->addWidget(mCommandFrame); 00272 initEmail(mActionGroup); 00273 layout->addWidget(mEmailFrame); 00274 00275 // Deferred date/time: visible only for a deferred recurring event. 00276 mDeferGroup = new TQGroupBox(1, Qt::Vertical, i18n("Deferred Alarm"), mainPage, "deferGroup"); 00277 topLayout->addWidget(mDeferGroup); 00278 TQLabel* label = new TQLabel(i18n("Deferred to:"), mDeferGroup); 00279 label->setFixedSize(label->sizeHint()); 00280 mDeferTimeLabel = new TQLabel(mDeferGroup); 00281 00282 mDeferChangeButton = new TQPushButton(i18n("C&hange..."), mDeferGroup); 00283 mDeferChangeButton->setFixedSize(mDeferChangeButton->sizeHint()); 00284 connect(mDeferChangeButton, TQT_SIGNAL(clicked()), TQT_SLOT(slotEditDeferral())); 00285 TQWhatsThis::add(mDeferChangeButton, i18n("Change the alarm's deferred time, or cancel the deferral")); 00286 mDeferGroup->addSpace(0); 00287 00288 layout = new TQHBoxLayout(topLayout); 00289 00290 // Date and time entry 00291 if (mTemplate) 00292 { 00293 mTemplateTimeGroup = new ButtonGroup(i18n("Time"), mainPage, "templateGroup"); 00294 connect(mTemplateTimeGroup, TQT_SIGNAL(buttonSet(int)), TQT_SLOT(slotTemplateTimeType(int))); 00295 layout->addWidget(mTemplateTimeGroup); 00296 grid = new TQGridLayout(mTemplateTimeGroup, 2, 2, marginHint(), spacingHint()); 00297 grid->addRowSpacing(0, fontMetrics().lineSpacing()/2); 00298 // Get alignment to use in TQGridLayout (AlignAuto doesn't work correctly there) 00299 int alignment = TQApplication::reverseLayout() ? TQt::AlignRight : TQt::AlignLeft; 00300 00301 mTemplateDefaultTime = new RadioButton(i18n("&Default time"), mTemplateTimeGroup, "templateDefTimeButton"); 00302 mTemplateDefaultTime->setFixedSize(mTemplateDefaultTime->sizeHint()); 00303 mTemplateDefaultTime->setReadOnly(mReadOnly); 00304 TQWhatsThis::add(mTemplateDefaultTime, 00305 i18n("Do not specify a start time for alarms based on this template. " 00306 "The normal default start time will be used.")); 00307 grid->addWidget(mTemplateDefaultTime, 0, 0, alignment); 00308 00309 TQHBox* box = new TQHBox(mTemplateTimeGroup); 00310 box->setSpacing(spacingHint()); 00311 mTemplateUseTime = new RadioButton(i18n("Time:"), box, "templateTimeButton"); 00312 mTemplateUseTime->setFixedSize(mTemplateUseTime->sizeHint()); 00313 mTemplateUseTime->setReadOnly(mReadOnly); 00314 TQWhatsThis::add(mTemplateUseTime, 00315 i18n("Specify a start time for alarms based on this template.")); 00316 mTemplateTimeGroup->insert(mTemplateUseTime); 00317 mTemplateTime = new TimeEdit(box, "templateTimeEdit"); 00318 mTemplateTime->setFixedSize(mTemplateTime->sizeHint()); 00319 mTemplateTime->setReadOnly(mReadOnly); 00320 TQWhatsThis::add(mTemplateTime, 00321 TQString("%1\n\n%2").arg(i18n("Enter the start time for alarms based on this template.")) 00322 .arg(TimeSpinBox::shiftWhatsThis())); 00323 box->setStretchFactor(new TQWidget(box), 1); // left adjust the controls 00324 box->setFixedHeight(box->sizeHint().height()); 00325 grid->addWidget(box, 0, 1, alignment); 00326 00327 mTemplateAnyTime = new RadioButton(i18n("An&y time"), mTemplateTimeGroup, "templateAnyTimeButton"); 00328 mTemplateAnyTime->setFixedSize(mTemplateAnyTime->sizeHint()); 00329 mTemplateAnyTime->setReadOnly(mReadOnly); 00330 TQWhatsThis::add(mTemplateAnyTime, 00331 i18n("Set the '%1' option for alarms based on this template.").arg(i18n("Any time"))); 00332 grid->addWidget(mTemplateAnyTime, 1, 0, alignment); 00333 00334 box = new TQHBox(mTemplateTimeGroup); 00335 box->setSpacing(spacingHint()); 00336 mTemplateUseTimeAfter = new RadioButton(AlarmTimeWidget::i18n_w_TimeFromNow(), box, "templateFromNowButton"); 00337 mTemplateUseTimeAfter->setFixedSize(mTemplateUseTimeAfter->sizeHint()); 00338 mTemplateUseTimeAfter->setReadOnly(mReadOnly); 00339 TQWhatsThis::add(mTemplateUseTimeAfter, 00340 i18n("Set alarms based on this template to start after the specified time " 00341 "interval from when the alarm is created.")); 00342 mTemplateTimeGroup->insert(mTemplateUseTimeAfter); 00343 mTemplateTimeAfter = new TimeSpinBox(1, maxDelayTime, box); 00344 mTemplateTimeAfter->setValue(1439); 00345 mTemplateTimeAfter->setFixedSize(mTemplateTimeAfter->sizeHint()); 00346 mTemplateTimeAfter->setReadOnly(mReadOnly); 00347 TQWhatsThis::add(mTemplateTimeAfter, 00348 TQString("%1\n\n%2").arg(AlarmTimeWidget::i18n_TimeAfterPeriod()) 00349 .arg(TimeSpinBox::shiftWhatsThis())); 00350 box->setFixedHeight(box->sizeHint().height()); 00351 grid->addWidget(box, 1, 1, alignment); 00352 00353 layout->addStretch(); 00354 } 00355 else 00356 { 00357 mTimeWidget = new AlarmTimeWidget(i18n("Time"), AlarmTimeWidget::AT_TIME, mainPage, "timeGroup"); 00358 connect(mTimeWidget, TQT_SIGNAL(anyTimeToggled(bool)), TQT_SLOT(slotAnyTimeToggled(bool))); 00359 topLayout->addWidget(mTimeWidget); 00360 } 00361 00362 // Reminder 00363 static const TQString reminderText = i18n("Enter how long in advance of the main alarm to display a reminder alarm."); 00364 mReminder = new Reminder(i18n("Rem&inder:"), 00365 i18n("Check to additionally display a reminder in advance of the main alarm time(s)."), 00366 TQString("%1\n\n%2").arg(reminderText).arg(TimeSpinBox::shiftWhatsThis()), 00367 true, true, mainPage); 00368 mReminder->setFixedSize(mReminder->sizeHint()); 00369 topLayout->addWidget(mReminder, 0, TQt::AlignAuto); 00370 00371 // Late cancel selector - default = allow late display 00372 mLateCancel = new LateCancelSelector(true, mainPage); 00373 topLayout->addWidget(mLateCancel, 0, TQt::AlignAuto); 00374 00375 // Acknowledgement confirmation required - default = no confirmation 00376 layout = new TQHBoxLayout(topLayout, 0); 00377 mConfirmAck = createConfirmAckCheckbox(mainPage); 00378 mConfirmAck->setFixedSize(mConfirmAck->sizeHint()); 00379 layout->addWidget(mConfirmAck); 00380 layout->addSpacing(2*spacingHint()); 00381 layout->addStretch(); 00382 00383 if (theApp()->korganizerEnabled()) 00384 { 00385 // Show in KOrganizer checkbox 00386 mShowInKorganizer = new CheckBox(i18n_ShowInKOrganizer(), mainPage); 00387 mShowInKorganizer->setFixedSize(mShowInKorganizer->sizeHint()); 00388 TQWhatsThis::add(mShowInKorganizer, i18n("Check to copy the alarm into KOrganizer's calendar")); 00389 layout->addWidget(mShowInKorganizer); 00390 } 00391 00392 setButtonWhatsThis(Ok, i18n("Schedule the alarm at the specified time.")); 00393 00394 // Initialise the state of all controls according to the specified event, if any 00395 initialise(event); 00396 if (mTemplateName) 00397 mTemplateName->setFocus(); 00398 00399 // Save the initial state of all controls so that we can later tell if they have changed 00400 saveState((event && (mTemplate || !event->isTemplate())) ? event : 0); 00401 00402 // Note the current desktop so that the dialog can be shown on it. 00403 // If a main window is visible, the dialog will by KDE default always appear on its 00404 // desktop. If the user invokes the dialog via the system tray on a different desktop, 00405 // that can cause confusion. 00406 mDesktop = KWin::currentDesktop(); 00407 } 00408 00409 EditAlarmDlg::~EditAlarmDlg() 00410 { 00411 delete mSavedEvent; 00412 } 00413 00414 /****************************************************************************** 00415 * Set up the dialog controls common to display alarms. 00416 */ 00417 void EditAlarmDlg::initDisplayAlarms(TQWidget* parent) 00418 { 00419 mDisplayAlarmsFrame = new TQFrame(parent); 00420 mDisplayAlarmsFrame->setFrameStyle(TQFrame::NoFrame); 00421 TQBoxLayout* frameLayout = new TQVBoxLayout(mDisplayAlarmsFrame, 0, spacingHint()); 00422 00423 // Text message edit box 00424 mTextMessageEdit = new TextEdit(mDisplayAlarmsFrame); 00425 mTextMessageEdit->setWordWrap(KTextEdit::NoWrap); 00426 TQWhatsThis::add(mTextMessageEdit, i18n("Enter the text of the alarm message. It may be multi-line.")); 00427 frameLayout->addWidget(mTextMessageEdit); 00428 00429 // File name edit box 00430 mFileBox = new TQHBox(mDisplayAlarmsFrame); 00431 frameLayout->addWidget(mFileBox); 00432 mFileMessageEdit = new LineEdit(LineEdit::Url, mFileBox); 00433 mFileMessageEdit->setAcceptDrops(true); 00434 TQWhatsThis::add(mFileMessageEdit, i18n("Enter the name or URL of a text or image file to display.")); 00435 00436 // File browse button 00437 mFileBrowseButton = new TQPushButton(mFileBox); 00438 mFileBrowseButton->setPixmap(SmallIcon("fileopen")); 00439 mFileBrowseButton->setFixedSize(mFileBrowseButton->sizeHint()); 00440 TQToolTip::add(mFileBrowseButton, i18n("Choose a file")); 00441 TQWhatsThis::add(mFileBrowseButton, i18n("Select a text or image file to display.")); 00442 mFileRadio->init(mFileBrowseButton, mFileMessageEdit); 00443 00444 // Font and colour choice button and sample text 00445 mFontColourButton = new FontColourButton(mDisplayAlarmsFrame); 00446 mFontColourButton->setMaximumHeight(mFontColourButton->sizeHint().height()); 00447 frameLayout->addWidget(mFontColourButton); 00448 00449 TQHBoxLayout* layout = new TQHBoxLayout(frameLayout, 0, 0); 00450 mBgColourBox = new TQHBox(mDisplayAlarmsFrame); 00451 mBgColourBox->setSpacing(spacingHint()); 00452 layout->addWidget(mBgColourBox); 00453 layout->addStretch(); 00454 TQLabel* label = new TQLabel(i18n("&Background color:"), mBgColourBox); 00455 mBgColourButton = new ColourCombo(mBgColourBox); 00456 label->setBuddy(mBgColourButton); 00457 TQWhatsThis::add(mBgColourBox, i18n("Select the alarm message background color")); 00458 00459 // Sound checkbox and file selector 00460 layout = new TQHBoxLayout(frameLayout); 00461 mSoundPicker = new SoundPicker(mDisplayAlarmsFrame); 00462 mSoundPicker->setFixedSize(mSoundPicker->sizeHint()); 00463 layout->addWidget(mSoundPicker); 00464 layout->addSpacing(2*spacingHint()); 00465 layout->addStretch(); 00466 00467 if (ShellProcess::authorised()) // don't display if shell commands not allowed (e.g. kiosk mode) 00468 { 00469 // Special actions button 00470 mSpecialActionsButton = new SpecialActionsButton(i18n_SpecialActions(), mDisplayAlarmsFrame); 00471 mSpecialActionsButton->setFixedSize(mSpecialActionsButton->sizeHint()); 00472 layout->addWidget(mSpecialActionsButton); 00473 } 00474 00475 // Top-adjust the controls 00476 mFilePadding = new TQHBox(mDisplayAlarmsFrame); 00477 frameLayout->addWidget(mFilePadding); 00478 frameLayout->setStretchFactor(mFilePadding, 1); 00479 } 00480 00481 /****************************************************************************** 00482 * Set up the command alarm dialog controls. 00483 */ 00484 void EditAlarmDlg::initCommand(TQWidget* parent) 00485 { 00486 mCommandFrame = new TQFrame(parent); 00487 mCommandFrame->setFrameStyle(TQFrame::NoFrame); 00488 TQBoxLayout* frameLayout = new TQVBoxLayout(mCommandFrame, 0, spacingHint()); 00489 00490 mCmdTypeScript = new CheckBox(i18n_p_EnterScript(), mCommandFrame); 00491 mCmdTypeScript->setFixedSize(mCmdTypeScript->sizeHint()); 00492 connect(mCmdTypeScript, TQT_SIGNAL(toggled(bool)), TQT_SLOT(slotCmdScriptToggled(bool))); 00493 TQWhatsThis::add(mCmdTypeScript, i18n("Check to enter the contents of a script instead of a shell command line")); 00494 frameLayout->addWidget(mCmdTypeScript, 0, TQt::AlignAuto); 00495 00496 mCmdCommandEdit = new LineEdit(LineEdit::Url, mCommandFrame); 00497 TQWhatsThis::add(mCmdCommandEdit, i18n("Enter a shell command to execute.")); 00498 frameLayout->addWidget(mCmdCommandEdit); 00499 00500 mCmdScriptEdit = new TextEdit(mCommandFrame); 00501 TQWhatsThis::add(mCmdScriptEdit, i18n("Enter the contents of a script to execute")); 00502 frameLayout->addWidget(mCmdScriptEdit); 00503 00504 // What to do with command output 00505 00506 mCmdOutputGroup = new ButtonGroup(i18n("Command Output"), mCommandFrame); 00507 frameLayout->addWidget(mCmdOutputGroup); 00508 TQBoxLayout* layout = new TQVBoxLayout(mCmdOutputGroup, marginHint(), spacingHint()); 00509 layout->addSpacing(fontMetrics().lineSpacing()/2); 00510 00511 // Execute in terminal window 00512 RadioButton* button = new RadioButton(i18n_u_ExecInTermWindow(), mCmdOutputGroup, "execInTerm"); 00513 button->setFixedSize(button->sizeHint()); 00514 TQWhatsThis::add(button, i18n("Check to execute the command in a terminal window")); 00515 mCmdOutputGroup->insert(button, EXEC_IN_TERMINAL); 00516 layout->addWidget(button, 0, TQt::AlignAuto); 00517 00518 // Log file name edit box 00519 TQHBox* box = new TQHBox(mCmdOutputGroup); 00520 (new TQWidget(box))->setFixedWidth(button->style().subRect(TQStyle::SR_RadioButtonIndicator, button).width()); // indent the edit box 00521 // (new TQWidget(box))->setFixedWidth(button->style().pixelMetric(TQStyle::PM_ExclusiveIndicatorWidth)); // indent the edit box 00522 mCmdLogFileEdit = new LineEdit(LineEdit::Url, box); 00523 mCmdLogFileEdit->setAcceptDrops(true); 00524 TQWhatsThis::add(mCmdLogFileEdit, i18n("Enter the name or path of the log file.")); 00525 00526 // Log file browse button. 00527 // The file browser dialogue is activated by the PickLogFileRadio class. 00528 TQPushButton* browseButton = new TQPushButton(box); 00529 browseButton->setPixmap(SmallIcon("fileopen")); 00530 browseButton->setFixedSize(browseButton->sizeHint()); 00531 TQToolTip::add(browseButton, i18n("Choose a file")); 00532 TQWhatsThis::add(browseButton, i18n("Select a log file.")); 00533 00534 // Log output to file 00535 button = new PickLogFileRadio(browseButton, mCmdLogFileEdit, i18n_g_LogToFile(), mCmdOutputGroup, "cmdLog"); 00536 button->setFixedSize(button->sizeHint()); 00537 TQWhatsThis::add(button, 00538 i18n("Check to log the command output to a local file. The output will be appended to any existing contents of the file.")); 00539 mCmdOutputGroup->insert(button, LOG_TO_FILE); 00540 layout->addWidget(button, 0, TQt::AlignAuto); 00541 layout->addWidget(box); 00542 00543 // Discard output 00544 button = new RadioButton(i18n("Discard"), mCmdOutputGroup, "cmdDiscard"); 00545 button->setFixedSize(button->sizeHint()); 00546 TQWhatsThis::add(button, i18n("Check to discard command output.")); 00547 mCmdOutputGroup->insert(button, DISCARD_OUTPUT); 00548 layout->addWidget(button, 0, TQt::AlignAuto); 00549 00550 // Top-adjust the controls 00551 mCmdPadding = new TQHBox(mCommandFrame); 00552 frameLayout->addWidget(mCmdPadding); 00553 frameLayout->setStretchFactor(mCmdPadding, 1); 00554 } 00555 00556 /****************************************************************************** 00557 * Set up the email alarm dialog controls. 00558 */ 00559 void EditAlarmDlg::initEmail(TQWidget* parent) 00560 { 00561 mEmailFrame = new TQFrame(parent); 00562 mEmailFrame->setFrameStyle(TQFrame::NoFrame); 00563 TQBoxLayout* layout = new TQVBoxLayout(mEmailFrame, 0, spacingHint()); 00564 TQGridLayout* grid = new TQGridLayout(layout, 3, 3, spacingHint()); 00565 grid->setColStretch(1, 1); 00566 00567 mEmailFromList = 0; 00568 if (Preferences::emailFrom() == Preferences::MAIL_FROM_KMAIL) 00569 { 00570 // Email sender identity 00571 TQLabel* label = new TQLabel(i18n_EmailFrom(), mEmailFrame); 00572 label->setFixedSize(label->sizeHint()); 00573 grid->addWidget(label, 0, 0); 00574 00575 mEmailFromList = new EmailIdCombo(KAMail::identityManager(), mEmailFrame); 00576 mEmailFromList->setMinimumSize(mEmailFromList->sizeHint()); 00577 label->setBuddy(mEmailFromList); 00578 TQWhatsThis::add(mEmailFromList, 00579 i18n("Your email identity, used to identify you as the sender when sending email alarms.")); 00580 grid->addMultiCellWidget(mEmailFromList, 0, 0, 1, 2); 00581 } 00582 00583 // Email recipients 00584 TQLabel* label = new TQLabel(i18n_EmailTo(), mEmailFrame); 00585 label->setFixedSize(label->sizeHint()); 00586 grid->addWidget(label, 1, 0); 00587 00588 mEmailToEdit = new LineEdit(LineEdit::Emails, mEmailFrame); 00589 mEmailToEdit->setMinimumSize(mEmailToEdit->sizeHint()); 00590 TQWhatsThis::add(mEmailToEdit, 00591 i18n("Enter the addresses of the email recipients. Separate multiple addresses by " 00592 "commas or semicolons.")); 00593 grid->addWidget(mEmailToEdit, 1, 1); 00594 00595 mEmailAddressButton = new TQPushButton(mEmailFrame); 00596 mEmailAddressButton->setPixmap(SmallIcon("contents")); 00597 mEmailAddressButton->setFixedSize(mEmailAddressButton->sizeHint()); 00598 connect(mEmailAddressButton, TQT_SIGNAL(clicked()), TQT_SLOT(openAddressBook())); 00599 TQToolTip::add(mEmailAddressButton, i18n("Open address book")); 00600 TQWhatsThis::add(mEmailAddressButton, i18n("Select email addresses from your address book.")); 00601 grid->addWidget(mEmailAddressButton, 1, 2); 00602 00603 // Email subject 00604 label = new TQLabel(i18n_j_EmailSubject(), mEmailFrame); 00605 label->setFixedSize(label->sizeHint()); 00606 grid->addWidget(label, 2, 0); 00607 00608 mEmailSubjectEdit = new LineEdit(mEmailFrame); 00609 mEmailSubjectEdit->setMinimumSize(mEmailSubjectEdit->sizeHint()); 00610 label->setBuddy(mEmailSubjectEdit); 00611 TQWhatsThis::add(mEmailSubjectEdit, i18n("Enter the email subject.")); 00612 grid->addMultiCellWidget(mEmailSubjectEdit, 2, 2, 1, 2); 00613 00614 // Email body 00615 mEmailMessageEdit = new TextEdit(mEmailFrame); 00616 TQWhatsThis::add(mEmailMessageEdit, i18n("Enter the email message.")); 00617 layout->addWidget(mEmailMessageEdit); 00618 00619 // Email attachments 00620 grid = new TQGridLayout(layout, 2, 3, spacingHint()); 00621 label = new TQLabel(i18n("Attachment&s:"), mEmailFrame); 00622 label->setFixedSize(label->sizeHint()); 00623 grid->addWidget(label, 0, 0); 00624 00625 mEmailAttachList = new TQComboBox(true, mEmailFrame); 00626 mEmailAttachList->setMinimumSize(mEmailAttachList->sizeHint()); 00627 mEmailAttachList->lineEdit()->setReadOnly(true); 00628 TQListBox* list = mEmailAttachList->listBox(); 00629 TQRect rect = list->geometry(); 00630 list->setGeometry(rect.left() - 50, rect.top(), rect.width(), rect.height()); 00631 label->setBuddy(mEmailAttachList); 00632 TQWhatsThis::add(mEmailAttachList, 00633 i18n("Files to send as attachments to the email.")); 00634 grid->addWidget(mEmailAttachList, 0, 1); 00635 grid->setColStretch(1, 1); 00636 00637 mEmailAddAttachButton = new TQPushButton(i18n("Add..."), mEmailFrame); 00638 connect(mEmailAddAttachButton, TQT_SIGNAL(clicked()), TQT_SLOT(slotAddAttachment())); 00639 TQWhatsThis::add(mEmailAddAttachButton, i18n("Add an attachment to the email.")); 00640 grid->addWidget(mEmailAddAttachButton, 0, 2); 00641 00642 mEmailRemoveButton = new TQPushButton(i18n("Remo&ve"), mEmailFrame); 00643 connect(mEmailRemoveButton, TQT_SIGNAL(clicked()), TQT_SLOT(slotRemoveAttachment())); 00644 TQWhatsThis::add(mEmailRemoveButton, i18n("Remove the highlighted attachment from the email.")); 00645 grid->addWidget(mEmailRemoveButton, 1, 2); 00646 00647 // BCC email to sender 00648 mEmailBcc = new CheckBox(i18n_s_CopyEmailToSelf(), mEmailFrame); 00649 mEmailBcc->setFixedSize(mEmailBcc->sizeHint()); 00650 TQWhatsThis::add(mEmailBcc, 00651 i18n("If checked, the email will be blind copied to you.")); 00652 grid->addMultiCellWidget(mEmailBcc, 1, 1, 0, 1, TQt::AlignAuto); 00653 } 00654 00655 /****************************************************************************** 00656 * Initialise the dialogue controls from the specified event. 00657 */ 00658 void EditAlarmDlg::initialise(const KAEvent* event) 00659 { 00660 mReadOnly = mDesiredReadOnly; 00661 if (!mTemplate && event && event->action() == KAEvent::COMMAND && !ShellProcess::authorised()) 00662 mReadOnly = true; // don't allow editing of existing command alarms in kiosk mode 00663 setReadOnly(); 00664 00665 mChanged = false; 00666 mOnlyDeferred = false; 00667 mExpiredRecurrence = false; 00668 mKMailSerialNumber = 0; 00669 bool deferGroupVisible = false; 00670 if (event) 00671 { 00672 // Set the values to those for the specified event 00673 if (mTemplate) 00674 mTemplateName->setText(event->templateName()); 00675 bool recurs = event->recurs(); 00676 if ((recurs || event->repeatCount()) && !mTemplate && event->deferred()) 00677 { 00678 deferGroupVisible = true; 00679 mDeferDateTime = event->deferDateTime(); 00680 mDeferTimeLabel->setText(mDeferDateTime.formatLocale()); 00681 mDeferGroup->show(); 00682 } 00683 if (event->defaultFont()) 00684 mFontColourButton->setDefaultFont(); 00685 else 00686 mFontColourButton->setFont(event->font()); 00687 mFontColourButton->setBgColour(event->bgColour()); 00688 mFontColourButton->setFgColour(event->fgColour()); 00689 mBgColourButton->setColour(event->bgColour()); 00690 if (mTemplate) 00691 { 00692 // Editing a template 00693 int afterTime = event->isTemplate() ? event->templateAfterTime() : -1; 00694 bool noTime = !afterTime; 00695 bool useTime = !event->mainDateTime().isDateOnly(); 00696 int button = mTemplateTimeGroup->id(noTime ? mTemplateDefaultTime : 00697 (afterTime > 0) ? mTemplateUseTimeAfter : 00698 useTime ? mTemplateUseTime : mTemplateAnyTime); 00699 mTemplateTimeGroup->setButton(button); 00700 mTemplateTimeAfter->setValue(afterTime > 0 ? afterTime : 1); 00701 if (!noTime && useTime) 00702 mTemplateTime->setValue(event->mainDateTime().time()); 00703 else 00704 mTemplateTime->setValue(0); 00705 } 00706 else 00707 { 00708 if (event->isTemplate()) 00709 { 00710 // Initialising from an alarm template: use current date 00711 TQDateTime now = TQDateTime::currentDateTime(); 00712 int afterTime = event->templateAfterTime(); 00713 if (afterTime >= 0) 00714 { 00715 mTimeWidget->setDateTime(TQDateTime(now.addSecs(afterTime * 60))); 00716 mTimeWidget->selectTimeFromNow(); 00717 } 00718 else 00719 { 00720 TQDate d = now.date(); 00721 TQTime t = event->startDateTime().time(); 00722 bool dateOnly = event->startDateTime().isDateOnly(); 00723 if (!dateOnly && now.time() >= t) 00724 d = d.addDays(1); // alarm time has already passed, so use tomorrow 00725 mTimeWidget->setDateTime(DateTime(TQDateTime(d, t), dateOnly)); 00726 } 00727 } 00728 else 00729 { 00730 mExpiredRecurrence = recurs && event->mainExpired(); 00731 mTimeWidget->setDateTime(recurs || event->uidStatus() == KAEvent::EXPIRED ? event->startDateTime() 00732 : event->mainExpired() ? event->deferDateTime() : event->mainDateTime()); 00733 } 00734 } 00735 00736 KAEvent::Action action = event->action(); 00737 AlarmText altext; 00738 if (event->commandScript()) 00739 altext.setScript(event->cleanText()); 00740 else 00741 altext.setText(event->cleanText()); 00742 setAction(action, altext); 00743 if (action == KAEvent::MESSAGE && event->kmailSerialNumber() 00744 && AlarmText::checkIfEmail(event->cleanText())) 00745 mKMailSerialNumber = event->kmailSerialNumber(); 00746 if (action == KAEvent::EMAIL) 00747 mEmailAttachList->insertStringList(event->emailAttachments()); 00748 00749 mLateCancel->setMinutes(event->lateCancel(), event->startDateTime().isDateOnly(), 00750 TimePeriod::HOURS_MINUTES); 00751 mLateCancel->showAutoClose(action == KAEvent::MESSAGE || action == KAEvent::FILE); 00752 mLateCancel->setAutoClose(event->autoClose()); 00753 mLateCancel->setFixedSize(mLateCancel->sizeHint()); 00754 if (mShowInKorganizer) 00755 mShowInKorganizer->setChecked(event->copyToKOrganizer()); 00756 mConfirmAck->setChecked(event->confirmAck()); 00757 int reminder = event->reminder(); 00758 if (!reminder && event->reminderDeferral() && !recurs) 00759 { 00760 reminder = event->reminderDeferral(); 00761 mReminderDeferral = true; 00762 } 00763 if (!reminder && event->reminderArchived() && recurs) 00764 { 00765 reminder = event->reminderArchived(); 00766 mReminderArchived = true; 00767 } 00768 mReminder->setMinutes(reminder, (mTimeWidget ? mTimeWidget->anyTime() : mTemplateAnyTime->isOn())); 00769 mReminder->setOnceOnly(event->reminderOnceOnly()); 00770 mReminder->enableOnceOnly(event->recurs()); 00771 if (mSpecialActionsButton) 00772 mSpecialActionsButton->setActions(event->preAction(), event->postAction()); 00773 mRecurrenceEdit->set(*event, (mTemplate || event->isTemplate())); // must be called after mTimeWidget is set up, to ensure correct date-only enabling 00774 mTabs->setTabLabel(mTabs->page(mRecurPageIndex), recurText(*event)); 00775 SoundPicker::Type soundType = event->speak() ? SoundPicker::SPEAK 00776 : event->beep() ? SoundPicker::BEEP 00777 : !event->audioFile().isEmpty() ? SoundPicker::PLAY_FILE 00778 : SoundPicker::NONE; 00779 mSoundPicker->set(soundType, event->audioFile(), event->soundVolume(), 00780 event->fadeVolume(), event->fadeSeconds(), event->repeatSound()); 00781 CmdLogType logType = event->commandXterm() ? EXEC_IN_TERMINAL 00782 : !event->logFile().isEmpty() ? LOG_TO_FILE 00783 : DISCARD_OUTPUT; 00784 if (logType == LOG_TO_FILE) 00785 mCmdLogFileEdit->setText(event->logFile()); // set file name before setting radio button 00786 mCmdOutputGroup->setButton(logType); 00787 mEmailToEdit->setText(event->emailAddresses(", ")); 00788 mEmailSubjectEdit->setText(event->emailSubject()); 00789 mEmailBcc->setChecked(event->emailBcc()); 00790 if (mEmailFromList) 00791 mEmailFromList->setCurrentIdentity(event->emailFromId()); 00792 } 00793 else 00794 { 00795 // Set the values to their defaults 00796 if (!ShellProcess::authorised()) 00797 { 00798 // Don't allow shell commands in kiosk mode 00799 mCommandRadio->setEnabled(false); 00800 if (mSpecialActionsButton) 00801 mSpecialActionsButton->setEnabled(false); 00802 } 00803 mFontColourButton->setDefaultFont(); 00804 mFontColourButton->setBgColour(Preferences::defaultBgColour()); 00805 mFontColourButton->setFgColour(Preferences::defaultFgColour()); 00806 mBgColourButton->setColour(Preferences::defaultBgColour()); 00807 TQDateTime defaultTime = TQDateTime::currentDateTime().addSecs(60); 00808 if (mTemplate) 00809 { 00810 mTemplateTimeGroup->setButton(mTemplateTimeGroup->id(mTemplateDefaultTime)); 00811 mTemplateTime->setValue(0); 00812 mTemplateTimeAfter->setValue(1); 00813 } 00814 else 00815 mTimeWidget->setDateTime(defaultTime); 00816 mActionGroup->setButton(mActionGroup->id(mMessageRadio)); 00817 mLateCancel->setMinutes((Preferences::defaultLateCancel() ? 1 : 0), false, TimePeriod::HOURS_MINUTES); 00818 mLateCancel->showAutoClose(true); 00819 mLateCancel->setAutoClose(Preferences::defaultAutoClose()); 00820 mLateCancel->setFixedSize(mLateCancel->sizeHint()); 00821 if (mShowInKorganizer) 00822 mShowInKorganizer->setChecked(Preferences::defaultCopyToKOrganizer()); 00823 mConfirmAck->setChecked(Preferences::defaultConfirmAck()); 00824 if (mSpecialActionsButton) 00825 mSpecialActionsButton->setActions(Preferences::defaultPreAction(), Preferences::defaultPostAction()); 00826 mRecurrenceEdit->setDefaults(defaultTime); // must be called after mTimeWidget is set up, to ensure correct date-only enabling 00827 slotRecurFrequencyChange(); // update the Recurrence text 00828 mReminder->setMinutes(0, false); 00829 mReminder->enableOnceOnly(mRecurrenceEdit->isTimedRepeatType()); // must be called after mRecurrenceEdit is set up 00830 mSoundPicker->set(Preferences::defaultSoundType(), Preferences::defaultSoundFile(), 00831 Preferences::defaultSoundVolume(), -1, 0, Preferences::defaultSoundRepeat()); 00832 mCmdTypeScript->setChecked(Preferences::defaultCmdScript()); 00833 mCmdLogFileEdit->setText(Preferences::defaultCmdLogFile()); // set file name before setting radio button 00834 mCmdOutputGroup->setButton(Preferences::defaultCmdLogType()); 00835 mEmailBcc->setChecked(Preferences::defaultEmailBcc()); 00836 } 00837 slotCmdScriptToggled(mCmdTypeScript->isChecked()); 00838 00839 if (!deferGroupVisible) 00840 mDeferGroup->hide(); 00841 00842 bool enable = !!mEmailAttachList->count(); 00843 mEmailAttachList->setEnabled(enable); 00844 if (mEmailRemoveButton) 00845 mEmailRemoveButton->setEnabled(enable); 00846 AlarmCalendar* cal = AlarmCalendar::templateCalendar(); 00847 bool empty = cal->isOpen() && !cal->events().count(); 00848 enableButton(Default, !empty); 00849 } 00850 00851 /****************************************************************************** 00852 * Set the read-only status of all non-template controls. 00853 */ 00854 void EditAlarmDlg::setReadOnly() 00855 { 00856 // Common controls 00857 mMessageRadio->setReadOnly(mReadOnly); 00858 mFileRadio->setReadOnly(mReadOnly); 00859 mCommandRadio->setReadOnly(mReadOnly); 00860 mEmailRadio->setReadOnly(mReadOnly); 00861 if (mTimeWidget) 00862 mTimeWidget->setReadOnly(mReadOnly); 00863 mLateCancel->setReadOnly(mReadOnly); 00864 if (mReadOnly) 00865 mDeferChangeButton->hide(); 00866 else 00867 mDeferChangeButton->show(); 00868 if (mShowInKorganizer) 00869 mShowInKorganizer->setReadOnly(mReadOnly); 00870 00871 // Message alarm controls 00872 mTextMessageEdit->setReadOnly(mReadOnly); 00873 mFileMessageEdit->setReadOnly(mReadOnly); 00874 mFontColourButton->setReadOnly(mReadOnly); 00875 mBgColourButton->setReadOnly(mReadOnly); 00876 mSoundPicker->setReadOnly(mReadOnly); 00877 mConfirmAck->setReadOnly(mReadOnly); 00878 mReminder->setReadOnly(mReadOnly); 00879 if (mSpecialActionsButton) 00880 mSpecialActionsButton->setReadOnly(mReadOnly); 00881 if (mReadOnly) 00882 { 00883 mFileBrowseButton->hide(); 00884 mFontColourButton->hide(); 00885 } 00886 else 00887 { 00888 mFileBrowseButton->show(); 00889 mFontColourButton->show(); 00890 } 00891 00892 // Command alarm controls 00893 mCmdTypeScript->setReadOnly(mReadOnly); 00894 mCmdCommandEdit->setReadOnly(mReadOnly); 00895 mCmdScriptEdit->setReadOnly(mReadOnly); 00896 for (int id = DISCARD_OUTPUT; id < EXEC_IN_TERMINAL; ++id) 00897 ((RadioButton*)mCmdOutputGroup->find(id))->setReadOnly(mReadOnly); 00898 00899 // Email alarm controls 00900 mEmailToEdit->setReadOnly(mReadOnly); 00901 mEmailSubjectEdit->setReadOnly(mReadOnly); 00902 mEmailMessageEdit->setReadOnly(mReadOnly); 00903 mEmailBcc->setReadOnly(mReadOnly); 00904 if (mEmailFromList) 00905 mEmailFromList->setReadOnly(mReadOnly); 00906 if (mReadOnly) 00907 { 00908 mEmailAddressButton->hide(); 00909 mEmailAddAttachButton->hide(); 00910 mEmailRemoveButton->hide(); 00911 } 00912 else 00913 { 00914 mEmailAddressButton->show(); 00915 mEmailAddAttachButton->show(); 00916 mEmailRemoveButton->show(); 00917 } 00918 } 00919 00920 /****************************************************************************** 00921 * Set the dialog's action and the action's text. 00922 */ 00923 void EditAlarmDlg::setAction(KAEvent::Action action, const AlarmText& alarmText) 00924 { 00925 TQString text = alarmText.displayText(); 00926 bool script; 00927 TQRadioButton* radio; 00928 switch (action) 00929 { 00930 case KAEvent::FILE: 00931 radio = mFileRadio; 00932 mFileMessageEdit->setText(text); 00933 break; 00934 case KAEvent::COMMAND: 00935 radio = mCommandRadio; 00936 script = alarmText.isScript(); 00937 mCmdTypeScript->setChecked(script); 00938 if (script) 00939 mCmdScriptEdit->setText(text); 00940 else 00941 mCmdCommandEdit->setText(text); 00942 break; 00943 case KAEvent::EMAIL: 00944 radio = mEmailRadio; 00945 mEmailMessageEdit->setText(text); 00946 break; 00947 case KAEvent::MESSAGE: 00948 default: 00949 radio = mMessageRadio; 00950 mTextMessageEdit->setText(text); 00951 mKMailSerialNumber = 0; 00952 if (alarmText.isEmail()) 00953 { 00954 mKMailSerialNumber = alarmText.kmailSerialNumber(); 00955 00956 // Set up email fields also, in case the user wants an email alarm 00957 mEmailToEdit->setText(alarmText.to()); 00958 mEmailSubjectEdit->setText(alarmText.subject()); 00959 mEmailMessageEdit->setText(alarmText.body()); 00960 } 00961 else if (alarmText.isScript()) 00962 { 00963 // Set up command script field also, in case the user wants a command alarm 00964 mCmdScriptEdit->setText(text); 00965 mCmdTypeScript->setChecked(true); 00966 } 00967 break; 00968 } 00969 mActionGroup->setButton(mActionGroup->id(radio)); 00970 } 00971 00972 /****************************************************************************** 00973 * Create an "acknowledgement confirmation required" checkbox. 00974 */ 00975 CheckBox* EditAlarmDlg::createConfirmAckCheckbox(TQWidget* parent, const char* name) 00976 { 00977 CheckBox* widget = new CheckBox(i18n_k_ConfirmAck(), parent, name); 00978 TQWhatsThis::add(widget, 00979 i18n("Check to be prompted for confirmation when you acknowledge the alarm.")); 00980 return widget; 00981 } 00982 00983 /****************************************************************************** 00984 * Save the state of all controls. 00985 */ 00986 void EditAlarmDlg::saveState(const KAEvent* event) 00987 { 00988 delete mSavedEvent; 00989 mSavedEvent = 0; 00990 if (event) 00991 mSavedEvent = new KAEvent(*event); 00992 if (mTemplate) 00993 { 00994 mSavedTemplateName = mTemplateName->text(); 00995 mSavedTemplateTimeType = mTemplateTimeGroup->selected(); 00996 mSavedTemplateTime = mTemplateTime->time(); 00997 mSavedTemplateAfterTime = mTemplateTimeAfter->value(); 00998 } 00999 mSavedTypeRadio = mActionGroup->selected(); 01000 mSavedSoundType = mSoundPicker->sound(); 01001 mSavedSoundFile = mSoundPicker->file(); 01002 mSavedSoundVolume = mSoundPicker->volume(mSavedSoundFadeVolume, mSavedSoundFadeSeconds); 01003 mSavedRepeatSound = mSoundPicker->repeat(); 01004 mSavedConfirmAck = mConfirmAck->isChecked(); 01005 mSavedFont = mFontColourButton->font(); 01006 mSavedFgColour = mFontColourButton->fgColour(); 01007 mSavedBgColour = mFileRadio->isOn() ? mBgColourButton->colour() : mFontColourButton->bgColour(); 01008 mSavedReminder = mReminder->minutes(); 01009 mSavedOnceOnly = mReminder->isOnceOnly(); 01010 if (mSpecialActionsButton) 01011 { 01012 mSavedPreAction = mSpecialActionsButton->preAction(); 01013 mSavedPostAction = mSpecialActionsButton->postAction(); 01014 } 01015 checkText(mSavedTextFileCommandMessage, false); 01016 mSavedCmdScript = mCmdTypeScript->isChecked(); 01017 mSavedCmdOutputRadio = mCmdOutputGroup->selected(); 01018 mSavedCmdLogFile = mCmdLogFileEdit->text(); 01019 if (mEmailFromList) 01020 mSavedEmailFrom = mEmailFromList->currentIdentityName(); 01021 mSavedEmailTo = mEmailToEdit->text(); 01022 mSavedEmailSubject = mEmailSubjectEdit->text(); 01023 mSavedEmailAttach.clear(); 01024 for (int i = 0; i < mEmailAttachList->count(); ++i) 01025 mSavedEmailAttach += mEmailAttachList->text(i); 01026 mSavedEmailBcc = mEmailBcc->isChecked(); 01027 if (mTimeWidget) 01028 mSavedDateTime = mTimeWidget->getDateTime(0, false, false); 01029 mSavedLateCancel = mLateCancel->minutes(); 01030 mSavedAutoClose = mLateCancel->isAutoClose(); 01031 if (mShowInKorganizer) 01032 mSavedShowInKorganizer = mShowInKorganizer->isChecked(); 01033 mSavedRecurrenceType = mRecurrenceEdit->repeatType(); 01034 } 01035 01036 /****************************************************************************** 01037 * Check whether any of the controls has changed state since the dialog was 01038 * first displayed. 01039 * Reply = true if any non-deferral controls have changed, or if it's a new event. 01040 * = false if no non-deferral controls have changed. In this case, 01041 * mOnlyDeferred indicates whether deferral controls may have changed. 01042 */ 01043 bool EditAlarmDlg::stateChanged() const 01044 { 01045 mChanged = true; 01046 mOnlyDeferred = false; 01047 if (!mSavedEvent) 01048 return true; 01049 TQString textFileCommandMessage; 01050 checkText(textFileCommandMessage, false); 01051 if (mTemplate) 01052 { 01053 if (mSavedTemplateName != mTemplateName->text() 01054 || mSavedTemplateTimeType != mTemplateTimeGroup->selected() 01055 || mTemplateUseTime->isOn() && mSavedTemplateTime != mTemplateTime->time() 01056 || mTemplateUseTimeAfter->isOn() && mSavedTemplateAfterTime != mTemplateTimeAfter->value()) 01057 return true; 01058 } 01059 else 01060 if (mSavedDateTime != mTimeWidget->getDateTime(0, false, false)) 01061 return true; 01062 if (mSavedTypeRadio != mActionGroup->selected() 01063 || mSavedLateCancel != mLateCancel->minutes() 01064 || mShowInKorganizer && mSavedShowInKorganizer != mShowInKorganizer->isChecked() 01065 || textFileCommandMessage != mSavedTextFileCommandMessage 01066 || mSavedRecurrenceType != mRecurrenceEdit->repeatType()) 01067 return true; 01068 if (mMessageRadio->isOn() || mFileRadio->isOn()) 01069 { 01070 if (mSavedSoundType != mSoundPicker->sound() 01071 || mSavedConfirmAck != mConfirmAck->isChecked() 01072 || mSavedFont != mFontColourButton->font() 01073 || mSavedFgColour != mFontColourButton->fgColour() 01074 || mSavedBgColour != (mFileRadio->isOn() ? mBgColourButton->colour() : mFontColourButton->bgColour()) 01075 || mSavedReminder != mReminder->minutes() 01076 || mSavedOnceOnly != mReminder->isOnceOnly() 01077 || mSavedAutoClose != mLateCancel->isAutoClose()) 01078 return true; 01079 if (mSpecialActionsButton) 01080 { 01081 if (mSavedPreAction != mSpecialActionsButton->preAction() 01082 || mSavedPostAction != mSpecialActionsButton->postAction()) 01083 return true; 01084 } 01085 if (mSavedSoundType == SoundPicker::PLAY_FILE) 01086 { 01087 if (mSavedSoundFile != mSoundPicker->file()) 01088 return true; 01089 if (!mSavedSoundFile.isEmpty()) 01090 { 01091 float fadeVolume; 01092 int fadeSecs; 01093 if (mSavedRepeatSound != mSoundPicker->repeat() 01094 || mSavedSoundVolume != mSoundPicker->volume(fadeVolume, fadeSecs) 01095 || mSavedSoundFadeVolume != fadeVolume 01096 || mSavedSoundFadeSeconds != fadeSecs) 01097 return true; 01098 } 01099 } 01100 } 01101 else if (mCommandRadio->isOn()) 01102 { 01103 if (mSavedCmdScript != mCmdTypeScript->isChecked() 01104 || mSavedCmdOutputRadio != mCmdOutputGroup->selected()) 01105 return true; 01106 if (mCmdOutputGroup->selectedId() == LOG_TO_FILE) 01107 { 01108 if (mSavedCmdLogFile != mCmdLogFileEdit->text()) 01109 return true; 01110 } 01111 } 01112 else if (mEmailRadio->isOn()) 01113 { 01114 TQStringList emailAttach; 01115 for (int i = 0; i < mEmailAttachList->count(); ++i) 01116 emailAttach += mEmailAttachList->text(i); 01117 if (mEmailFromList && mSavedEmailFrom != mEmailFromList->currentIdentityName() 01118 || mSavedEmailTo != mEmailToEdit->text() 01119 || mSavedEmailSubject != mEmailSubjectEdit->text() 01120 || mSavedEmailAttach != emailAttach 01121 || mSavedEmailBcc != mEmailBcc->isChecked()) 01122 return true; 01123 } 01124 if (mRecurrenceEdit->stateChanged()) 01125 return true; 01126 if (mSavedEvent && mSavedEvent->deferred()) 01127 mOnlyDeferred = true; 01128 mChanged = false; 01129 return false; 01130 } 01131 01132 /****************************************************************************** 01133 * Get the currently entered dialogue data. 01134 * The data is returned in the supplied KAEvent instance. 01135 * Reply = false if the only change has been to an existing deferral. 01136 */ 01137 bool EditAlarmDlg::getEvent(KAEvent& event) 01138 { 01139 if (mChanged) 01140 { 01141 // It's a new event, or the edit controls have changed 01142 setEvent(event, mAlarmMessage, false); 01143 return true; 01144 } 01145 01146 // Only the deferral time may have changed 01147 event = *mSavedEvent; 01148 if (mOnlyDeferred) 01149 { 01150 // Just modify the original event, to avoid expired recurring events 01151 // being returned as rubbish. 01152 if (mDeferDateTime.isValid()) 01153 event.defer(mDeferDateTime, event.reminderDeferral(), false); 01154 else 01155 event.cancelDefer(); 01156 } 01157 return false; 01158 } 01159 01160 /****************************************************************************** 01161 * Extract the data in the dialogue and set up a KAEvent from it. 01162 * If 'trial' is true, the event is set up for a simple one-off test, ignoring 01163 * recurrence, reminder, template etc. data. 01164 */ 01165 void EditAlarmDlg::setEvent(KAEvent& event, const TQString& text, bool trial) 01166 { 01167 TQDateTime dt; 01168 if (!trial) 01169 { 01170 if (!mTemplate) 01171 dt = mAlarmDateTime.dateTime(); 01172 else if (mTemplateUseTime->isOn()) 01173 dt = TQDateTime(TQDate(2000,1,1), mTemplateTime->time()); 01174 } 01175 KAEvent::Action type = getAlarmType(); 01176 event.set(dt, text, (mFileRadio->isOn() ? mBgColourButton->colour() : mFontColourButton->bgColour()), 01177 mFontColourButton->fgColour(), mFontColourButton->font(), 01178 type, (trial ? 0 : mLateCancel->minutes()), getAlarmFlags()); 01179 switch (type) 01180 { 01181 case KAEvent::MESSAGE: 01182 if (AlarmText::checkIfEmail(text)) 01183 event.setKMailSerialNumber(mKMailSerialNumber); 01184 // fall through to FILE 01185 case KAEvent::FILE: 01186 { 01187 float fadeVolume; 01188 int fadeSecs; 01189 float volume = mSoundPicker->volume(fadeVolume, fadeSecs); 01190 event.setAudioFile(mSoundPicker->file(), volume, fadeVolume, fadeSecs); 01191 if (!trial) 01192 event.setReminder(mReminder->minutes(), mReminder->isOnceOnly()); 01193 if (mSpecialActionsButton) 01194 event.setActions(mSpecialActionsButton->preAction(), mSpecialActionsButton->postAction()); 01195 break; 01196 } 01197 case KAEvent::EMAIL: 01198 { 01199 uint from = mEmailFromList ? mEmailFromList->currentIdentity() : 0; 01200 event.setEmail(from, mEmailAddresses, mEmailSubjectEdit->text(), mEmailAttachments); 01201 break; 01202 } 01203 case KAEvent::COMMAND: 01204 if (mCmdOutputGroup->selectedId() == LOG_TO_FILE) 01205 event.setLogFile(mCmdLogFileEdit->text()); 01206 break; 01207 default: 01208 break; 01209 } 01210 if (!trial) 01211 { 01212 if (mRecurrenceEdit->repeatType() != RecurrenceEdit::NO_RECUR) 01213 { 01214 mRecurrenceEdit->updateEvent(event, !mTemplate); 01215 TQDateTime now = TQDateTime::currentDateTime(); 01216 bool dateOnly = mAlarmDateTime.isDateOnly(); 01217 if (dateOnly && mAlarmDateTime.date() < now.date() 01218 || !dateOnly && mAlarmDateTime.rawDateTime() < now) 01219 { 01220 // A timed recurrence has an entered start date which has 01221 // already expired, so we must adjust the next repetition. 01222 event.setNextOccurrence(now); 01223 } 01224 mAlarmDateTime = event.startDateTime(); 01225 if (mDeferDateTime.isValid() && mDeferDateTime < mAlarmDateTime) 01226 { 01227 bool deferral = true; 01228 bool deferReminder = false; 01229 int reminder = mReminder->minutes(); 01230 if (reminder) 01231 { 01232 DateTime remindTime = mAlarmDateTime.addMins(-reminder); 01233 if (mDeferDateTime >= remindTime) 01234 { 01235 if (remindTime > TQDateTime::currentDateTime()) 01236 deferral = false; // ignore deferral if it's after next reminder 01237 else if (mDeferDateTime > remindTime) 01238 deferReminder = true; // it's the reminder which is being deferred 01239 } 01240 } 01241 if (deferral) 01242 event.defer(mDeferDateTime, deferReminder, false); 01243 } 01244 } 01245 if (mTemplate) 01246 { 01247 int afterTime = mTemplateDefaultTime->isOn() ? 0 01248 : mTemplateUseTimeAfter->isOn() ? mTemplateTimeAfter->value() : -1; 01249 event.setTemplate(mTemplateName->text(), afterTime); 01250 } 01251 } 01252 } 01253 01254 /****************************************************************************** 01255 * Get the currently specified alarm flag bits. 01256 */ 01257 int EditAlarmDlg::getAlarmFlags() const 01258 { 01259 bool displayAlarm = mMessageRadio->isOn() || mFileRadio->isOn(); 01260 bool cmdAlarm = mCommandRadio->isOn(); 01261 bool emailAlarm = mEmailRadio->isOn(); 01262 return (displayAlarm && mSoundPicker->sound() == SoundPicker::BEEP ? KAEvent::BEEP : 0) 01263 | (displayAlarm && mSoundPicker->sound() == SoundPicker::SPEAK ? KAEvent::SPEAK : 0) 01264 | (displayAlarm && mSoundPicker->repeat() ? KAEvent::REPEAT_SOUND : 0) 01265 | (displayAlarm && mConfirmAck->isChecked() ? KAEvent::CONFIRM_ACK : 0) 01266 | (displayAlarm && mLateCancel->isAutoClose() ? KAEvent::AUTO_CLOSE : 0) 01267 | (cmdAlarm && mCmdTypeScript->isChecked() ? KAEvent::SCRIPT : 0) 01268 | (cmdAlarm && mCmdOutputGroup->selectedId() == EXEC_IN_TERMINAL ? KAEvent::EXEC_IN_XTERM : 0) 01269 | (emailAlarm && mEmailBcc->isChecked() ? KAEvent::EMAIL_BCC : 0) 01270 | (mShowInKorganizer && mShowInKorganizer->isChecked() ? KAEvent::COPY_KORGANIZER : 0) 01271 | (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN ? KAEvent::REPEAT_AT_LOGIN : 0) 01272 | ((mTemplate ? mTemplateAnyTime->isOn() : mAlarmDateTime.isDateOnly()) ? KAEvent::ANY_TIME : 0) 01273 | (mFontColourButton->defaultFont() ? KAEvent::DEFAULT_FONT : 0); 01274 } 01275 01276 /****************************************************************************** 01277 * Get the currently selected alarm type. 01278 */ 01279 KAEvent::Action EditAlarmDlg::getAlarmType() const 01280 { 01281 return mFileRadio->isOn() ? KAEvent::FILE 01282 : mCommandRadio->isOn() ? KAEvent::COMMAND 01283 : mEmailRadio->isOn() ? KAEvent::EMAIL 01284 : KAEvent::MESSAGE; 01285 } 01286 01287 /****************************************************************************** 01288 * Called when the dialog is displayed. 01289 * The first time through, sets the size to the same as the last time it was 01290 * displayed. 01291 */ 01292 void EditAlarmDlg::showEvent(TQShowEvent* se) 01293 { 01294 if (!mDeferGroupHeight) 01295 { 01296 mDeferGroupHeight = mDeferGroup->height() + spacingHint(); 01297 TQSize s; 01298 if (KAlarm::readConfigWindowSize(EDIT_DIALOG_NAME, s)) 01299 s.setHeight(s.height() + (mDeferGroup->isHidden() ? 0 : mDeferGroupHeight)); 01300 else 01301 s = minimumSize(); 01302 resize(s); 01303 } 01304 KWin::setOnDesktop(winId(), mDesktop); // ensure it displays on the desktop expected by the user 01305 KDialog::showEvent(se); 01306 } 01307 01308 /****************************************************************************** 01309 * Called when the dialog's size has changed. 01310 * Records the new size (adjusted to ignore the optional height of the deferred 01311 * time edit widget) in the config file. 01312 */ 01313 void EditAlarmDlg::resizeEvent(TQResizeEvent* re) 01314 { 01315 if (isVisible()) 01316 { 01317 TQSize s = re->size(); 01318 s.setHeight(s.height() - (mDeferGroup->isHidden() ? 0 : mDeferGroupHeight)); 01319 KAlarm::writeConfigWindowSize(EDIT_DIALOG_NAME, s); 01320 } 01321 KDialog::resizeEvent(re); 01322 } 01323 01324 /****************************************************************************** 01325 * Called when the OK button is clicked. 01326 * Validate the input data. 01327 */ 01328 void EditAlarmDlg::slotOk() 01329 { 01330 if (!stateChanged()) 01331 { 01332 // No changes have been made except possibly to an existing deferral 01333 if (!mOnlyDeferred) 01334 reject(); 01335 else 01336 accept(); 01337 return; 01338 } 01339 RecurrenceEdit::RepeatType recurType = mRecurrenceEdit->repeatType(); 01340 if (mTimeWidget 01341 && mTabs->currentPageIndex() == mRecurPageIndex && recurType == RecurrenceEdit::AT_LOGIN) 01342 mTimeWidget->setDateTime(mRecurrenceEdit->endDateTime()); 01343 bool timedRecurrence = mRecurrenceEdit->isTimedRepeatType(); // does it recur other than at login? 01344 if (mTemplate) 01345 { 01346 // Check that the template name is not blank and is unique 01347 TQString errmsg; 01348 TQString name = mTemplateName->text(); 01349 if (name.isEmpty()) 01350 errmsg = i18n("You must enter a name for the alarm template"); 01351 else if (name != mSavedTemplateName) 01352 { 01353 AlarmCalendar* cal = AlarmCalendar::templateCalendarOpen(); 01354 if (cal && KAEvent::findTemplateName(*cal, name).valid()) 01355 errmsg = i18n("Template name is already in use"); 01356 } 01357 if (!errmsg.isEmpty()) 01358 { 01359 mTemplateName->setFocus(); 01360 KMessageBox::sorry(this, errmsg); 01361 return; 01362 } 01363 } 01364 else 01365 { 01366 TQWidget* errWidget; 01367 mAlarmDateTime = mTimeWidget->getDateTime(0, !timedRecurrence, false, &errWidget); 01368 if (errWidget) 01369 { 01370 // It's more than just an existing deferral being changed, so the time matters 01371 mTabs->setCurrentPage(mMainPageIndex); 01372 errWidget->setFocus(); 01373 mTimeWidget->getDateTime(); // display the error message now 01374 return; 01375 } 01376 } 01377 if (!checkCommandData() 01378 || !checkEmailData()) 01379 return; 01380 if (!mTemplate) 01381 { 01382 if (timedRecurrence) 01383 { 01384 TQDateTime now = TQDateTime::currentDateTime(); 01385 if (mAlarmDateTime.date() < now.date() 01386 || mAlarmDateTime.date() == now.date() 01387 && !mAlarmDateTime.isDateOnly() && mAlarmDateTime.time() < now.time()) 01388 { 01389 // A timed recurrence has an entered start date which 01390 // has already expired, so we must adjust it. 01391 KAEvent event; 01392 getEvent(event); // this may adjust mAlarmDateTime 01393 if (( mAlarmDateTime.date() < now.date() 01394 || mAlarmDateTime.date() == now.date() 01395 && !mAlarmDateTime.isDateOnly() && mAlarmDateTime.time() < now.time()) 01396 && event.nextOccurrence(now, mAlarmDateTime, KAEvent::ALLOW_FOR_REPETITION) == KAEvent::NO_OCCURRENCE) 01397 { 01398 KMessageBox::sorry(this, i18n("Recurrence has already expired")); 01399 return; 01400 } 01401 } 01402 } 01403 TQString errmsg; 01404 TQWidget* errWidget = mRecurrenceEdit->checkData(mAlarmDateTime.dateTime(), errmsg); 01405 if (errWidget) 01406 { 01407 mTabs->setCurrentPage(mRecurPageIndex); 01408 errWidget->setFocus(); 01409 KMessageBox::sorry(this, errmsg); 01410 return; 01411 } 01412 } 01413 if (recurType != RecurrenceEdit::NO_RECUR) 01414 { 01415 KAEvent recurEvent; 01416 int longestRecurInterval = -1; 01417 int reminder = mReminder->minutes(); 01418 if (reminder && !mReminder->isOnceOnly()) 01419 { 01420 mRecurrenceEdit->updateEvent(recurEvent, false); 01421 longestRecurInterval = recurEvent.longestRecurrenceInterval(); 01422 if (longestRecurInterval && reminder >= longestRecurInterval) 01423 { 01424 mTabs->setCurrentPage(mMainPageIndex); 01425 mReminder->setFocusOnCount(); 01426 KMessageBox::sorry(this, i18n("Reminder period must be less than the recurrence interval, unless '%1' is checked." 01427 ).arg(Reminder::i18n_first_recurrence_only())); 01428 return; 01429 } 01430 } 01431 if (mRecurrenceEdit->subRepeatCount()) 01432 { 01433 if (longestRecurInterval < 0) 01434 { 01435 mRecurrenceEdit->updateEvent(recurEvent, false); 01436 longestRecurInterval = recurEvent.longestRecurrenceInterval(); 01437 } 01438 if (longestRecurInterval > 0 01439 && recurEvent.repeatInterval() * recurEvent.repeatCount() >= longestRecurInterval - reminder) 01440 { 01441 KMessageBox::sorry(this, i18n("The duration of a repetition within the recurrence must be less than the recurrence interval minus any reminder period")); 01442 mRecurrenceEdit->activateSubRepetition(); // display the alarm repetition dialog again 01443 return; 01444 } 01445 if (recurEvent.repeatInterval() % 1440 01446 && (mTemplate && mTemplateAnyTime->isOn() || !mTemplate && mAlarmDateTime.isDateOnly())) 01447 { 01448 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")); 01449 mRecurrenceEdit->activateSubRepetition(); // display the alarm repetition dialog again 01450 return; 01451 } 01452 } 01453 } 01454 if (checkText(mAlarmMessage)) 01455 accept(); 01456 } 01457 01458 /****************************************************************************** 01459 * Called when the Try button is clicked. 01460 * Display/execute the alarm immediately for the user to check its configuration. 01461 */ 01462 void EditAlarmDlg::slotTry() 01463 { 01464 TQString text; 01465 if (checkText(text)) 01466 { 01467 if (mEmailRadio->isOn()) 01468 { 01469 if (!checkEmailData() 01470 || KMessageBox::warningContinueCancel(this, i18n("Do you really want to send the email now to the specified recipient(s)?"), 01471 i18n("Confirm Email"), i18n("&Send")) != KMessageBox::Continue) 01472 return; 01473 } 01474 KAEvent event; 01475 setEvent(event, text, true); 01476 void* proc = theApp()->execAlarm(event, event.firstAlarm(), false, false); 01477 if (proc) 01478 { 01479 if (mCommandRadio->isOn() && mCmdOutputGroup->selectedId() != EXEC_IN_TERMINAL) 01480 { 01481 theApp()->commandMessage((ShellProcess*)proc, this); 01482 KMessageBox::information(this, i18n("Command executed:\n%1").arg(text)); 01483 theApp()->commandMessage((ShellProcess*)proc, 0); 01484 } 01485 else if (mEmailRadio->isOn()) 01486 { 01487 TQString bcc; 01488 if (mEmailBcc->isChecked()) 01489 bcc = i18n("\nBcc: %1").arg(Preferences::emailBccAddress()); 01490 KMessageBox::information(this, i18n("Email sent to:\n%1%2").arg(mEmailAddresses.join("\n")).arg(bcc)); 01491 } 01492 } 01493 } 01494 } 01495 01496 /****************************************************************************** 01497 * Called when the Cancel button is clicked. 01498 */ 01499 void EditAlarmDlg::slotCancel() 01500 { 01501 reject(); 01502 } 01503 01504 /****************************************************************************** 01505 * Called when the Load Template button is clicked. 01506 * Prompt to select a template and initialise the dialogue with its contents. 01507 */ 01508 void EditAlarmDlg::slotDefault() 01509 { 01510 TemplatePickDlg dlg(this, "templPickDlg"); 01511 if (dlg.exec() == TQDialog::Accepted) 01512 initialise(dlg.selectedTemplate()); 01513 } 01514 01515 /****************************************************************************** 01516 * Called when the Change deferral button is clicked. 01517 */ 01518 void EditAlarmDlg::slotEditDeferral() 01519 { 01520 if (!mTimeWidget) 01521 return; 01522 bool limit = true; 01523 int repeatInterval; 01524 int repeatCount = mRecurrenceEdit->subRepeatCount(&repeatInterval); 01525 DateTime start = mSavedEvent->recurs() ? (mExpiredRecurrence ? DateTime() : mSavedEvent->mainDateTime()) 01526 : mTimeWidget->getDateTime(0, !repeatCount, !mExpiredRecurrence); 01527 if (!start.isValid()) 01528 { 01529 if (!mExpiredRecurrence) 01530 return; 01531 limit = false; 01532 } 01533 TQDateTime now = TQDateTime::currentDateTime(); 01534 if (limit) 01535 { 01536 if (repeatCount && start < now) 01537 { 01538 // Sub-repetition - find the time of the next one 01539 repeatInterval *= 60; 01540 int repetition = (start.secsTo(now) + repeatInterval - 1) / repeatInterval; 01541 if (repetition > repeatCount) 01542 { 01543 mTimeWidget->getDateTime(); // output the appropriate error message 01544 return; 01545 } 01546 start = start.addSecs(repetition * repeatInterval); 01547 } 01548 } 01549 01550 bool deferred = mDeferDateTime.isValid(); 01551 DeferAlarmDlg deferDlg(i18n("Defer Alarm"), (deferred ? mDeferDateTime : DateTime(now.addSecs(60))), 01552 deferred, this, "EditDeferDlg"); 01553 if (limit) 01554 { 01555 // Don't allow deferral past the next recurrence 01556 int reminder = mReminder->minutes(); 01557 if (reminder) 01558 { 01559 DateTime remindTime = start.addMins(-reminder); 01560 if (TQDateTime::currentDateTime() < remindTime) 01561 start = remindTime; 01562 } 01563 deferDlg.setLimit(start.addSecs(-60)); 01564 } 01565 if (deferDlg.exec() == TQDialog::Accepted) 01566 { 01567 mDeferDateTime = deferDlg.getDateTime(); 01568 mDeferTimeLabel->setText(mDeferDateTime.isValid() ? mDeferDateTime.formatLocale() : TQString()); 01569 } 01570 } 01571 01572 /****************************************************************************** 01573 * Called when the main page is shown. 01574 * Sets the focus widget to the first edit field. 01575 */ 01576 void EditAlarmDlg::slotShowMainPage() 01577 { 01578 slotAlarmTypeChanged(-1); 01579 if (!mMainPageShown) 01580 { 01581 if (mTemplateName) 01582 mTemplateName->setFocus(); 01583 mMainPageShown = true; 01584 } 01585 if (mTimeWidget) 01586 { 01587 if (!mReadOnly && mRecurPageShown && mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN) 01588 mTimeWidget->setDateTime(mRecurrenceEdit->endDateTime()); 01589 if (mReadOnly || mRecurrenceEdit->isTimedRepeatType()) 01590 mTimeWidget->setMinDateTime(); // don't set a minimum date/time 01591 else 01592 mTimeWidget->setMinDateTimeIsCurrent(); // set the minimum date/time to track the clock 01593 } 01594 } 01595 01596 /****************************************************************************** 01597 * Called when the recurrence edit page is shown. 01598 * The recurrence defaults are set to correspond to the start date. 01599 * The first time, for a new alarm, the recurrence end date is set according to 01600 * the alarm start time. 01601 */ 01602 void EditAlarmDlg::slotShowRecurrenceEdit() 01603 { 01604 mRecurPageIndex = mTabs->currentPageIndex(); 01605 if (!mReadOnly && !mTemplate) 01606 { 01607 TQDateTime now = TQDateTime::currentDateTime(); 01608 mAlarmDateTime = mTimeWidget->getDateTime(0, false, false); 01609 bool expired = (mAlarmDateTime.dateTime() < now); 01610 if (mRecurSetDefaultEndDate) 01611 { 01612 mRecurrenceEdit->setDefaultEndDate(expired ? now.date() : mAlarmDateTime.date()); 01613 mRecurSetDefaultEndDate = false; 01614 } 01615 mRecurrenceEdit->setStartDate(mAlarmDateTime.date(), now.date()); 01616 if (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN) 01617 mRecurrenceEdit->setEndDateTime(expired ? now : mAlarmDateTime); 01618 } 01619 mRecurPageShown = true; 01620 } 01621 01622 /****************************************************************************** 01623 * Called when the recurrence type selection changes. 01624 * Enables/disables date-only alarms as appropriate. 01625 * Enables/disables controls depending on at-login setting. 01626 */ 01627 void EditAlarmDlg::slotRecurTypeChange(int repeatType) 01628 { 01629 bool atLogin = (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN); 01630 if (!mTemplate) 01631 { 01632 bool recurs = (mRecurrenceEdit->repeatType() != RecurrenceEdit::NO_RECUR); 01633 if (mDeferGroup) 01634 mDeferGroup->setEnabled(recurs); 01635 mTimeWidget->enableAnyTime(!recurs || repeatType != RecurrenceEdit::SUBDAILY); 01636 if (atLogin) 01637 { 01638 mAlarmDateTime = mTimeWidget->getDateTime(0, false, false); 01639 mRecurrenceEdit->setEndDateTime(mAlarmDateTime.dateTime()); 01640 } 01641 mReminder->enableOnceOnly(recurs && !atLogin); 01642 } 01643 mReminder->setEnabled(!atLogin); 01644 mLateCancel->setEnabled(!atLogin); 01645 if (mShowInKorganizer) 01646 mShowInKorganizer->setEnabled(!atLogin); 01647 slotRecurFrequencyChange(); 01648 } 01649 01650 /****************************************************************************** 01651 * Called when the recurrence frequency selection changes, or the sub- 01652 * repetition interval changes. 01653 * Updates the recurrence frequency text. 01654 */ 01655 void EditAlarmDlg::slotRecurFrequencyChange() 01656 { 01657 slotSetSubRepetition(); 01658 KAEvent event; 01659 mRecurrenceEdit->updateEvent(event, false); 01660 mTabs->setTabLabel(mTabs->page(mRecurPageIndex), recurText(event)); 01661 } 01662 01663 /****************************************************************************** 01664 * Called when the Repetition within Recurrence button has been pressed to 01665 * display the sub-repetition dialog. 01666 * Alarm repetition has the following restrictions: 01667 * 1) Not allowed for a repeat-at-login alarm 01668 * 2) For a date-only alarm, the repeat interval must be a whole number of days. 01669 * 3) The overall repeat duration must be less than the recurrence interval. 01670 */ 01671 void EditAlarmDlg::slotSetSubRepetition() 01672 { 01673 bool dateOnly = mTemplate ? mTemplateAnyTime->isOn() : mTimeWidget->anyTime(); 01674 mRecurrenceEdit->setSubRepetition(mReminder->minutes(), dateOnly); 01675 } 01676 01677 /****************************************************************************** 01678 * Validate and convert command alarm data. 01679 */ 01680 bool EditAlarmDlg::checkCommandData() 01681 { 01682 if (mCommandRadio->isOn() && mCmdOutputGroup->selectedId() == LOG_TO_FILE) 01683 { 01684 // Validate the log file name 01685 TQString file = mCmdLogFileEdit->text(); 01686 TQFileInfo info(file); 01687 TQDir::setCurrent(TQDir::homeDirPath()); 01688 bool err = file.isEmpty() || info.isDir(); 01689 if (!err) 01690 { 01691 if (info.exists()) 01692 { 01693 err = !info.isWritable(); 01694 } 01695 else 01696 { 01697 TQFileInfo dirinfo(info.dirPath(true)); // get absolute directory path 01698 err = (!dirinfo.isDir() || !dirinfo.isWritable()); 01699 } 01700 } 01701 if (err) 01702 { 01703 mTabs->setCurrentPage(mMainPageIndex); 01704 mCmdLogFileEdit->setFocus(); 01705 KMessageBox::sorry(this, i18n("Log file must be the name or path of a local file, with write permission.")); 01706 return false; 01707 } 01708 // Convert the log file to an absolute path 01709 mCmdLogFileEdit->setText(info.absFilePath()); 01710 } 01711 return true; 01712 } 01713 01714 /****************************************************************************** 01715 * Convert the email addresses to a list, and validate them. Convert the email 01716 * attachments to a list. 01717 */ 01718 bool EditAlarmDlg::checkEmailData() 01719 { 01720 if (mEmailRadio->isOn()) 01721 { 01722 TQString addrs = mEmailToEdit->text(); 01723 if (addrs.isEmpty()) 01724 mEmailAddresses.clear(); 01725 else 01726 { 01727 TQString bad = KAMail::convertAddresses(addrs, mEmailAddresses); 01728 if (!bad.isEmpty()) 01729 { 01730 mEmailToEdit->setFocus(); 01731 KMessageBox::error(this, i18n("Invalid email address:\n%1").arg(bad)); 01732 return false; 01733 } 01734 } 01735 if (mEmailAddresses.isEmpty()) 01736 { 01737 mEmailToEdit->setFocus(); 01738 KMessageBox::error(this, i18n("No email address specified")); 01739 return false; 01740 } 01741 01742 mEmailAttachments.clear(); 01743 for (int i = 0; i < mEmailAttachList->count(); ++i) 01744 { 01745 TQString att = mEmailAttachList->text(i); 01746 switch (KAMail::checkAttachment(att)) 01747 { 01748 case 1: 01749 mEmailAttachments.append(att); 01750 break; 01751 case 0: 01752 break; // empty 01753 case -1: 01754 mEmailAttachList->setFocus(); 01755 KMessageBox::error(this, i18n("Invalid email attachment:\n%1").arg(att)); 01756 return false; 01757 } 01758 } 01759 } 01760 return true; 01761 } 01762 01763 /****************************************************************************** 01764 * Called when one of the alarm action type radio buttons is clicked, 01765 * to display the appropriate set of controls for that action type. 01766 */ 01767 void EditAlarmDlg::slotAlarmTypeChanged(int) 01768 { 01769 bool displayAlarm = false; 01770 TQWidget* focus = 0; 01771 if (mMessageRadio->isOn()) 01772 { 01773 mFileBox->hide(); 01774 mFilePadding->hide(); 01775 mTextMessageEdit->show(); 01776 mFontColourButton->show(); 01777 mBgColourBox->hide(); 01778 mSoundPicker->showSpeak(true); 01779 mDisplayAlarmsFrame->show(); 01780 mCommandFrame->hide(); 01781 mEmailFrame->hide(); 01782 mReminder->show(); 01783 mConfirmAck->show(); 01784 setButtonWhatsThis(Try, i18n("Display the alarm message now")); 01785 focus = mTextMessageEdit; 01786 displayAlarm = true; 01787 } 01788 else if (mFileRadio->isOn()) 01789 { 01790 mTextMessageEdit->hide(); 01791 mFileBox->show(); 01792 mFilePadding->show(); 01793 mFontColourButton->hide(); 01794 mBgColourBox->show(); 01795 mSoundPicker->showSpeak(false); 01796 mDisplayAlarmsFrame->show(); 01797 mCommandFrame->hide(); 01798 mEmailFrame->hide(); 01799 mReminder->show(); 01800 mConfirmAck->show(); 01801 setButtonWhatsThis(Try, i18n("Display the file now")); 01802 mFileMessageEdit->setNoSelect(); 01803 focus = mFileMessageEdit; 01804 displayAlarm = true; 01805 } 01806 else if (mCommandRadio->isOn()) 01807 { 01808 mDisplayAlarmsFrame->hide(); 01809 mCommandFrame->show(); 01810 mEmailFrame->hide(); 01811 mReminder->hide(); 01812 mConfirmAck->hide(); 01813 setButtonWhatsThis(Try, i18n("Execute the specified command now")); 01814 mCmdCommandEdit->setNoSelect(); 01815 focus = mCmdCommandEdit; 01816 } 01817 else if (mEmailRadio->isOn()) 01818 { 01819 mDisplayAlarmsFrame->hide(); 01820 mCommandFrame->hide(); 01821 mEmailFrame->show(); 01822 mReminder->hide(); 01823 mConfirmAck->hide(); 01824 setButtonWhatsThis(Try, i18n("Send the email to the specified addressees now")); 01825 mEmailToEdit->setNoSelect(); 01826 focus = mEmailToEdit; 01827 } 01828 mLateCancel->showAutoClose(displayAlarm); 01829 mLateCancel->setFixedSize(mLateCancel->sizeHint()); 01830 if (focus) 01831 focus->setFocus(); 01832 } 01833 01834 /****************************************************************************** 01835 * Called when one of the command type radio buttons is clicked, 01836 * to display the appropriate edit field. 01837 */ 01838 void EditAlarmDlg::slotCmdScriptToggled(bool on) 01839 { 01840 if (on) 01841 { 01842 mCmdCommandEdit->hide(); 01843 mCmdPadding->hide(); 01844 mCmdScriptEdit->show(); 01845 mCmdScriptEdit->setFocus(); 01846 } 01847 else 01848 { 01849 mCmdScriptEdit->hide(); 01850 mCmdCommandEdit->show(); 01851 mCmdPadding->show(); 01852 mCmdCommandEdit->setFocus(); 01853 } 01854 } 01855 01856 /****************************************************************************** 01857 * Called when one of the template time radio buttons is clicked, 01858 * to enable or disable the template time entry spin boxes. 01859 */ 01860 void EditAlarmDlg::slotTemplateTimeType(int) 01861 { 01862 mTemplateTime->setEnabled(mTemplateUseTime->isOn()); 01863 mTemplateTimeAfter->setEnabled(mTemplateUseTimeAfter->isOn()); 01864 } 01865 01866 /****************************************************************************** 01867 * Called when the "Any time" checkbox is toggled in the date/time widget. 01868 * Sets the advance reminder and late cancel units to days if any time is checked. 01869 */ 01870 void EditAlarmDlg::slotAnyTimeToggled(bool anyTime) 01871 { 01872 if (mReminder->isReminder()) 01873 mReminder->setDateOnly(anyTime); 01874 mLateCancel->setDateOnly(anyTime); 01875 } 01876 01877 /****************************************************************************** 01878 * Get a selection from the Address Book. 01879 */ 01880 void EditAlarmDlg::openAddressBook() 01881 { 01882 KABC::Addressee a = KABC::AddresseeDialog::getAddressee(this); 01883 if (a.isEmpty()) 01884 return; 01885 Person person(a.realName(), a.preferredEmail()); 01886 TQString addrs = mEmailToEdit->text().stripWhiteSpace(); 01887 if (!addrs.isEmpty()) 01888 addrs += ", "; 01889 addrs += person.fullName(); 01890 mEmailToEdit->setText(addrs); 01891 } 01892 01893 /****************************************************************************** 01894 * Select a file to attach to the email. 01895 */ 01896 void EditAlarmDlg::slotAddAttachment() 01897 { 01898 TQString url = KAlarm::browseFile(i18n("Choose File to Attach"), mAttachDefaultDir, TQString(), 01899 TQString(), KFile::ExistingOnly, this, "pickAttachFile"); 01900 if (!url.isEmpty()) 01901 { 01902 mEmailAttachList->insertItem(url); 01903 mEmailAttachList->setCurrentItem(mEmailAttachList->count() - 1); // select the new item 01904 mEmailRemoveButton->setEnabled(true); 01905 mEmailAttachList->setEnabled(true); 01906 } 01907 } 01908 01909 /****************************************************************************** 01910 * Remove the currently selected attachment from the email. 01911 */ 01912 void EditAlarmDlg::slotRemoveAttachment() 01913 { 01914 int item = mEmailAttachList->currentItem(); 01915 mEmailAttachList->removeItem(item); 01916 int count = mEmailAttachList->count(); 01917 if (item >= count) 01918 mEmailAttachList->setCurrentItem(count - 1); 01919 if (!count) 01920 { 01921 mEmailRemoveButton->setEnabled(false); 01922 mEmailAttachList->setEnabled(false); 01923 } 01924 } 01925 01926 /****************************************************************************** 01927 * Clean up the alarm text, and if it's a file, check whether it's valid. 01928 */ 01929 bool EditAlarmDlg::checkText(TQString& result, bool showErrorMessage) const 01930 { 01931 if (mMessageRadio->isOn()) 01932 result = mTextMessageEdit->text(); 01933 else if (mEmailRadio->isOn()) 01934 result = mEmailMessageEdit->text(); 01935 else if (mCommandRadio->isOn()) 01936 { 01937 if (mCmdTypeScript->isChecked()) 01938 result = mCmdScriptEdit->text(); 01939 else 01940 result = mCmdCommandEdit->text(); 01941 result = result.stripWhiteSpace(); 01942 } 01943 else if (mFileRadio->isOn()) 01944 { 01945 TQString alarmtext = mFileMessageEdit->text().stripWhiteSpace(); 01946 // Convert any relative file path to absolute 01947 // (using home directory as the default) 01948 enum Err { NONE = 0, BLANK, NONEXISTENT, DIRECTORY, UNREADABLE, NOT_TEXT_IMAGE }; 01949 Err err = NONE; 01950 KURL url; 01951 int i = alarmtext.find(TQString::fromLatin1("/")); 01952 if (i > 0 && alarmtext[i - 1] == ':') 01953 { 01954 url = alarmtext; 01955 url.cleanPath(); 01956 alarmtext = url.prettyURL(); 01957 KIO::UDSEntry uds; 01958 if (!KIO::NetAccess::stat(url, uds, MainWindow::mainMainWindow())) 01959 err = NONEXISTENT; 01960 else 01961 { 01962 KFileItem fi(uds, url); 01963 if (fi.isDir()) err = DIRECTORY; 01964 else if (!fi.isReadable()) err = UNREADABLE; 01965 } 01966 } 01967 else if (alarmtext.isEmpty()) 01968 err = BLANK; // blank file name 01969 else 01970 { 01971 // It's a local file - convert to absolute path & check validity 01972 TQFileInfo info(alarmtext); 01973 TQDir::setCurrent(TQDir::homeDirPath()); 01974 alarmtext = info.absFilePath(); 01975 url.setPath(alarmtext); 01976 alarmtext = TQString::fromLatin1("file:") + alarmtext; 01977 if (!err) 01978 { 01979 if (info.isDir()) err = DIRECTORY; 01980 else if (!info.exists()) err = NONEXISTENT; 01981 else if (!info.isReadable()) err = UNREADABLE; 01982 } 01983 } 01984 if (!err) 01985 { 01986 switch (KAlarm::fileType(KFileItem(KFileItem::Unknown, KFileItem::Unknown, url).mimetype())) 01987 { 01988 case KAlarm::TextFormatted: 01989 case KAlarm::TextPlain: 01990 case KAlarm::TextApplication: 01991 case KAlarm::Image: 01992 break; 01993 default: 01994 err = NOT_TEXT_IMAGE; 01995 break; 01996 } 01997 } 01998 if (err && showErrorMessage) 01999 { 02000 mFileMessageEdit->setFocus(); 02001 TQString errmsg; 02002 switch (err) 02003 { 02004 case BLANK: 02005 KMessageBox::sorry(const_cast<EditAlarmDlg*>(this), i18n("Please select a file to display")); 02006 return false; 02007 case NONEXISTENT: errmsg = i18n("%1\nnot found"); break; 02008 case DIRECTORY: errmsg = i18n("%1\nis a folder"); break; 02009 case UNREADABLE: errmsg = i18n("%1\nis not readable"); break; 02010 case NOT_TEXT_IMAGE: errmsg = i18n("%1\nappears not to be a text or image file"); break; 02011 case NONE: 02012 default: 02013 break; 02014 } 02015 if (KMessageBox::warningContinueCancel(const_cast<EditAlarmDlg*>(this), errmsg.arg(alarmtext)) 02016 == KMessageBox::Cancel) 02017 return false; 02018 } 02019 result = alarmtext; 02020 } 02021 return true; 02022 } 02023 02024 02025 /*============================================================================= 02026 = Class TextEdit 02027 = A text edit field with a minimum height of 3 text lines. 02028 = Provides KDE 2 compatibility. 02029 =============================================================================*/ 02030 TextEdit::TextEdit(TQWidget* parent, const char* name) 02031 : KTextEdit(parent, name) 02032 { 02033 TQSize tsize = sizeHint(); 02034 tsize.setHeight(fontMetrics().lineSpacing()*13/4 + 2*frameWidth()); 02035 setMinimumSize(tsize); 02036 } 02037 02038 void TextEdit::dragEnterEvent(TQDragEnterEvent* e) 02039 { 02040 if (KCal::ICalDrag::canDecode(e)) 02041 e->accept(false); // don't accept "text/calendar" objects 02042 KTextEdit::dragEnterEvent(e); 02043 }