korganizer

koeditoralarms.cpp

00001 /*
00002     This file is part of KOrganizer.
00003 
00004     Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
00005     Copyright (C) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
00006 
00007     This program is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU General Public License as published by
00009     the Free Software Foundation; either version 2 of the License, or
00010     (at your option) any later version.
00011 
00012     This program is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00015     GNU General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program; if not, write to the Free Software
00019     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020 
00021     As a special exception, permission is given to link this program
00022     with any edition of Qt, and distribute the resulting executable,
00023     without including the source code for Qt in the source distribution.
00024 */
00025 
00026 #include "koeditoralarms_base.h"
00027 #include "koeditoralarms.h"
00028 #include "koprefs.h"
00029 
00030 #include <libkcal/duration.h>
00031 
00032 #include <qlayout.h>
00033 #include <qlistview.h>
00034 #include <qpushbutton.h>
00035 #include <qspinbox.h>
00036 #include <qcombobox.h>
00037 #include <qcheckbox.h>
00038 #include <qbuttongroup.h>
00039 #include <qtextedit.h>
00040 #include <qwidgetstack.h>
00041 #include <qradiobutton.h>
00042 #include <qtooltip.h>
00043 #include <qwhatsthis.h>
00044 
00045 #include <kurlrequester.h>
00046 #include <klocale.h>
00047 #include <kdebug.h>
00048 
00049 #include <libkcal/alarm.h>
00050 #include <libkcal/incidence.h>
00051 
00052 #include <libemailfunctions/email.h>
00053 
00054 class AlarmListViewItem : public QListViewItem
00055 {
00056   public:
00057     AlarmListViewItem( QListView *parent, KCal::Alarm *alarm, const QCString &inctype );
00058     virtual ~AlarmListViewItem();
00059     KCal::Alarm *alarm() const { return mAlarm; }
00060     void construct();
00061     enum AlarmViewColumns { ColAlarmType=0, ColAlarmOffset, ColAlarmRepeat };
00062 
00063   protected:
00064     KCal::Alarm *mAlarm;
00065 
00066   private:
00067     QCString mIncType;
00068 };
00069 
00070 AlarmListViewItem::AlarmListViewItem( QListView *parent, KCal::Alarm *alarm,
00071                                       const QCString &inctype )
00072     : QListViewItem( parent ), mIncType( inctype )
00073 {
00074   if ( alarm ) {
00075     mAlarm = new KCal::Alarm( *alarm );
00076   } else {
00077     mAlarm = new KCal::Alarm( 0 );
00078     mAlarm->setType( KCal::Alarm::Display );
00079     int duration; // in secs
00080     switch( KOPrefs::instance()->mReminderTimeUnits ) {
00081     default:
00082     case 0: // mins
00083       duration = KOPrefs::instance()->mReminderTime * 60;
00084       break;
00085     case 1: // hours
00086       duration = KOPrefs::instance()->mReminderTime * 60 * 60;
00087       break;
00088     case 2: // days
00089       duration = KOPrefs::instance()->mReminderTime * 60 * 60 * 24;
00090       break;
00091     }
00092     if ( mIncType == "Event" ) {
00093       mAlarm->setStartOffset( KCal::Duration( -duration ) );
00094     } else {
00095       mAlarm->setEndOffset( KCal::Duration( -duration ) );
00096     }
00097   }
00098   construct();
00099 }
00100 
00101 AlarmListViewItem::~AlarmListViewItem()
00102 {
00103   delete mAlarm;
00104 }
00105 
00106 void AlarmListViewItem::construct()
00107 {
00108   if ( mAlarm ) {
00109     // Alarm type:
00110     QString type;
00111     switch ( mAlarm->type() ) {
00112       case KCal::Alarm::Display:
00113         type = i18n("Reminder Dialog");
00114         break;
00115       case KCal::Alarm::Procedure:
00116         type = i18n("Program");
00117         break;
00118       case KCal::Alarm::Email:
00119         type = i18n("Email");
00120         break;
00121       case KCal::Alarm::Audio:
00122         type = i18n("Audio");
00123         break;
00124       default:
00125         type = i18n("Unknown");
00126         break;
00127     }
00128     setText( ColAlarmType, type );
00129 
00130     // Alarm offset:
00131     QString offsetstr;
00132     int offset = 0;
00133     if ( mAlarm->hasStartOffset() ) {
00134       offset = mAlarm->startOffset().asSeconds();
00135       if ( offset < 0 ) {
00136         offsetstr = i18n( "N days/hours/minutes before/after the start/end",
00137                           "%1 before the start" );
00138         offset = -offset;
00139       } else {
00140         offsetstr = i18n( "N days/hours/minutes before/after the start/end",
00141                           "%1 after the start" );
00142       }
00143     } else if ( mAlarm->hasEndOffset() ) {
00144       offset = mAlarm->endOffset().asSeconds();
00145       if ( offset < 0 ) {
00146         if ( mIncType == "Todo" ) {
00147           offsetstr = i18n( "N days/hours/minutes before/after the due date",
00148                             "%1 before the to-do is due" );
00149         } else {
00150           offsetstr = i18n( "N days/hours/minutes before/after the start/end",
00151                             "%1 before the end" );
00152         }
00153         offset = -offset;
00154       } else {
00155         if ( mIncType == "Todo" ) {
00156           offsetstr = i18n( "N days/hours/minutes before/after the due date",
00157                             "%1 after the to-do is due" );
00158         } else {
00159           offsetstr = i18n( "N days/hours/minutes before/after the start/end",
00160                             "%1 after the end" );
00161         }
00162       }
00163     }
00164 
00165     offset = offset / 60; // make minutes
00166     int useoffset = offset;
00167 
00168     if ( offset % (24*60) == 0 && offset>0 ) { // divides evenly into days?
00169       useoffset = offset / (24*60);
00170       offsetstr = offsetstr.arg( i18n("1 day", "%n days", useoffset ) );
00171     } else if (offset % 60 == 0 && offset>0 ) { // divides evenly into hours?
00172       useoffset = offset / 60;
00173       offsetstr = offsetstr.arg( i18n("1 hour", "%n hours", useoffset ) );
00174     } else {
00175       useoffset = offset;
00176       offsetstr = offsetstr.arg( i18n("1 minute", "%n minutes", useoffset ) );
00177     }
00178     setText( ColAlarmOffset, offsetstr );
00179 
00180     // Alarm repeat
00181     if ( mAlarm->repeatCount()>0 ) {
00182       setText( ColAlarmRepeat, i18n("Yes") );
00183     } else {
00184       setText( ColAlarmRepeat, i18n("No") );
00185     }
00186   }
00187 }
00188 
00189 
00190 KOEditorAlarms::KOEditorAlarms( const QCString &type,
00191                                 KCal::Alarm::List *alarms, QWidget *parent,
00192                                 const char *name )
00193   : KDialogBase( parent, name, true, i18n("Advanced Reminders"), Ok | Cancel ),
00194     mType( type ), mAlarms( alarms ),mCurrentItem( 0 )
00195 {
00196   if ( mType != "Todo" ) {
00197     // only Todos and Events can have reminders
00198     mType = "Event";
00199   }
00200   setMainWidget( mWidget = new KOEditorAlarms_base( this ) );
00201   mWidget->mAlarmList->setResizeMode( QListView::LastColumn );
00202   mWidget->mAlarmList->setColumnWidthMode( 0, QListView::Maximum );
00203   mWidget->mAlarmList->setColumnWidthMode( 1, QListView::Maximum );
00204   connect( mWidget->mAlarmList, SIGNAL( selectionChanged( QListViewItem * ) ),
00205            SLOT( selectionChanged( QListViewItem * ) ) );
00206   connect( mWidget->mAddButton, SIGNAL( clicked() ), SLOT( slotAdd() ) );
00207   connect( mWidget->mRemoveButton, SIGNAL( clicked() ), SLOT( slotRemove() ) );
00208   connect( mWidget->mDuplicateButton, SIGNAL( clicked() ), SLOT( slotDuplicate() ) );
00209 
00210   connect( mWidget->mAlarmOffset, SIGNAL( valueChanged( int ) ), SLOT( changed() ) );
00211   connect( mWidget->mOffsetUnit, SIGNAL( activated( int ) ), SLOT( changed() ) );
00212   connect( mWidget->mBeforeAfter, SIGNAL( activated( int ) ), SLOT( changed() ) );
00213   connect( mWidget->mRepeats, SIGNAL( toggled( bool ) ), SLOT( changed() ) );
00214   connect( mWidget->mRepeatCount, SIGNAL( valueChanged( int ) ), SLOT( changed() ) );
00215   connect( mWidget->mRepeatInterval, SIGNAL( valueChanged( int ) ), SLOT( changed() ) );
00216   connect( mWidget->mAlarmType, SIGNAL(clicked(int)), SLOT( changed() ) );
00217   connect( mWidget->mDisplayText, SIGNAL( textChanged() ), SLOT( changed() ) );
00218   connect( mWidget->mSoundFile, SIGNAL( textChanged( const QString & ) ), SLOT( changed() ) );
00219   connect( mWidget->mApplication, SIGNAL( textChanged( const QString & ) ), SLOT( changed() ) );
00220   connect( mWidget->mAppArguments, SIGNAL( textChanged( const QString & ) ), SLOT( changed() ) );
00221   connect( mWidget->mEmailAddress, SIGNAL( textChanged( const QString & ) ), SLOT( changed() ) );
00222   connect( mWidget->mEmailText, SIGNAL( textChanged() ), SLOT( changed() ) );
00223 
00224   init();
00225 
00226   //TODO: backport email reminders from trunk
00227   mWidget->mTypeEmailRadio->hide(); //email reminders not implemented yet
00228 
00229   mWidget->setMinimumSize( 500, 500 );
00230 }
00231 
00232 KOEditorAlarms::~KOEditorAlarms()
00233 {
00234 }
00235 
00236 void KOEditorAlarms::changed()
00237 {
00238   if ( !mInitializing && mCurrentItem ) {
00239     writeAlarm( mCurrentItem->alarm() );
00240     mCurrentItem->construct();
00241   }
00242 }
00243 
00244 void KOEditorAlarms::readAlarm( KCal::Alarm *alarm )
00245 {
00246   if ( !alarm ) return;
00247 
00248   mInitializing = true;
00249 
00250   // Offsets
00251   int offset;
00252   int beforeafterpos = 0;
00253   if ( mType == "Todo" ) {
00254     if ( !alarm->hasStartOffset() ) {
00255       beforeafterpos = 2;
00256     }
00257   }
00258   if ( alarm->hasEndOffset() ) {
00259     beforeafterpos = 2;
00260     offset = alarm->endOffset().asSeconds();
00261   } else {
00262     // TODO: Also allow alarms at fixed times, not relative to start/end
00263     offset = alarm->startOffset().asSeconds();
00264   }
00265   // Negative offset means before the start/end...
00266   if ( offset < 0 ) {
00267     offset = -offset;
00268   } else {
00269     ++beforeafterpos;
00270   }
00271   mWidget->mBeforeAfter->setCurrentItem( beforeafterpos );
00272 
00273   offset = offset / 60; // make minutes
00274   int useoffset = offset;
00275 
00276   if ( offset % (24*60) == 0 && offset>0 ) { // divides evenly into days?
00277     useoffset = offset / (24*60);
00278     mWidget->mOffsetUnit->setCurrentItem( 2 );
00279   } else if (offset % 60 == 0 && offset>0 ) { // divides evenly into hours?
00280     useoffset = offset / 60;
00281     mWidget->mOffsetUnit->setCurrentItem( 1 );
00282   } else {
00283     useoffset = offset;
00284     mWidget->mOffsetUnit->setCurrentItem( 0 );
00285   }
00286   mWidget->mAlarmOffset->setValue( useoffset );
00287 
00288 
00289   // Repeating
00290   mWidget->mRepeats->setChecked( alarm->repeatCount()>0 );
00291   if ( alarm->repeatCount()>0 ) {
00292     mWidget->mRepeatCount->setValue( alarm->repeatCount() );
00293     mWidget->mRepeatInterval->setValue( alarm->snoozeTime().asSeconds() / 60 ); // show as minutes
00294   }
00295 
00296   switch ( alarm->type() ) {
00297     case KCal::Alarm::Audio:
00298         mWidget->mAlarmType->setButton( 1 );
00299         mWidget->mSoundFile->setURL( alarm->audioFile() );
00300         break;
00301     case KCal::Alarm::Procedure:
00302         mWidget->mAlarmType->setButton( 2 );
00303         mWidget->mApplication->setURL( alarm->programFile() );
00304         mWidget->mAppArguments->setText( alarm->programArguments() );
00305         break;
00306     case KCal::Alarm::Email: {
00307         mWidget->mAlarmType->setButton( 3 );
00308         QValueList<KCal::Person> addresses = alarm->mailAddresses();
00309         QStringList add;
00310         for ( QValueList<KCal::Person>::ConstIterator it = addresses.begin();
00311               it != addresses.end(); ++it ) {
00312           add << (*it).fullName();
00313         }
00314         mWidget->mEmailAddress->setText( add.join(", ") );
00315         mWidget->mEmailText->setText( alarm->mailText() );
00316         break;}
00317     case KCal::Alarm::Display:
00318     case KCal::Alarm::Invalid:
00319     default:
00320         mWidget->mAlarmType->setButton( 0 );
00321         mWidget->mDisplayText->setText( alarm->text() );
00322         break;
00323   }
00324 
00325   mWidget->mTypeStack->raiseWidget( mWidget->mAlarmType->selectedId() );
00326 
00327   mInitializing = false;
00328 }
00329 
00330 void KOEditorAlarms::writeAlarm( KCal::Alarm *alarm )
00331 {
00332   // Offsets
00333   int offset = mWidget->mAlarmOffset->value()*60; // minutes
00334   int offsetunit = mWidget->mOffsetUnit->currentItem();
00335   if ( offsetunit >= 1 ) offset *= 60; // hours
00336   if ( offsetunit >= 2 ) offset *= 24; // days
00337   if ( offsetunit >= 3 ) offset *= 7; // weeks
00338 
00339   int beforeafterpos = mWidget->mBeforeAfter->currentItem();
00340   if ( beforeafterpos % 2 == 0 ) { // before -> negative
00341     offset = -offset;
00342   }
00343 
00344   // TODO: Add possibility to specify a given time for the reminder
00345   if ( beforeafterpos / 2 == 0 ) { // start offset
00346     alarm->setStartOffset( KCal::Duration( offset ) );
00347   } else {
00348     alarm->setEndOffset( KCal::Duration( offset ) );
00349   }
00350 
00351   // Repeating
00352   if ( mWidget->mRepeats->isChecked() ) {
00353     alarm->setRepeatCount( mWidget->mRepeatCount->value() );
00354     alarm->setSnoozeTime( KCal::Duration( mWidget->mRepeatInterval->value() * 60 ) ); // convert back to seconds
00355   } else {
00356     alarm->setRepeatCount( 0 );
00357   }
00358 
00359   switch ( mWidget->mAlarmType->selectedId() ) {
00360     case 1: // Audio
00361         alarm->setAudioAlarm( mWidget->mSoundFile->url() );
00362         break;
00363     case 2: // Procedure
00364         alarm->setProcedureAlarm( mWidget->mApplication->url(), mWidget->mAppArguments->text() );
00365         break;
00366     case 3: { // Email
00367         QStringList addresses = KPIM::splitEmailAddrList( mWidget->mEmailAddress->text() );
00368         QValueList<KCal::Person> add;
00369         for ( QStringList::Iterator it = addresses.begin(); it != addresses.end();
00370               ++it ) {
00371           add << KCal::Person( *it );
00372         }
00373         // TODO: Add a subject line and possibilities for attachments
00374         alarm->setEmailAlarm( QString::null, mWidget->mEmailText->text(),
00375                               add );
00376         break; }
00377     case 0: // Display
00378     default:
00379         alarm->setDisplayAlarm( mWidget->mDisplayText->text() );
00380         break;
00381   }
00382 }
00383 
00384 void KOEditorAlarms::selectionChanged( QListViewItem *listviewitem )
00385 {
00386   AlarmListViewItem *item = dynamic_cast<AlarmListViewItem*>(listviewitem);
00387   mCurrentItem = item;
00388   mWidget->mTimeGroup->setEnabled( item );
00389   mWidget->mTypeGroup->setEnabled( item );
00390   if ( item ) {
00391     readAlarm( item->alarm() );
00392   }
00393 }
00394 
00395 void KOEditorAlarms::slotOk()
00396 {
00397   // save the current item settings, if any
00398   changed();
00399 
00400   // copy the mAlarms list
00401   if ( mAlarms ) {
00402     mAlarms->clear();
00403     QListViewItemIterator it( mWidget->mAlarmList );
00404     while ( it.current() ) {
00405       AlarmListViewItem *item = dynamic_cast<AlarmListViewItem*>(*it);
00406       if ( item ) {
00407         mAlarms->append( new KCal::Alarm( *(item->alarm()) ) );
00408       }
00409       ++it;
00410     }
00411   }
00412   accept();
00413 }
00414 
00415 void KOEditorAlarms::slotAdd()
00416 {
00417   mCurrentItem = new AlarmListViewItem( mWidget->mAlarmList, 0, mType );
00418   mWidget->mAlarmList->setCurrentItem( mCurrentItem );
00419 }
00420 
00421 void KOEditorAlarms::slotDuplicate()
00422 {
00423   if ( mCurrentItem ) {
00424     mCurrentItem = new AlarmListViewItem( mWidget->mAlarmList, mCurrentItem->alarm(), mType );
00425     mWidget->mAlarmList->setCurrentItem( mCurrentItem );
00426   }
00427 }
00428 
00429 void KOEditorAlarms::slotRemove()
00430 {
00431   if ( mCurrentItem ) {
00432     delete mCurrentItem;
00433     mCurrentItem = dynamic_cast<AlarmListViewItem*>( mWidget->mAlarmList->currentItem() );
00434     mWidget->mAlarmList->setSelected( mCurrentItem, true );
00435   }
00436 }
00437 
00438 void KOEditorAlarms::init()
00439 {
00440   mInitializing = true;
00441 
00442   // Tweak some UI stuff depending on the Incidence type
00443   if ( mType == "Todo" ) {
00444     // Replace before/after end datetime with before/after due datetime
00445     mWidget->mBeforeAfter->clear();
00446     mWidget->mBeforeAfter->insertItem( i18n( "before the to-do starts" ), 0 );
00447     mWidget->mBeforeAfter->insertItem( i18n( "after the to-do starts" ), 1 );
00448     mWidget->mBeforeAfter->insertItem( i18n( "before the to-do is due" ), 2 );
00449     mWidget->mBeforeAfter->insertItem( i18n( "after the to-do is due" ), 3 );
00450     QToolTip::add(
00451       mWidget->mBeforeAfter,
00452       i18n( "Select the reminder trigger relative to the start or due time" ) );
00453     QWhatsThis::add(
00454       mWidget->mBeforeAfter,
00455       i18n( "Use this combobox to specify if you want the reminder to "
00456             "trigger before or after the start or due time." ) );
00457 
00458     mWidget->mBeforeAfter->setCurrentItem( 2 );  // default is before due start
00459   }
00460 
00461   // Fill-in existing alarms
00462   KCal::Alarm::List::ConstIterator it;
00463   for ( it = mAlarms->begin(); it != mAlarms->end(); ++it ) {
00464     new AlarmListViewItem( mWidget->mAlarmList, *it, mType );
00465   }
00466   mWidget->mAlarmList->setSelected( mWidget->mAlarmList->firstChild(), true );
00467   mInitializing = false;
00468 }
00469 
00470 #include "koeditoralarms.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys