• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdepim
  • Sitemap
  • Contact Us
 

kalarm

editdlg.cpp

Go to the documentation of this file.
00001 /*
00002  *  editdlg.cpp  -  dialog 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 "alarmcalendar.h"
00024 #include "alarmresources.h"
00025 #include "alarmtimewidget.h"
00026 #include "buttongroup.h"
00027 #include "checkbox.h"
00028 #include "deferdlg.h"
00029 #include "dialogscroll.h"
00030 #include "functions.h"
00031 #include "kalarmapp.h"
00032 #include "latecancel.h"
00033 #include "lineedit.h"
00034 #include "mainwindow.h"
00035 #include "packedlayout.h"
00036 #include "preferences.h"
00037 #include "radiobutton.h"
00038 #include "recurrenceedit.h"
00039 #include "reminder.h"
00040 #include "shellprocess.h"
00041 #include "spinbox.h"
00042 #include "templatepickdlg.h"
00043 #include "timeedit.h"
00044 #include "timespinbox.h"
00045 #include "editdlg.moc"
00046 #include "editdlgprivate.moc"
00047 #include "editdlgtypes.h"
00048 
00049 #include <QLabel>
00050 #include <QDir>
00051 #include <QStyle>
00052 #include <QGroupBox>
00053 #include <QPushButton>
00054 #include <QGridLayout>
00055 #include <QHBoxLayout>
00056 #include <QVBoxLayout>
00057 #include <QDragEnterEvent>
00058 #include <QResizeEvent>
00059 #include <QShowEvent>
00060 
00061 #include <kglobal.h>
00062 #include <klocale.h>
00063 #include <kconfig.h>
00064 #include <kfiledialog.h>
00065 #include <kmessagebox.h>
00066 #include <ktabwidget.h>
00067 #include <khbox.h>
00068 #include <kvbox.h>
00069 #include <kwindowsystem.h>
00070 #include <kdebug.h>
00071 
00072 #include <libkdepim/maillistdrag.h>
00073 #include <libkdepim/kvcarddrag.h>
00074 #include <kcal/icaldrag.h>
00075 
00076 using namespace KCal;
00077 
00078 static const char EDIT_DIALOG_NAME[] = "EditDialog";
00079 static const char TEMPLATE_DIALOG_NAME[] = "EditTemplateDialog";
00080 static const int  maxDelayTime = 99*60 + 59;    // < 100 hours
00081 
00082 inline QString recurText(const KAEvent& event)
00083 {
00084     QString r;
00085     if (event.repeatCount())
00086         r = QString::fromLatin1("%1 / %2").arg(event.recurrenceText()).arg(event.repetitionText());
00087     else
00088         r = event.recurrenceText();
00089     return i18nc("@title:tab", "Recurrence - [%1]", r);
00090 }
00091 
00092 // Collect these widget labels together to ensure consistent wording and
00093 // translations across different modules.
00094 QString EditAlarmDlg::i18n_chk_ShowInKOrganizer()   { return i18nc("@option:check", "Show in KOrganizer"); }
00095 
00096 
00097 EditAlarmDlg* EditAlarmDlg::create(bool Template, Type type, bool newAlarm, QWidget* parent, GetResourceType getResource)
00098 {
00099     kDebug();
00100     switch (type)
00101     {
00102         case DISPLAY:  return new EditDisplayAlarmDlg(Template, newAlarm, parent, getResource);
00103         case COMMAND:  return new EditCommandAlarmDlg(Template, newAlarm, parent, getResource);
00104         case EMAIL:    return new EditEmailAlarmDlg(Template, newAlarm, parent, getResource);
00105     }
00106     return 0;
00107 }
00108 
00109 EditAlarmDlg* EditAlarmDlg::create(bool Template, const KAEvent* event, bool newAlarm, QWidget* parent,
00110                                    GetResourceType getResource, bool readOnly)
00111 {
00112     switch (event->action())
00113     {
00114         case KAEvent::COMMAND:
00115             if (!event->commandDisplay())
00116                 return new EditCommandAlarmDlg(Template, event, newAlarm, parent, getResource, readOnly);
00117             // fall through to MESSAGE
00118         case KAEvent::MESSAGE:
00119         case KAEvent::FILE:     return new EditDisplayAlarmDlg(Template, event, newAlarm, parent, getResource, readOnly);
00120         case KAEvent::EMAIL:    return new EditEmailAlarmDlg(Template, event, newAlarm, parent, getResource, readOnly);
00121     }
00122     return 0;
00123 }
00124 
00125 
00126 /******************************************************************************
00127 * Constructor.
00128 * Parameters:
00129 *   Template = true to edit/create an alarm template
00130 *            = false to edit/create an alarm.
00131 *   event   != to initialise the dialog to show the specified event's data.
00132 */
00133 EditAlarmDlg::EditAlarmDlg(bool Template, KAEvent::Action action, QWidget* parent, GetResourceType getResource)
00134     : KDialog(parent),
00135       mAlarmType(action),
00136       mMainPageShown(false),
00137       mRecurPageShown(false),
00138       mRecurSetDefaultEndDate(true),
00139       mTemplateName(0),
00140       mDeferGroup(0),
00141       mDeferChangeButton(0),
00142       mTimeWidget(0),
00143       mShowInKorganizer(0),
00144       mResource(0),
00145       mDeferGroupHeight(0),
00146       mTemplate(Template),
00147       mDesiredReadOnly(false),
00148       mReadOnly(false),
00149       mSavedEvent(0)
00150 {
00151     init(0, getResource);
00152 }
00153 
00154 EditAlarmDlg::EditAlarmDlg(bool Template, const KAEvent* event, QWidget* parent,
00155                            GetResourceType getResource, bool readOnly)
00156     : KDialog(parent),
00157       mAlarmType(event->action()),
00158       mMainPageShown(false),
00159       mRecurPageShown(false),
00160       mRecurSetDefaultEndDate(true),
00161       mTemplateName(0),
00162       mDeferGroup(0),
00163       mDeferChangeButton(0),
00164       mTimeWidget(0),
00165       mShowInKorganizer(0),
00166       mResource(0),
00167       mDeferGroupHeight(0),
00168       mTemplate(Template),
00169       mDesiredReadOnly(readOnly),
00170       mReadOnly(readOnly),
00171       mSavedEvent(0)
00172 {
00173     init(event, getResource);
00174 }
00175 
00176 void EditAlarmDlg::init(const KAEvent* event, GetResourceType getResource)
00177 {
00178     switch (getResource)
00179     {
00180         case RES_USE_EVENT_ID:
00181             if (event)
00182             {
00183                 mResourceEventId = event->id();
00184                 break;
00185             }
00186             // fall through to RES_PROMPT
00187         case RES_PROMPT:
00188             mResourceEventId = QString("");   // empty but non-null
00189             break;
00190         case RES_IGNORE:
00191         default:
00192             mResourceEventId.clear();
00193             break;
00194     }
00195 }
00196 
00197 void EditAlarmDlg::init(const KAEvent* event, bool newAlarm)
00198 {
00199     setObjectName(mTemplate ? "TemplEditDlg" : "EditDlg");    // used by LikeBack
00200     QString caption;
00201     if (mReadOnly)
00202         caption = mTemplate ? i18nc("@title:window", "Alarm Template [read-only]")
00203                 : event->expired() ? i18nc("@title:window", "Archived Alarm [read-only]")
00204                                    : i18nc("@title:window", "Alarm [read-only]");
00205     else
00206         caption = type_caption(newAlarm);
00207     setCaption(caption);
00208     setButtons((mReadOnly ? Cancel|Try : mTemplate ? Ok|Cancel|Try : Ok|Cancel|Try|Default));
00209     setDefaultButton(mReadOnly ? Cancel : Ok);
00210     setButtonText(Default, i18nc("@action:button", "Load Template..."));
00211     connect(this, SIGNAL(tryClicked()), SLOT(slotTry()));
00212     connect(this, SIGNAL(defaultClicked()), SLOT(slotDefault()));
00213     KVBox* mainWidget = new KVBox(this);
00214     mainWidget->setMargin(0);
00215     setMainWidget(mainWidget);
00216     if (mTemplate)
00217     {
00218         KHBox* box = new KHBox(mainWidget);
00219         box->setMargin(0);
00220         box->setSpacing(spacingHint());
00221         QLabel* label = new QLabel(i18nc("@label:textbox", "Template name:"), box);
00222         label->setFixedSize(label->sizeHint());
00223         mTemplateName = new KLineEdit(box);
00224         mTemplateName->setReadOnly(mReadOnly);
00225         label->setBuddy(mTemplateName);
00226         box->setWhatsThis(i18nc("@info:whatsthis", "Enter the name of the alarm template"));
00227         box->setFixedHeight(box->sizeHint().height());
00228     }
00229     mTabs = new KTabWidget(mainWidget);
00230 
00231     DialogScroll<EditAlarmDlg>* mainScroll = new DialogScroll<EditAlarmDlg>();
00232     mTabs->addTab(mainScroll, i18nc("@title:tab", "Alarm"));
00233     mMainPageIndex = 0;
00234     PageFrame* mainPage = new PageFrame(mainScroll);
00235     mainScroll->setWidget(mainPage);   // mainPage becomes the child of mainScroll
00236     connect(mainPage, SIGNAL(shown()), SLOT(slotShowMainPage()));
00237     QVBoxLayout* topLayout = new QVBoxLayout(mainPage);
00238     topLayout->setMargin(marginHint());
00239     topLayout->setSpacing(spacingHint());
00240 
00241     // Recurrence tab
00242     DialogScroll<EditAlarmDlg>* recurScroll = new DialogScroll<EditAlarmDlg>();
00243     mTabs->addTab(recurScroll, QString());
00244     mRecurPageIndex = 1;
00245     KVBox* recurTab = new KVBox();
00246     recurTab->setMargin(marginHint());
00247     recurScroll->setWidget(recurTab);   // recurTab becomes the child of recurScroll
00248     mRecurrenceEdit = new RecurrenceEdit(mReadOnly, recurTab);
00249     connect(mRecurrenceEdit, SIGNAL(shown()), SLOT(slotShowRecurrenceEdit()));
00250     connect(mRecurrenceEdit, SIGNAL(typeChanged(int)), SLOT(slotRecurTypeChange(int)));
00251     connect(mRecurrenceEdit, SIGNAL(frequencyChanged()), SLOT(slotRecurFrequencyChange()));
00252     connect(mRecurrenceEdit, SIGNAL(repeatNeedsInitialisation()), SLOT(slotSetSubRepetition()));
00253 
00254     // Controls specific to the alarm type
00255     QGroupBox* actionBox = new QGroupBox(i18nc("@title:group", "Action"), mainPage);
00256     topLayout->addWidget(actionBox, 1);
00257     QVBoxLayout* layout = new QVBoxLayout(actionBox);
00258     layout->setMargin(marginHint());
00259     layout->setSpacing(spacingHint());
00260 
00261     type_init(actionBox, layout);
00262 
00263     if (!mTemplate)
00264     {
00265         // Deferred date/time: visible only for a deferred recurring event.
00266         mDeferGroup = new QGroupBox(i18nc("@title:group", "Deferred Alarm"), mainPage);
00267         topLayout->addWidget(mDeferGroup);
00268         QHBoxLayout* hlayout = new QHBoxLayout(mDeferGroup);
00269         hlayout->setMargin(marginHint());
00270         hlayout->setSpacing(spacingHint());
00271         QLabel* label = new QLabel(i18nc("@label", "Deferred to:"), mDeferGroup);
00272         label->setFixedSize(label->sizeHint());
00273         hlayout->addWidget(label);
00274         mDeferTimeLabel = new QLabel(mDeferGroup);
00275         hlayout->addWidget(mDeferTimeLabel);
00276 
00277         mDeferChangeButton = new QPushButton(i18nc("@action:button", "Change..."), mDeferGroup);
00278         mDeferChangeButton->setFixedSize(mDeferChangeButton->sizeHint());
00279         connect(mDeferChangeButton, SIGNAL(clicked()), SLOT(slotEditDeferral()));
00280         mDeferChangeButton->setWhatsThis(i18nc("@info:whatsthis", "Change the alarm's deferred time, or cancel the deferral"));
00281         hlayout->addWidget(mDeferChangeButton);
00282 //??        mDeferGroup->addSpace(0);
00283     }
00284 
00285     QHBoxLayout* hlayout = new QHBoxLayout();
00286     hlayout->setMargin(0);
00287     topLayout->addLayout(hlayout);
00288 
00289     // Date and time entry
00290     if (mTemplate)
00291     {
00292         QGroupBox* templateTimeBox = new QGroupBox(i18nc("@title:group", "Time"), mainPage);
00293         hlayout->addWidget(templateTimeBox);
00294         QGridLayout* grid = new QGridLayout(templateTimeBox);
00295         grid->setMargin(marginHint());
00296         grid->setSpacing(spacingHint());
00297         mTemplateTimeGroup = new ButtonGroup(templateTimeBox);
00298         connect(mTemplateTimeGroup, SIGNAL(buttonSet(QAbstractButton*)), SLOT(slotTemplateTimeType(QAbstractButton*)));
00299 
00300         mTemplateDefaultTime = new RadioButton(i18nc("@option:radio", "Default time"), templateTimeBox);
00301         mTemplateDefaultTime->setFixedSize(mTemplateDefaultTime->sizeHint());
00302         mTemplateDefaultTime->setReadOnly(mReadOnly);
00303         mTemplateDefaultTime->setWhatsThis(i18nc("@info:whatsthis", "Do not specify a start time for alarms based on this template. "
00304                                                 "The normal default start time will be used."));
00305         mTemplateTimeGroup->addButton(mTemplateDefaultTime);
00306         grid->addWidget(mTemplateDefaultTime, 0, 0, Qt::AlignLeft);
00307 
00308         KHBox* box = new KHBox(templateTimeBox);
00309         box->setMargin(0);
00310         box->setSpacing(spacingHint());
00311         mTemplateUseTime = new RadioButton(i18nc("@option:radio", "Time:"), box);
00312         mTemplateUseTime->setFixedSize(mTemplateUseTime->sizeHint());
00313         mTemplateUseTime->setReadOnly(mReadOnly);
00314         mTemplateUseTime->setWhatsThis(i18nc("@info:whatsthis", "Specify a start time for alarms based on this template."));
00315         mTemplateTimeGroup->addButton(mTemplateUseTime);
00316         mTemplateTime = new TimeEdit(box);
00317         mTemplateTime->setFixedSize(mTemplateTime->sizeHint());
00318         mTemplateTime->setReadOnly(mReadOnly);
00319         mTemplateTime->setWhatsThis(i18nc("@info:whatsthis",
00320               "<para>Enter the start time for alarms based on this template.</para><para>%1</para>",
00321               TimeSpinBox::shiftWhatsThis()));
00322         box->setStretchFactor(new QWidget(box), 1);    // left adjust the controls
00323         box->setFixedHeight(box->sizeHint().height());
00324         grid->addWidget(box, 0, 1, Qt::AlignLeft);
00325 
00326         mTemplateAnyTime = new RadioButton(i18nc("@option:radio", "Date only"), templateTimeBox);
00327         mTemplateAnyTime->setFixedSize(mTemplateAnyTime->sizeHint());
00328         mTemplateAnyTime->setReadOnly(mReadOnly);
00329         mTemplateAnyTime->setWhatsThis(i18nc("@info:whatsthis", "Set the <interface>Date</interface> option for alarms based on this template."));
00330         mTemplateTimeGroup->addButton(mTemplateAnyTime);
00331         grid->addWidget(mTemplateAnyTime, 1, 0, Qt::AlignLeft);
00332 
00333         box = new KHBox(templateTimeBox);
00334         box->setMargin(0);
00335         box->setSpacing(spacingHint());
00336         mTemplateUseTimeAfter = new RadioButton(i18nc("@option:radio", "Time from now:"), box);
00337         mTemplateUseTimeAfter->setFixedSize(mTemplateUseTimeAfter->sizeHint());
00338         mTemplateUseTimeAfter->setReadOnly(mReadOnly);
00339         mTemplateUseTimeAfter->setWhatsThis(i18nc("@info:whatsthis",
00340                                                   "Set alarms based on this template to start after the specified time "
00341                                                  "interval from when the alarm is created."));
00342         mTemplateTimeGroup->addButton(mTemplateUseTimeAfter);
00343         mTemplateTimeAfter = new TimeSpinBox(1, maxDelayTime, box);
00344         mTemplateTimeAfter->setValue(1439);
00345         mTemplateTimeAfter->setFixedSize(mTemplateTimeAfter->sizeHint());
00346         mTemplateTimeAfter->setReadOnly(mReadOnly);
00347         mTemplateTimeAfter->setWhatsThis(i18nc("@info:whatsthis", "<para>%1</para><para>%2</para>",
00348                                                AlarmTimeWidget::i18n_TimeAfterPeriod(), TimeSpinBox::shiftWhatsThis()));
00349         box->setFixedHeight(box->sizeHint().height());
00350         grid->addWidget(box, 1, 1, Qt::AlignLeft);
00351 
00352         hlayout->addStretch();
00353     }
00354     else
00355     {
00356         mTimeWidget = new AlarmTimeWidget(i18nc("@title:group", "Time"), AlarmTimeWidget::AT_TIME, mainPage);
00357         connect(mTimeWidget, SIGNAL(dateOnlyToggled(bool)), SLOT(slotAnyTimeToggled(bool)));
00358         topLayout->addWidget(mTimeWidget);
00359     }
00360 
00361     // Reminder
00362     mReminder = createReminder(mainPage);
00363     if (mReminder)
00364     {
00365         mReminder->setFixedSize(mReminder->sizeHint());
00366         topLayout->addWidget(mReminder, 0, Qt::AlignLeft);
00367     }
00368 
00369     // Late cancel selector - default = allow late display
00370     mLateCancel = new LateCancelSelector(true, mainPage);
00371     topLayout->addWidget(mLateCancel, 0, Qt::AlignLeft);
00372 
00373     PackedLayout* playout = new PackedLayout(Qt::AlignJustify);
00374     playout->setSpacing(2*spacingHint());
00375     topLayout->addLayout(playout);
00376 
00377     // Acknowledgement confirmation required - default = no confirmation
00378     CheckBox* confirmAck = type_createConfirmAckCheckbox(mainPage);
00379     if (confirmAck)
00380     {
00381         confirmAck->setFixedSize(confirmAck->sizeHint());
00382         playout->addWidget(confirmAck);
00383     }
00384 
00385     if (theApp()->korganizerEnabled())
00386     {
00387         // Show in KOrganizer checkbox
00388         mShowInKorganizer = new CheckBox(i18n_chk_ShowInKOrganizer(), mainPage);
00389         mShowInKorganizer->setFixedSize(mShowInKorganizer->sizeHint());
00390         mShowInKorganizer->setWhatsThis(i18nc("@info:whatsthis", "Check to copy the alarm into KOrganizer's calendar"));
00391         playout->addWidget(mShowInKorganizer);
00392     }
00393 
00394     setButtonWhatsThis(Ok, i18nc("@info:whatsthis", "Schedule the alarm at the specified time."));
00395 
00396     // Initialise the state of all controls according to the specified event, if any
00397     initValues(event);
00398     if (mTemplateName)
00399         mTemplateName->setFocus();
00400 
00401     // Save the initial state of all controls so that we can later tell if they have changed
00402     saveState((event && (mTemplate || !event->isTemplate())) ? event : 0);
00403 
00404     // Note the current desktop so that the dialog can be shown on it.
00405     // If a main window is visible, the dialog will by KDE default always appear on its
00406     // desktop. If the user invokes the dialog via the system tray on a different desktop,
00407     // that can cause confusion.
00408     mDesktop = KWindowSystem::currentDesktop();
00409 }
00410 
00411 EditAlarmDlg::~EditAlarmDlg()
00412 {
00413     delete mSavedEvent;
00414 }
00415 
00416 /******************************************************************************
00417 * Initialise the dialog controls from the specified event.
00418 */
00419 void EditAlarmDlg::initValues(const KAEvent* event)
00420 {
00421     setReadOnly(mDesiredReadOnly);
00422 
00423     mChanged           = false;
00424     mOnlyDeferred      = false;
00425     mExpiredRecurrence = false;
00426     mLateCancel->showAutoClose(false);
00427     bool deferGroupVisible = false;
00428     if (event)
00429     {
00430         // Set the values to those for the specified event
00431         if (mTemplate)
00432             mTemplateName->setText(event->templateName());
00433         bool recurs = event->recurs();
00434         if ((recurs || event->repeatCount())  &&  !mTemplate  &&  event->deferred())
00435         {
00436             deferGroupVisible = true;
00437             mDeferDateTime = event->deferDateTime();
00438             mDeferTimeLabel->setText(mDeferDateTime.formatLocale());
00439             mDeferGroup->show();
00440         }
00441         if (mTemplate)
00442         {
00443             // Editing a template
00444             int afterTime = event->isTemplate() ? event->templateAfterTime() : -1;
00445             bool noTime   = !afterTime;
00446             bool useTime  = !event->mainDateTime().isDateOnly();
00447             RadioButton* button = noTime          ? mTemplateDefaultTime :
00448                                   (afterTime > 0) ? mTemplateUseTimeAfter :
00449                                   useTime         ? mTemplateUseTime : mTemplateAnyTime;
00450             button->setChecked(true);
00451             mTemplateTimeAfter->setValue(afterTime > 0 ? afterTime : 1);
00452             if (!noTime && useTime)
00453                 mTemplateTime->setValue(event->mainDateTime().kDateTime().time());
00454             else
00455                 mTemplateTime->setValue(0);
00456         }
00457         else
00458         {
00459             if (event->isTemplate())
00460             {
00461                 // Initialising from an alarm template: use current date
00462                 KDateTime now = KDateTime::currentDateTime(Preferences::timeZone());
00463                 int afterTime = event->templateAfterTime();
00464                 if (afterTime >= 0)
00465                 {
00466                     mTimeWidget->setDateTime(now.addSecs(afterTime * 60));
00467                     mTimeWidget->selectTimeFromNow();
00468                 }
00469                 else
00470                 {
00471                     KDateTime dt = event->startDateTime().kDateTime();
00472                     dt.setTimeSpec(Preferences::timeZone());
00473                     QDate d = now.date();
00474                     if (!dt.isDateOnly()  &&  now.time() >= dt.time())
00475                         d = d.addDays(1);     // alarm time has already passed, so use tomorrow
00476                     dt.setDate(d);
00477                     mTimeWidget->setDateTime(dt);
00478                 }
00479             }
00480             else
00481             {
00482                 mExpiredRecurrence = recurs && event->mainExpired();
00483                 mTimeWidget->setDateTime(recurs || event->category() == KCalEvent::ARCHIVED ? event->startDateTime()
00484                                          : event->mainExpired() ? event->deferDateTime() : event->mainDateTime());
00485             }
00486         }
00487 
00488         KAEvent::Action action = event->action();
00489         AlarmText altext;
00490         if (event->commandScript())
00491             altext.setScript(event->cleanText());
00492         else
00493             altext.setText(event->cleanText());
00494         setAction(action, altext);
00495 
00496         mLateCancel->setMinutes(event->lateCancel(), event->startDateTime().isDateOnly(),
00497                                 TimePeriod::HoursMinutes);
00498         if (mShowInKorganizer)
00499             mShowInKorganizer->setChecked(event->copyToKOrganizer());
00500         type_initValues(event);
00501         mRecurrenceEdit->set(*event, (mTemplate || event->isTemplate()));   // must be called after mTimeWidget is set up, to ensure correct date-only enabling
00502         mTabs->setTabText(mRecurPageIndex, recurText(*event));
00503     }
00504     else
00505     {
00506         // Set the values to their defaults
00507         KDateTime defaultTime = KDateTime::currentUtcDateTime().addSecs(60).toTimeSpec(Preferences::timeZone());
00508         if (mTemplate)
00509         {
00510             mTemplateDefaultTime->setChecked(true);
00511             mTemplateTime->setValue(0);
00512             mTemplateTimeAfter->setValue(1);
00513         }
00514         else
00515             mTimeWidget->setDateTime(defaultTime);
00516         mLateCancel->setMinutes((Preferences::defaultLateCancel() ? 1 : 0), false, TimePeriod::HoursMinutes);
00517         if (mShowInKorganizer)
00518             mShowInKorganizer->setChecked(Preferences::defaultCopyToKOrganizer());
00519         type_initValues(0);
00520         mRecurrenceEdit->setDefaults(defaultTime);   // must be called after mTimeWidget is set up, to ensure correct date-only enabling
00521         slotRecurFrequencyChange();      // update the Recurrence text
00522     }
00523 
00524     if (!deferGroupVisible  &&  mDeferGroup)
00525         mDeferGroup->hide();
00526 
00527     bool empty = AlarmCalendar::resources()->events(KCalEvent::TEMPLATE).isEmpty();
00528     enableButton(Default, !empty);
00529 }
00530 
00531 /******************************************************************************
00532 * Set the read-only status of all non-template controls.
00533 */
00534 void EditAlarmDlg::setReadOnly(bool readOnly)
00535 {
00536     mReadOnly = readOnly;
00537 
00538     if (mTimeWidget)
00539         mTimeWidget->setReadOnly(readOnly);
00540     mLateCancel->setReadOnly(readOnly);
00541     if (mDeferChangeButton)
00542     {
00543         if (readOnly)
00544             mDeferChangeButton->hide();
00545         else
00546             mDeferChangeButton->show();
00547     }
00548     if (mShowInKorganizer)
00549         mShowInKorganizer->setReadOnly(readOnly);
00550 }
00551 
00552 /******************************************************************************
00553 * Save the state of all controls.
00554 */
00555 void EditAlarmDlg::saveState(const KAEvent* event)
00556 {
00557     delete mSavedEvent;
00558     mSavedEvent = 0;
00559     if (event)
00560         mSavedEvent = new KAEvent(*event);
00561     if (mTemplate)
00562     {
00563         mSavedTemplateName      = mTemplateName->text();
00564         mSavedTemplateTimeType  = mTemplateTimeGroup->checkedButton();
00565         mSavedTemplateTime      = mTemplateTime->time();
00566         mSavedTemplateAfterTime = mTemplateTimeAfter->value();
00567     }
00568     checkText(mSavedTextFileCommandMessage, false);
00569     if (mTimeWidget)
00570         mSavedDateTime = mTimeWidget->getDateTime(0, false, false);
00571     mSavedLateCancel       = mLateCancel->minutes();
00572     if (mShowInKorganizer)
00573         mSavedShowInKorganizer = mShowInKorganizer->isChecked();
00574     mSavedRecurrenceType   = mRecurrenceEdit->repeatType();
00575 }
00576 
00577 /******************************************************************************
00578 * Check whether any of the controls has changed state since the dialog was
00579 * first displayed.
00580 * Reply = true if any non-deferral controls have changed, or if it's a new event.
00581 *       = false if no non-deferral controls have changed. In this case,
00582 *         mOnlyDeferred indicates whether deferral controls may have changed.
00583 */
00584 bool EditAlarmDlg::stateChanged() const
00585 {
00586     mChanged      = true;
00587     mOnlyDeferred = false;
00588     if (!mSavedEvent)
00589         return true;
00590     QString textFileCommandMessage;
00591     checkText(textFileCommandMessage, false);
00592     if (mTemplate)
00593     {
00594         if (mSavedTemplateName     != mTemplateName->text()
00595         ||  mSavedTemplateTimeType != mTemplateTimeGroup->checkedButton()
00596         ||  (mTemplateUseTime->isChecked()  &&  mSavedTemplateTime != mTemplateTime->time())
00597         ||  (mTemplateUseTimeAfter->isChecked()  &&  mSavedTemplateAfterTime != mTemplateTimeAfter->value()))
00598             return true;
00599     }
00600     else
00601     {
00602         KDateTime dt = mTimeWidget->getDateTime(0, false, false);
00603         if (mSavedDateTime.timeSpec() != dt.timeSpec()  ||  mSavedDateTime != dt)
00604             return true;
00605     }
00606     if (mSavedLateCancel       != mLateCancel->minutes()
00607     ||  (mShowInKorganizer && mSavedShowInKorganizer != mShowInKorganizer->isChecked())
00608     ||  textFileCommandMessage != mSavedTextFileCommandMessage
00609     ||  mSavedRecurrenceType   != mRecurrenceEdit->repeatType())
00610         return true;
00611     if (type_stateChanged())
00612         return true;
00613     if (mRecurrenceEdit->stateChanged())
00614         return true;
00615     if (mSavedEvent  &&  mSavedEvent->deferred())
00616         mOnlyDeferred = true;
00617     mChanged = false;
00618     return false;
00619 }
00620 
00621 /******************************************************************************
00622 * Get the currently entered dialog data.
00623 * The data is returned in the supplied KAEvent instance.
00624 * Reply = false if the only change has been to an existing deferral.
00625 */
00626 bool EditAlarmDlg::getEvent(KAEvent& event, AlarmResource*& resource)
00627 {
00628     resource = mResource;
00629     if (mChanged)
00630     {
00631         // It's a new event, or the edit controls have changed
00632         setEvent(event, mAlarmMessage, false);
00633         return true;
00634     }
00635 
00636     // Only the deferral time may have changed
00637     event = *mSavedEvent;
00638     if (mOnlyDeferred)
00639     {
00640         // Just modify the original event, to avoid expired recurring events
00641         // being returned as rubbish.
00642         if (mDeferDateTime.isValid())
00643             event.defer(mDeferDateTime, event.reminderDeferral(), false);
00644         else
00645             event.cancelDefer();
00646     }
00647     return false;
00648 }
00649 
00650 /******************************************************************************
00651 *  Extract the data in the dialog and set up a KAEvent from it.
00652 *  If 'trial' is true, the event is set up for a simple one-off test, ignoring
00653 *  recurrence, reminder, template etc. data.
00654 */
00655 void EditAlarmDlg::setEvent(KAEvent& event, const QString& text, bool trial)
00656 {
00657     KDateTime dt;
00658     if (!trial)
00659     {
00660         if (!mTemplate)
00661             dt = mAlarmDateTime.effectiveKDateTime();
00662         else if (mTemplateUseTime->isChecked())
00663             dt = KDateTime(QDate(2000,1,1), mTemplateTime->time());
00664     }
00665 
00666     type_setEvent(event, dt, text, (trial ? 0 : mLateCancel->minutes()), trial);
00667 
00668     if (!trial)
00669     {
00670         if (mRecurrenceEdit->repeatType() != RecurrenceEdit::NO_RECUR)
00671         {
00672             mRecurrenceEdit->updateEvent(event, !mTemplate);
00673             KDateTime now = KDateTime::currentDateTime(mAlarmDateTime.timeSpec());
00674             bool dateOnly = mAlarmDateTime.isDateOnly();
00675             if ((dateOnly  &&  mAlarmDateTime.date() < now.date())
00676             ||  (!dateOnly  &&  mAlarmDateTime.kDateTime() < now))
00677             {
00678                 // A timed recurrence has an entered start date which has
00679                 // already expired, so we must adjust the next repetition.
00680                 event.setNextOccurrence(now);
00681             }
00682             mAlarmDateTime = event.startDateTime();
00683             if (mDeferDateTime.isValid()  &&  mDeferDateTime < mAlarmDateTime)
00684             {
00685                 bool deferral = true;
00686                 bool deferReminder = false;
00687                 int reminder = mReminder ? mReminder->minutes() : 0;
00688                 if (reminder)
00689                 {
00690                     DateTime remindTime = mAlarmDateTime.addMins(-reminder);
00691                     if (mDeferDateTime >= remindTime)
00692                     {
00693                         if (remindTime > KDateTime::currentUtcDateTime())
00694                             deferral = false;    // ignore deferral if it's after next reminder
00695                         else if (mDeferDateTime > remindTime)
00696                             deferReminder = true;    // it's the reminder which is being deferred
00697                     }
00698                 }
00699                 if (deferral)
00700                     event.defer(mDeferDateTime, deferReminder, false);
00701             }
00702         }
00703         if (mTemplate)
00704         {
00705             int afterTime = mTemplateDefaultTime->isChecked() ? 0
00706                           : mTemplateUseTimeAfter->isChecked() ? mTemplateTimeAfter->value() : -1;
00707             event.setTemplate(mTemplateName->text(), afterTime);
00708         }
00709     }
00710 }
00711 
00712 /******************************************************************************
00713 * Get the currently specified alarm flag bits.
00714 */
00715 int EditAlarmDlg::getAlarmFlags() const
00716 {
00717     return (mShowInKorganizer && mShowInKorganizer->isChecked()                  ? KAEvent::COPY_KORGANIZER : 0)
00718          | (mRecurrenceEdit->repeatType() == RecurrenceEdit::AT_LOGIN            ? KAEvent::REPEAT_AT_LOGIN : 0)
00719          | ((mTemplate ? mTemplateAnyTime->isChecked() : mAlarmDateTime.isDateOnly()) ? KAEvent::ANY_TIME : 0);
00720 }
00721 
00722 /******************************************************************************
00723 *  Called when the dialog is displayed.
00724 *  The first time through, sets the size to the same as the last time it was
00725 *  displayed.
00726 */
00727 void EditAlarmDlg::showEvent(QShowEvent* se)
00728 {
00729     KDialog::showEvent(se);
00730     if (!mDeferGroupHeight)
00731     {
00732         if (mDeferGroup)
00733             mDeferGroupHeight = mDeferGroup->height() + spacingHint();
00734         QSize s;
00735         if (KAlarm::readConfigWindowSize(mTemplate ? TEMPLATE_DIALOG_NAME : EDIT_DIALOG_NAME, s))
00736         {
00737             bool defer = mDeferGroup && !mDeferGroup->isHidden();
00738             s.setHeight(s.height() + (defer ? mDeferGroupHeight : 0));
00739             if (!defer)
00740                 DialogScroll<EditAlarmDlg>::setSized();
00741             resize(s);
00742         }
00743     }
00744     KWindowSystem::setOnDesktop(winId(), mDesktop);    // ensure it displays on the desktop expected by the user
00745 }
00746 
00747 /******************************************************************************
00748 * Return the minimum size for the dialog.
00749 * If the minimum size would be too high to fit the desktop, the tab contents
00750 * are made scrollable.
00751 */
00752 QSize EditAlarmDlg::minimumSizeHint() const
00753 {
00754     QSize s = DialogScroll<EditAlarmDlg>::initMinimumHeight(const_cast<EditAlarmDlg*>(this));
00755     if (s.isValid())
00756         return s;
00757     return KDialog::minimumSizeHint();
00758 }
00759 
00760 /******************************************************************************
00761 *  Called when the dialog's size has changed.
00762 *  Records the new size (adjusted to ignore the optional height of the deferred
00763 *  time edit widget) in the config file.
00764 */
00765 void EditAlarmDlg::resizeEvent(QResizeEvent* re)
00766 {
00767     if (isVisible())
00768     {
00769         QSize s = re->size();
00770         s.setHeight(s.height() - (!mDeferGroup || mDeferGroup->isHidden() ? 0 : mDeferGroupHeight));
00771         KAlarm::writeConfigWindowSize(mTemplate ? TEMPLATE_DIALOG_NAME : EDIT_DIALOG_NAME, s);
00772     }
00773     KDialog::resizeEvent(re);
00774 }
00775 
00776 /******************************************************************************
00777 *  Called when any button is clicked.
00778 */
00779 void EditAlarmDlg::slotButtonClicked(int button)
00780 {
00781     if (button == Ok)
00782     {
00783         if (validate())
00784             accept();
00785     }
00786     else
00787         KDialog::slotButtonClicked(button);
00788 }
00789 
00790 /******************************************************************************
00791 *  Called when the OK button is clicked.
00792 *  Validate the input data.
00793 */
00794 bool EditAlarmDlg::validate()
00795 {
00796     if (!stateChanged())
00797     {
00798         // No changes have been made except possibly to an existing deferral
00799         if (!mOnlyDeferred)
00800             reject();
00801         return mOnlyDeferred;
00802     }
00803     RecurrenceEdit::RepeatType recurType = mRecurrenceEdit->repeatType();
00804     if (mTimeWidget
00805     &&  mTabs->currentIndex() == mRecurPageIndex  &&  recurType == RecurrenceEdit::AT_LOGIN)
00806         mTimeWidget->setDateTime(mRecurrenceEdit->endDateTime());
00807     bool timedRecurrence = mRecurrenceEdit->isTimedRepeatType();    // does it recur other than at login?
00808     if (mTemplate)
00809     {
00810         // Check that the template name is not blank and is unique
00811         QString errmsg;
00812         QString name = mTemplateName->text();
00813         if (name.isEmpty())
00814             errmsg = i18nc("@info", "You must enter a name for the alarm template");
00815         else if (name != mSavedTemplateName)
00816         {
00817             if (AlarmCalendar::resources()->templateEvent(name))
00818                 errmsg = i18nc("@info", "Template name is already in use");
00819         }
00820         if (!errmsg.isEmpty())
00821         {
00822             mTemplateName->setFocus();
00823             KMessageBox::sorry(this, errmsg);
00824             return false;
00825         }
00826     }
00827     else if (mTimeWidget)
00828     {
00829         QWidget* errWidget;
00830         mAlarmDateTime = mTimeWidget->getDateTime(0, !timedRecurrence, false, &errWidget);
00831         if (errWidget)
00832         {
00833             // It's more than just an existing deferral being changed, so the time matters
00834             mTabs->setCurrentIndex(mMainPageIndex);
00835             errWidget->setFocus();
00836             mTimeWidget->getDateTime();   // display the error message now
00837             return false;
00838         }
00839     }
00840     if (!type_validate(false))
00841         return false;
00842     if (!mTemplate)
00843     {
00844         if (timedRecurrence)
00845         {
00846             KAEvent event;
00847             AlarmResource* r;
00848             getEvent(event, r);     // this may adjust mAlarmDateTime
00849             KDateTime now = KDateTime::currentDateTime(mAlarmDateTime.timeSpec());
00850             bool dateOnly = mAlarmDateTime.isDateOnly();
00851             if ((dateOnly  &&  mAlarmDateTime.date() < now.date())
00852             ||  (!dateOnly  &&  mAlarmDateTime.kDateTime() < now))
00853             {
00854                 // A timed recurrence has an entered start date which
00855                 // has already expired, so we must adjust it.
00856                 if (event.nextOccurrence(now, mAlarmDateTime, KAEvent::ALLOW_FOR_REPETITION) == KAEvent::NO_OCCURRENCE)
00857                 {
00858                     KMessageBox::sorry(this, i18nc("@info", "Recurrence has already expired"));
00859                     return false;
00860                 }
00861                 if (event.workTimeOnly()  &&  !event.nextTrigger(KAEvent::DISPLAY_TRIGGER).isValid())
00862                 {
00863                     if (KMessageBox::warningContinueCancel(this, i18nc("@info", "The alarm will never occur during working hours"))
00864                         != KMessageBox::Continue)
00865                         return false;
00866                 }
00867             }
00868         }
00869         QString errmsg;
00870         QWidget* errWidget = mRecurrenceEdit->checkData(mAlarmDateTime.effectiveKDateTime(), errmsg);
00871         if (errWidget)
00872         {
00873             mTabs->setCurrentIndex(mRecurPageIndex);
00874             errWidget->setFocus();
00875             KMessageBox::sorry(this, errmsg);
00876             return false;
00877         }
00878     }
00879     if (recurType != RecurrenceEdit::NO_RECUR)
00880     {
00881         KAEvent recurEvent;
00882         int longestRecurMinutes = -1;
00883         int reminder = mReminder ? mReminder->minutes() : 0;
00884         if (reminder  &&  !mReminder->isOnceOnly())
00885         {
00886             mRecurrenceEdit->updateEvent(recurEvent, false);
00887             longestRecurMinutes = recurEvent.longestRecurrenceInterval().asSeconds() / 60;
00888             if (longestRecurMinutes  &&  reminder >= longestRecurMinutes)
00889             {
00890                 mTabs->setCurrentIndex(mMainPageIndex);
00891                 mReminder->setFocusOnCount();
00892                 KMessageBox::sorry(this, i18nc("@info", "Reminder period must be less than the recurrence interval, unless <interface>%1</interface> is checked."
00893                                  , Reminder::i18n_chk_FirstRecurrenceOnly()));
00894                 return false;
00895             }
00896         }
00897         if (mRecurrenceEdit->subRepeatCount())
00898         {
00899             if (longestRecurMinutes < 0)
00900             {
00901                 mRecurrenceEdit->updateEvent(recurEvent, false);
00902                 longestRecurMinutes = recurEvent.longestRecurrenceInterval().asSeconds() / 60;
00903             }
00904             if (longestRecurMinutes > 0
00905             &&  recurEvent.repeatInterval().asSeconds()/60 * recurEvent.repeatCount() >= longestRecurMinutes - reminder)
00906             {
00907                 KMessageBox::sorry(this, i18nc("@info", "The duration of a repetition within the recurrence must be less than the recurrence interval minus any reminder period"));
00908                 mRecurrenceEdit->activateSubRepetition();   // display the alarm repetition dialog again
00909                 return false;
00910             }
00911             if (!recurEvent.repeatInterval().isDaily()
00912             &&  ((mTemplate && mTemplateAnyTime->isChecked())  ||  (!mTemplate && mAlarmDateTime.isDateOnly())))
00913             {
00914                 KMessageBox::sorry(this, i18nc("@info", "For a repetition within the recurrence, its period must be in units of days or weeks for a date-only alarm"));
00915                 mRecurrenceEdit->activateSubRepetition();   // display the alarm repetition dialog again
00916                 return false;
00917             }
00918         }
00919     }
00920     if (!checkText(mAlarmMessage))
00921         return false;
00922 
00923     mResource = 0;
00924     // A null resource event ID indicates that the caller already
00925     // knows which resource to use.
00926     if (!mResourceEventId.isNull())
00927     {
00928         if (!mResourceEventId.isEmpty())
00929         {
00930             mResource = AlarmCalendar::resources()->resourceForEvent(mResourceEventId);
00931             AlarmResource::Type type = mTemplate ? AlarmResource::TEMPLATE : AlarmResource::ACTIVE;
00932             if (mResource->alarmType() != type)
00933                 mResource = 0;   // event may have expired while dialog was open
00934         }
00935         bool cancelled = false;
00936         if (!mResource  ||  !mResource->writable())
00937         {
00938             KCalEvent::Status type = mTemplate ? KCalEvent::TEMPLATE : KCalEvent::ACTIVE;
00939             mResource = AlarmResources::instance()->destination(type, this, false, &cancelled);
00940         }
00941         if (!mResource)
00942         {
00943             if (!cancelled)
00944                 KMessageBox::sorry(this, i18nc("@info", "You must select a resource to save the alarm in"));
00945             return false;
00946         }
00947     }
00948     return true;
00949 }
00950 
00951 /******************************************************************************
00952 *  Called when the Try button is clicked.
00953 *  Display/execute the alarm immediately for the user to check its configuration.
00954 */
00955 void EditAlarmDlg::slotTry()
00956 {
00957     QString text;
00958     if (checkText(text))
00959     {
00960         if (!type_validate(true))
00961             return;
00962         KAEvent event;
00963         setEvent(event, text, true);
00964         connect(theApp(), SIGNAL(execAlarmSuccess()), SLOT(slotTrySuccess()));
00965         void* proc = theApp()->execAlarm(event, event.firstAlarm(), false, false);
00966         if (proc  &&  proc != (void*)-1)
00967             type_trySuccessMessage((ShellProcess*)proc, text);
00968     }
00969 }
00970 
00971 /******************************************************************************
00972 *  Called when the Try action has completed, successfully.
00973 */
00974 void EditAlarmDlg::slotTrySuccess()
00975 {
00976     type_trySuccessMessage(0, QString());