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