kalarm

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