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