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     beforeafterpos = 2; // to-dos default to alarms before the due start
00255   }
00256   if ( alarm->hasEndOffset() ) {
00257     beforeafterpos = 2;
00258     offset = alarm->endOffset().asSeconds();
00259   } else {
00260     // TODO: Also allow alarms at fixed times, not relative to start/end
00261     offset = alarm->startOffset().asSeconds();
00262   }
00263   // Negative offset means before the start/end...
00264   if ( offset < 0 ) {
00265     offset = -offset;
00266   } else {
00267     ++beforeafterpos;
00268   }
00269   mWidget->mBeforeAfter->setCurrentItem( beforeafterpos );
00270 
00271   offset = offset / 60; // make minutes
00272   int useoffset = offset;
00273 
00274   if ( offset % (24*60) == 0 && offset>0 ) { // divides evenly into days?
00275     useoffset = offset / (24*60);
00276     mWidget->mOffsetUnit->setCurrentItem( 2 );
00277   } else if (offset % 60 == 0 && offset>0 ) { // divides evenly into hours?
00278     useoffset = offset / 60;
00279     mWidget->mOffsetUnit->setCurrentItem( 1 );
00280   } else {
00281     useoffset = offset;
00282     mWidget->mOffsetUnit->setCurrentItem( 0 );
00283   }
00284   mWidget->mAlarmOffset->setValue( useoffset );
00285 
00286 
00287   // Repeating
00288   mWidget->mRepeats->setChecked( alarm->repeatCount()>0 );
00289   if ( alarm->repeatCount()>0 ) {
00290     mWidget->mRepeatCount->setValue( alarm->repeatCount() );
00291     mWidget->mRepeatInterval->setValue( alarm->snoozeTime().asSeconds() / 60 ); // show as minutes
00292   }
00293 
00294   switch ( alarm->type() ) {
00295     case KCal::Alarm::Audio:
00296         mWidget->mAlarmType->setButton( 1 );
00297         mWidget->mSoundFile->setURL( alarm->audioFile() );
00298         break;
00299     case KCal::Alarm::Procedure:
00300         mWidget->mAlarmType->setButton( 2 );
00301         mWidget->mApplication->setURL( alarm->programFile() );
00302         mWidget->mAppArguments->setText( alarm->programArguments() );
00303         break;
00304     case KCal::Alarm::Email: {
00305         mWidget->mAlarmType->setButton( 3 );
00306         QValueList<KCal::Person> addresses = alarm->mailAddresses();
00307         QStringList add;
00308         for ( QValueList<KCal::Person>::ConstIterator it = addresses.begin();
00309               it != addresses.end(); ++it ) {
00310           add << (*it).fullName();
00311         }
00312         mWidget->mEmailAddress->setText( add.join(", ") );
00313         mWidget->mEmailText->setText( alarm->mailText() );
00314         break;}
00315     case KCal::Alarm::Display:
00316     case KCal::Alarm::Invalid:
00317     default:
00318         mWidget->mAlarmType->setButton( 0 );
00319         mWidget->mDisplayText->setText( alarm->text() );
00320         break;
00321   }
00322 
00323   mWidget->mTypeStack->raiseWidget( mWidget->mAlarmType->selectedId() );
00324 
00325   mInitializing = false;
00326 }
00327 
00328 void KOEditorAlarms::writeAlarm( KCal::Alarm *alarm )
00329 {
00330   // Offsets
00331   int offset = mWidget->mAlarmOffset->value()*60; // minutes
00332   int offsetunit = mWidget->mOffsetUnit->currentItem();
00333   if ( offsetunit >= 1 ) offset *= 60; // hours
00334   if ( offsetunit >= 2 ) offset *= 24; // days
00335   if ( offsetunit >= 3 ) offset *= 7; // weeks
00336 
00337   int beforeafterpos = mWidget->mBeforeAfter->currentItem();
00338   if ( beforeafterpos % 2 == 0 ) { // before -> negative
00339     offset = -offset;
00340   }
00341 
00342   // TODO: Add possibility to specify a given time for the reminder
00343   if ( beforeafterpos / 2 == 0 ) { // start offset
00344     alarm->setStartOffset( KCal::Duration( offset ) );
00345   } else {
00346     alarm->setEndOffset( KCal::Duration( offset ) );
00347   }
00348 
00349   // Repeating
00350   if ( mWidget->mRepeats->isChecked() ) {
00351     alarm->setRepeatCount( mWidget->mRepeatCount->value() );
00352     alarm->setSnoozeTime( KCal::Duration( mWidget->mRepeatInterval->value() * 60 ) ); // convert back to seconds
00353   } else {
00354     alarm->setRepeatCount( 0 );
00355   }
00356 
00357   switch ( mWidget->mAlarmType->selectedId() ) {
00358     case 1: // Audio
00359         alarm->setAudioAlarm( mWidget->mSoundFile->url() );
00360         break;
00361     case 2: // Procedure
00362         alarm->setProcedureAlarm( mWidget->mApplication->url(), mWidget->mAppArguments->text() );
00363         break;
00364     case 3: { // Email
00365         QStringList addresses = KPIM::splitEmailAddrList( mWidget->mEmailAddress->text() );
00366         QValueList<KCal::Person> add;
00367         for ( QStringList::Iterator it = addresses.begin(); it != addresses.end();
00368               ++it ) {
00369           add << KCal::Person( *it );
00370         }
00371         // TODO: Add a subject line and possibilities for attachments
00372         alarm->setEmailAlarm( QString::null, mWidget->mEmailText->text(),
00373                               add );
00374         break; }
00375     case 0: // Display
00376     default:
00377         alarm->setDisplayAlarm( mWidget->mDisplayText->text() );
00378         break;
00379   }
00380 }
00381 
00382 void KOEditorAlarms::selectionChanged( QListViewItem *listviewitem )
00383 {
00384   AlarmListViewItem *item = dynamic_cast<AlarmListViewItem*>(listviewitem);
00385   mCurrentItem = item;
00386   mWidget->mTimeGroup->setEnabled( item );
00387   mWidget->mTypeGroup->setEnabled( item );
00388   if ( item ) {
00389     readAlarm( item->alarm() );
00390   }
00391 }
00392 
00393 void KOEditorAlarms::slotOk()
00394 {
00395   // save the current item settings, if any
00396   changed();
00397 
00398   // copy the mAlarms list
00399   if ( mAlarms ) {
00400     mAlarms->clear();
00401     QListViewItemIterator it( mWidget->mAlarmList );
00402     while ( it.current() ) {
00403       AlarmListViewItem *item = dynamic_cast<AlarmListViewItem*>(*it);
00404       if ( item ) {
00405         mAlarms->append( new KCal::Alarm( *(item->alarm()) ) );
00406       }
00407       ++it;
00408     }
00409   }
00410   accept();
00411 }
00412 
00413 void KOEditorAlarms::slotAdd()
00414 {
00415   mCurrentItem = new AlarmListViewItem( mWidget->mAlarmList, 0, mType );
00416   mWidget->mAlarmList->setCurrentItem( mCurrentItem );
00417 }
00418 
00419 void KOEditorAlarms::slotDuplicate()
00420 {
00421   if ( mCurrentItem ) {
00422     mCurrentItem = new AlarmListViewItem( mWidget->mAlarmList, mCurrentItem->alarm(), mType );
00423     mWidget->mAlarmList->setCurrentItem( mCurrentItem );
00424   }
00425 }
00426 
00427 void KOEditorAlarms::slotRemove()
00428 {
00429   if ( mCurrentItem ) {
00430     delete mCurrentItem;
00431     mCurrentItem = dynamic_cast<AlarmListViewItem*>( mWidget->mAlarmList->currentItem() );
00432     mWidget->mAlarmList->setSelected( mCurrentItem, true );
00433   }
00434 }
00435 
00436 void KOEditorAlarms::init()
00437 {
00438   mInitializing = true;
00439 
00440   // Tweak some UI stuff depending on the Incidence type
00441   if ( mType == "Todo" ) {
00442     // Replace before/after end datetime with before/after due datetime
00443     mWidget->mBeforeAfter->clear();
00444     mWidget->mBeforeAfter->insertItem( i18n( "before the to-do starts" ), 0 );
00445     mWidget->mBeforeAfter->insertItem( i18n( "after the to-do starts" ), 1 );
00446     mWidget->mBeforeAfter->insertItem( i18n( "before the to-do is due" ), 2 );
00447     mWidget->mBeforeAfter->insertItem( i18n( "after the to-do is due" ), 3 );
00448     QToolTip::add(
00449       mWidget->mBeforeAfter,
00450       i18n( "Select the reminder trigger relative to the start or due time" ) );
00451     QWhatsThis::add(
00452       mWidget->mBeforeAfter,
00453       i18n( "Use this combobox to specify if you want the reminder to "
00454             "trigger before or after the start or due time." ) );
00455 
00456     mWidget->mBeforeAfter->setCurrentItem( 2 );  // default is before due start
00457   }
00458 
00459   // Fill-in existing alarms
00460   KCal::Alarm::List::ConstIterator it;
00461   for ( it = mAlarms->begin(); it != mAlarms->end(); ++it ) {
00462     new AlarmListViewItem( mWidget->mAlarmList, *it, mType );
00463   }
00464   mWidget->mAlarmList->setSelected( mWidget->mAlarmList->firstChild(), true );
00465   mInitializing = false;
00466 }
00467 
00468 #include "koeditoralarms.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys