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(recurs || event->uidStatus() == KAEvent::EXPIRED ? event->startDateTime()
00721                                          : event->mainExpired() ? event->deferDateTime() : event->mainDateTime());
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, (mTemplate || event->isTemplate()));   // 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             mRecurrenceEdit->updateEvent(recurEvent, false);
01407             longestRecurInterval = recurEvent.longestRecurrenceInterval();
01408             if (longestRecurInterval  &&  reminder >= longestRecurInterval)
01409             {
01410                 mTabs->setCurrentPage(mMainPageIndex);
01411                 mReminder->setFocusOnCount();
01412                 KMessageBox::sorry(this, i18n("Reminder period must be less than the recurrence interval, unless '%1' is checked."
01413                                              ).arg(Reminder::i18n_first_recurrence_only()));
01414                 return;
01415             }
01416         }
01417         if (mRecurrenceEdit->subRepeatCount())
01418         {
01419             if (longestRecurInterval < 0)
01420             {
01421                 mRecurrenceEdit->updateEvent(recurEvent, false);
01422                 longestRecurInterval = recurEvent.longestRecurrenceInterval();
01423             }
01424             if (longestRecurInterval > 0
01425             &&  recurEvent.repeatInterval() * recurEvent.repeatCount() >= longestRecurInterval - reminder)
01426             {
01427                 KMessageBox::sorry(this, i18n("The duration of a repetition within the recurrence must be less than the recurrence interval minus any reminder period"));
01428                 mRecurrenceEdit->activateSubRepetition();   // display the alarm repetition dialog again
01429                 return;
01430             }
01431             if (recurEvent.repeatInterval() % 1440
01432             &&  (mTemplate && mTemplateAnyTime->isOn()  ||  !mTemplate && mAlarmDateTime.isDateOnly()))
01433             {
01434                 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"));
01435                 mRecurrenceEdit->activateSubRepetition();   // display the alarm repetition dialog again
01436                 return;
01437             }
01438         }
01439     }
01440     if (checkText(mAlarmMessage))
01441         accept();
01442 }
01443 
01444 /******************************************************************************
01445 *  Called when the Try button is clicked.
01446 *  Display/execute the alarm immediately for the user to check its configuration.
01447 */
01448 void EditAlarmDlg::slotTry()
01449 {
01450     QString text;
01451     if (checkText(text))
01452     {
01453         if (mEmailRadio->isOn())
01454         {
01455             if (!checkEmailData()
01456             ||  KMessageBox::warningContinueCancel(this, i18n("Do you really want to send the email now to the specified recipient(s)?"),
01457                                                    i18n("Confirm Email"), i18n("&Send")) != KMessageBox::Continue)
01458                 return;
01459         }
01460         KAEvent event;
01461         setEvent(event, text, true);
01462         void* proc = theApp()->execAlarm(event, event.firstAlarm(), false, false);
01463         if (proc)
01464         {
01465             if (mCommandRadio->isOn()  &&  mCmdOutputGroup->selectedId() != EXEC_IN_TERMINAL)
01466             {
01467                 theApp()->commandMessage((ShellProcess*)proc, this);
01468                 KMessageBox::information(this, i18n("Command executed:\n%1").arg(text));
01469                 theApp()->commandMessage((ShellProcess*)proc, 0);
01470             }
01471             else if (mEmailRadio->isOn())
01472             {
01473                 QString bcc;
01474                 if (mEmailBcc->isChecked())
01475                     bcc = i18n("\nBcc: %1").arg(Preferences::emailBccAddress());
01476                 KMessageBox::information(this, i18n("Email sent to:\n%1%2").arg(mEmailAddresses.join("\n")).arg(bcc));
01477             }
01478         }
01479     }
01480 }
01481 
01482 /******************************************************************************
01483 *  Called when the Cancel button is clicked.
01484 */
01485 void EditAlarmDlg::slotCancel()
01486 {
01487     reject();
01488 }
01489 
01490 /******************************************************************************
01491 *  Called when the Load Template button is clicked.
01492 *  Prompt to select a template and initialise the dialogue with its contents.
01493 */
01494 void EditAlarmDlg::slotDefault()
01495 {
01496     TemplatePickDlg dlg(this, "templPickDlg");
01497     if (dlg.exec() == QDialog::Accepted)
01498         initialise(dlg.selectedTemplate());
01499 }
01500 
01501 /******************************************************************************
01502  * Called when the Change deferral button is clicked.
01503  */
01504 void EditAlarmDlg::slotEditDeferral()
01505 {
01506     if (!mTimeWidget)
01507         return;
01508     bool limit = true;
01509     int repeatInterval;
01510     int repeatCount = mRecurrenceEdit->subRepeatCount(&repeatInterval);
01511     DateTime start = mTimeWidget->getDateTime(0, !repeatCount, !mExpiredRecurrence);
01512     if (!start.isValid())
01513     {
01514         if (!mExpiredRecurrence)
01515             return;
01516         limit = false;
01517     }
01518     QDateTime now = QDateTime::currentDateTime();
01519     if (limit)
01520     {
01521         if (repeatCount  &&  start < now)
01522         {
01523             // Sub-repetition - find the time of the next one
01524             repeatInterval *= 60;
01525             int repetition = (start.secsTo(now) + repeatInterval - 1) / repeatInterval;
01526             if (repetition > repeatCount)
01527             {
01528                 mTimeWidget->getDateTime();    // output the appropriate error message
01529                 return;
01530             }
01531             start = start.addSecs(repetition * repeatInterval);
01532         }
01533     }
01534 
01535     bool deferred = mDeferDateTime.isValid();
01536     DeferAlarmDlg deferDlg(i18n("Defer Alarm"), (deferred ? mDeferDateTime : DateTime(now.addSecs(60))),
01537                            deferred, this, "EditDeferDlg");
01538     if (limit)
01539     {
01540         // Don't allow deferral past the next recurrence
01541         int reminder = mReminder->minutes();
01542         if (reminder)
01543         {
01544             DateTime remindTime = start.addMins(-reminder);
01545             if (QDateTime::currentDateTime() < remindTime)
01546                 start = remindTime;
01547         }
01548         deferDlg.setLimit(start.addSecs(-60));
01549     }
01550     if (deferDlg.exec() == QDialog::Accepted)
01551     {
01552         mDeferDateTime = deferDlg.getDateTime();
01553         mDeferTimeLabel->setText(mDeferDateTime.isValid() ? mDeferDateTime.formatLocale() : QString::null);
01554     }
01555 }
01556 
01557 /******************************************************************************
01558 *  Called when the main page is shown.
01559 *  Sets the focus widget to the first edit field.
01560 */
01561 void EditAlarmDlg::slotShowMainPage()
01562 {
01563     slotAlarmTypeChanged(-1);
01564     if (!mMainPageShown)
01565     {
01566         if (mTemplateName)
01567             mTemplateName->setFocus();
01568         mMainPageShown = true;
01569     }
01570     if (mTimeWidget)
01571     {
01572         if (!mReadOnly  &&  mRecurPageShown  &&  mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN)
01573             mTimeWidget->setDateTime(mRecurrenceEdit->endDateTime());
01574         if (mReadOnly  ||  mRecurrenceEdit->isTimedRepeatType())
01575             mTimeWidget->setMinDateTime();             // don't set a minimum date/time
01576         else
01577             mTimeWidget->setMinDateTimeIsCurrent();    // set the minimum date/time to track the clock
01578     }
01579 }
01580 
01581 /******************************************************************************
01582 *  Called when the recurrence edit page is shown.
01583 *  The recurrence defaults are set to correspond to the start date.
01584 *  The first time, for a new alarm, the recurrence end date is set according to
01585 *  the alarm start time.
01586 */
01587 void EditAlarmDlg::slotShowRecurrenceEdit()
01588 {
01589     mRecurPageIndex = mTabs->currentPageIndex();
01590     if (!mReadOnly  &&  !mTemplate)
01591     {
01592         QDateTime now = QDateTime::currentDateTime();
01593         mAlarmDateTime = mTimeWidget->getDateTime(0, false, false);
01594         bool expired = (mAlarmDateTime.dateTime() < now);
01595         if (mRecurSetDefaultEndDate)
01596         {
01597             mRecurrenceEdit->setDefaultEndDate(expired ? now.date() : mAlarmDateTime.date());
01598             mRecurSetDefaultEndDate = false;
01599         }
01600         mRecurrenceEdit->setStartDate(mAlarmDateTime.date(), now.date());
01601         if (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN)
01602             mRecurrenceEdit->setEndDateTime(expired ? now : mAlarmDateTime);
01603     }
01604     mRecurPageShown = true;
01605 }
01606 
01607 /******************************************************************************
01608 *  Called when the recurrence type selection changes.
01609 *  Enables/disables date-only alarms as appropriate.
01610 *  Enables/disables controls depending on at-login setting.
01611 */
01612 void EditAlarmDlg::slotRecurTypeChange(int repeatType)
01613 {
01614     bool atLogin = (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN);
01615     if (!mTemplate)
01616     {
01617         bool recurs = (mRecurrenceEdit->repeatType() != RecurrenceEdit::NO_RECUR);
01618         if (mDeferGroup)
01619             mDeferGroup->setEnabled(recurs);
01620         mTimeWidget->enableAnyTime(!recurs || repeatType != RecurrenceEdit::SUBDAILY);
01621         if (atLogin)
01622         {
01623             mAlarmDateTime = mTimeWidget->getDateTime(0, false, false);
01624             mRecurrenceEdit->setEndDateTime(mAlarmDateTime.dateTime());
01625         }
01626         mReminder->enableOnceOnly(recurs && !atLogin);
01627     }
01628     mReminder->setEnabled(!atLogin);
01629         mLateCancel->setEnabled(!atLogin);
01630         if (mShowInKorganizer)
01631                 mShowInKorganizer->setEnabled(!atLogin);
01632     slotRecurFrequencyChange();
01633 }
01634 
01635 /******************************************************************************
01636 *  Called when the recurrence frequency selection changes, or the sub-
01637 *  repetition interval changes.
01638 *  Updates the recurrence frequency text.
01639 */
01640 void EditAlarmDlg::slotRecurFrequencyChange()
01641 {
01642     slotSetSubRepetition();
01643     KAEvent event;
01644     mRecurrenceEdit->updateEvent(event, false);
01645     mTabs->setTabLabel(mTabs->page(mRecurPageIndex), recurText(event));
01646 }
01647 
01648 /******************************************************************************
01649 *  Called when the Repetition within Recurrence button has been pressed to
01650 *  display the sub-repetition dialog.
01651 *  Alarm repetition has the following restrictions:
01652 *  1) Not allowed for a repeat-at-login alarm
01653 *  2) For a date-only alarm, the repeat interval must be a whole number of days.
01654 *  3) The overall repeat duration must be less than the recurrence interval.
01655 */
01656 void EditAlarmDlg::slotSetSubRepetition()
01657 {
01658     bool dateOnly = mTemplate ? mTemplateAnyTime->isOn() : mTimeWidget->anyTime();
01659     mRecurrenceEdit->setSubRepetition(mReminder->minutes(), dateOnly);
01660 }
01661 
01662 /******************************************************************************
01663 *  Validate and convert command alarm data.
01664 */
01665 bool EditAlarmDlg::checkCommandData()
01666 {
01667     if (mCommandRadio->isOn()  &&  mCmdOutputGroup->selectedId() == LOG_TO_FILE)
01668     {
01669         // Validate the log file name
01670         QString file = mCmdLogFileEdit->text();
01671         QFileInfo info(file);
01672         QDir::setCurrent(QDir::homeDirPath());
01673         bool err = file.isEmpty()  ||  info.isDir();
01674         if (!err)
01675         {
01676             if (info.exists())
01677             {
01678                 err = !info.isWritable();
01679             }
01680             else
01681             {
01682                 QFileInfo dirinfo(info.dirPath(true));    // get absolute directory path
01683                 err = (!dirinfo.isDir()  ||  !dirinfo.isWritable());
01684             }
01685         }
01686         if (err)
01687         {
01688             mTabs->setCurrentPage(mMainPageIndex);
01689             mCmdLogFileEdit->setFocus();
01690             KMessageBox::sorry(this, i18n("Log file must be the name or path of a local file, with write permission."));
01691             return false;
01692         }
01693         // Convert the log file to an absolute path
01694         mCmdLogFileEdit->setText(info.absFilePath());
01695     }
01696     return true;
01697 }
01698 
01699 /******************************************************************************
01700 *  Convert the email addresses to a list, and validate them. Convert the email
01701 *  attachments to a list.
01702 */
01703 bool EditAlarmDlg::checkEmailData()
01704 {
01705     if (mEmailRadio->isOn())
01706     {
01707         QString addrs = mEmailToEdit->text();
01708         if (addrs.isEmpty())
01709             mEmailAddresses.clear();
01710         else
01711         {
01712             QString bad = KAMail::convertAddresses(addrs, mEmailAddresses);
01713             if (!bad.isEmpty())
01714             {
01715                 mEmailToEdit->setFocus();
01716                 KMessageBox::error(this, i18n("Invalid email address:\n%1").arg(bad));
01717                 return false;
01718             }
01719         }
01720         if (mEmailAddresses.isEmpty())
01721         {
01722             mEmailToEdit->setFocus();
01723             KMessageBox::error(this, i18n("No email address specified"));
01724             return false;
01725         }
01726 
01727         mEmailAttachments.clear();
01728         for (int i = 0;  i < mEmailAttachList->count();  ++i)
01729         {
01730             QString att = mEmailAttachList->text(i);
01731             switch (KAMail::checkAttachment(att))
01732             {
01733                 case 1:
01734                     mEmailAttachments.append(att);
01735                     break;
01736                 case 0:
01737                     break;      // empty
01738                 case -1:
01739                     mEmailAttachList->setFocus();
01740                     KMessageBox::error(this, i18n("Invalid email attachment:\n%1").arg(att));
01741                     return false;
01742             }
01743         }
01744     }
01745     return true;
01746 }
01747 
01748 /******************************************************************************
01749 *  Called when one of the alarm action type radio buttons is clicked,
01750 *  to display the appropriate set of controls for that action type.
01751 */
01752 void EditAlarmDlg::slotAlarmTypeChanged(int)
01753 {
01754     bool displayAlarm = false;
01755     QWidget* focus = 0;
01756     if (mMessageRadio->isOn())
01757     {
01758         mFileBox->hide();
01759         mFilePadding->hide();
01760         mTextMessageEdit->show();
01761         mFontColourButton->show();
01762         mSoundPicker->showSpeak(true);
01763         mDisplayAlarmsFrame->show();
01764         mCommandFrame->hide();
01765         mEmailFrame->hide();
01766         mReminder->show();
01767         mConfirmAck->show();
01768         setButtonWhatsThis(Try, i18n("Display the alarm message now"));
01769         focus = mTextMessageEdit;
01770         displayAlarm = true;
01771     }
01772     else if (mFileRadio->isOn())
01773     {
01774         mTextMessageEdit->hide();
01775         mFileBox->show();
01776         mFilePadding->show();
01777         mFontColourButton->hide();
01778         mSoundPicker->showSpeak(false);
01779         mDisplayAlarmsFrame->show();
01780         mCommandFrame->hide();
01781         mEmailFrame->hide();
01782         mReminder->show();
01783         mConfirmAck->show();
01784         setButtonWhatsThis(Try, i18n("Display the file now"));
01785         mFileMessageEdit->setNoSelect();
01786         focus = mFileMessageEdit;
01787         displayAlarm = true;
01788     }
01789     else if (mCommandRadio->isOn())
01790     {
01791         mDisplayAlarmsFrame->hide();
01792         mCommandFrame->show();
01793         mEmailFrame->hide();
01794         mReminder->hide();
01795         mConfirmAck->hide();
01796         setButtonWhatsThis(Try, i18n("Execute the specified command now"));
01797         mCmdCommandEdit->setNoSelect();
01798         focus = mCmdCommandEdit;
01799     }
01800     else if (mEmailRadio->isOn())
01801     {
01802         mDisplayAlarmsFrame->hide();
01803         mCommandFrame->hide();
01804         mEmailFrame->show();
01805         mReminder->hide();
01806         mConfirmAck->hide();
01807         setButtonWhatsThis(Try, i18n("Send the email to the specified addressees now"));
01808         mEmailToEdit->setNoSelect();
01809         focus = mEmailToEdit;
01810     }
01811     mLateCancel->showAutoClose(displayAlarm);
01812     mLateCancel->setFixedSize(mLateCancel->sizeHint());
01813     if (focus)
01814         focus->setFocus();
01815 }
01816 
01817 /******************************************************************************
01818 *  Called when one of the command type radio buttons is clicked,
01819 *  to display the appropriate edit field.
01820 */
01821 void EditAlarmDlg::slotCmdScriptToggled(bool on)
01822 {
01823     if (on)
01824     {
01825         mCmdCommandEdit->hide();
01826         mCmdPadding->hide();
01827         mCmdScriptEdit->show();
01828         mCmdScriptEdit->setFocus();
01829     }
01830     else
01831     {
01832         mCmdScriptEdit->hide();
01833         mCmdCommandEdit->show();
01834         mCmdPadding->show();
01835         mCmdCommandEdit->setFocus();
01836     }
01837 }
01838 
01839 /******************************************************************************
01840 *  Called when one of the template time radio buttons is clicked,
01841 *  to enable or disable the template time entry spin boxes.
01842 */
01843 void EditAlarmDlg::slotTemplateTimeType(int)
01844 {
01845     mTemplateTime->setEnabled(mTemplateUseTime->isOn());
01846     mTemplateTimeAfter->setEnabled(mTemplateUseTimeAfter->isOn());
01847 }
01848 
01849 /******************************************************************************
01850 *  Called when the "Any time" checkbox is toggled in the date/time widget.
01851 *  Sets the advance reminder and late cancel units to days if any time is checked.
01852 */
01853 void EditAlarmDlg::slotAnyTimeToggled(bool anyTime)
01854 {
01855     if (mReminder->isReminder())
01856         mReminder->setDateOnly(anyTime);
01857     mLateCancel->setDateOnly(anyTime);
01858 }
01859 
01860 /******************************************************************************
01861  * Get a selection from the Address Book.
01862  */
01863 void EditAlarmDlg::openAddressBook()
01864 {
01865     KABC::Addressee a = KABC::AddresseeDialog::getAddressee(this);
01866     if (a.isEmpty())
01867         return;
01868     Person person(a.realName(), a.preferredEmail());
01869     QString addrs = mEmailToEdit->text().stripWhiteSpace();
01870     if (!addrs.isEmpty())
01871         addrs += ", ";
01872     addrs += person.fullName();
01873     mEmailToEdit->setText(addrs);
01874 }
01875 
01876 /******************************************************************************
01877  * Select a file to attach to the email.
01878  */
01879 void EditAlarmDlg::slotAddAttachment()
01880 {
01881     QString url = KAlarm::browseFile(i18n("Choose File to Attach"), mAttachDefaultDir, QString::null,
01882                                      QString::null, KFile::ExistingOnly, this, "pickAttachFile");
01883     if (!url.isEmpty())
01884     {
01885         mEmailAttachList->insertItem(url);
01886         mEmailAttachList->setCurrentItem(mEmailAttachList->count() - 1);   // select the new item
01887         mEmailRemoveButton->setEnabled(true);
01888         mEmailAttachList->setEnabled(true);
01889     }
01890 }
01891 
01892 /******************************************************************************
01893  * Remove the currently selected attachment from the email.
01894  */
01895 void EditAlarmDlg::slotRemoveAttachment()
01896 {
01897     int item = mEmailAttachList->currentItem();
01898     mEmailAttachList->removeItem(item);
01899     int count = mEmailAttachList->count();
01900     if (item >= count)
01901         mEmailAttachList->setCurrentItem(count - 1);
01902     if (!count)
01903     {
01904         mEmailRemoveButton->setEnabled(false);
01905         mEmailAttachList->setEnabled(false);
01906     }
01907 }
01908 
01909 /******************************************************************************
01910 *  Clean up the alarm text, and if it's a file, check whether it's valid.
01911 */
01912 bool EditAlarmDlg::checkText(QString& result, bool showErrorMessage) const
01913 {
01914     if (mMessageRadio->isOn())
01915         result = mTextMessageEdit->text();
01916     else if (mEmailRadio->isOn())
01917         result = mEmailMessageEdit->text();
01918     else if (mCommandRadio->isOn())
01919     {
01920         if (mCmdTypeScript->isChecked())
01921             result = mCmdScriptEdit->text();
01922         else
01923             result = mCmdCommandEdit->text();
01924         result = result.stripWhiteSpace();
01925     }
01926     else if (mFileRadio->isOn())
01927     {
01928         QString alarmtext = mFileMessageEdit->text().stripWhiteSpace();
01929         // Convert any relative file path to absolute
01930         // (using home directory as the default)
01931         enum Err { NONE = 0, BLANK, NONEXISTENT, DIRECTORY, UNREADABLE, NOT_TEXT_IMAGE };
01932         Err err = NONE;
01933         KURL url;
01934         int i = alarmtext.find(QString::fromLatin1("/"));
01935         if (i > 0  &&  alarmtext[i - 1] == ':')
01936         {
01937             url = alarmtext;
01938             url.cleanPath();
01939             alarmtext = url.prettyURL();
01940             KIO::UDSEntry uds;
01941             if (!KIO::NetAccess::stat(url, uds, MainWindow::mainMainWindow()))
01942                 err = NONEXISTENT;
01943             else
01944             {
01945                 KFileItem fi(uds, url);
01946                 if (fi.isDir())             err = DIRECTORY;
01947                 else if (!fi.isReadable())  err = UNREADABLE;
01948             }
01949         }
01950         else if (alarmtext.isEmpty())
01951             err = BLANK;    // blank file name
01952         else
01953         {
01954             // It's a local file - convert to absolute path & check validity
01955             QFileInfo info(alarmtext);
01956             QDir::setCurrent(QDir::homeDirPath());
01957             alarmtext = info.absFilePath();
01958             url.setPath(alarmtext);
01959             alarmtext = QString::fromLatin1("file:") + alarmtext;
01960             if (!err)
01961             {
01962                 if      (info.isDir())        err = DIRECTORY;
01963                 else if (!info.exists())      err = NONEXISTENT;
01964                 else if (!info.isReadable())  err = UNREADABLE;
01965             }
01966         }
01967         if (!err)
01968         {
01969             switch (KAlarm::fileType(KFileItem(KFileItem::Unknown, KFileItem::Unknown, url).mimetype()))
01970             {
01971                 case KAlarm::TextFormatted:
01972                 case KAlarm::TextPlain:
01973                 case KAlarm::TextApplication:
01974                 case KAlarm::Image:
01975                     break;
01976                 default:
01977                     err = NOT_TEXT_IMAGE;
01978                     break;
01979             }
01980         }
01981         if (err  &&  showErrorMessage)
01982         {
01983             mFileMessageEdit->setFocus();
01984             QString errmsg;
01985             switch (err)
01986             {
01987                 case BLANK:
01988                     KMessageBox::sorry(const_cast<EditAlarmDlg*>(this), i18n("Please select a file to display"));
01989                     return false;
01990                 case NONEXISTENT:     errmsg = i18n("%1\nnot found");  break;
01991                 case DIRECTORY:       errmsg = i18n("%1\nis a folder");  break;
01992                 case UNREADABLE:      errmsg = i18n("%1\nis not readable");  break;
01993                 case NOT_TEXT_IMAGE:  errmsg = i18n("%1\nappears not to be a text or image file");  break;
01994                 case NONE:
01995                 default:
01996                     break;
01997             }
01998             if (KMessageBox::warningContinueCancel(const_cast<EditAlarmDlg*>(this), errmsg.arg(alarmtext))
01999                 == KMessageBox::Cancel)
02000                 return false;
02001         }
02002         result = alarmtext;
02003     }
02004     return true;
02005 }
02006 
02007 
02008 /*=============================================================================
02009 = Class TextEdit
02010 = A text edit field with a minimum height of 3 text lines.
02011 = Provides KDE 2 compatibility.
02012 =============================================================================*/
02013 TextEdit::TextEdit(QWidget* parent, const char* name)
02014     : KTextEdit(parent, name)
02015 {
02016     QSize tsize = sizeHint();
02017     tsize.setHeight(fontMetrics().lineSpacing()*13/4 + 2*frameWidth());
02018     setMinimumSize(tsize);
02019 }
02020 
02021 void TextEdit::dragEnterEvent(QDragEnterEvent* e)
02022 {
02023     if (KCal::ICalDrag::canDecode(e))
02024         e->accept(false);   // don't accept "text/calendar" objects
02025     KTextEdit::dragEnterEvent(e);
02026 }
KDE Home | KDE Accessibility Home | Description of Access Keys