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