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 TQt, and distribute the resulting executable, 00023 without including the source code for TQt 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 <tqlayout.h> 00033 #include <tqlistview.h> 00034 #include <tqpushbutton.h> 00035 #include <tqspinbox.h> 00036 #include <tqcombobox.h> 00037 #include <tqcheckbox.h> 00038 #include <tqbuttongroup.h> 00039 #include <tqtextedit.h> 00040 #include <tqwidgetstack.h> 00041 #include <tqradiobutton.h> 00042 #include <tqtooltip.h> 00043 #include <tqwhatsthis.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 TQListViewItem 00055 { 00056 public: 00057 AlarmListViewItem( TQListView *parent, KCal::Alarm *alarm, const TQCString &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 TQCString mIncType; 00068 }; 00069 00070 AlarmListViewItem::AlarmListViewItem( TQListView *parent, KCal::Alarm *alarm, 00071 const TQCString &inctype ) 00072 : TQListViewItem( 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 TQString 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 TQString 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 TQCString &type, 00191 KCal::Alarm::List *alarms, TQWidget *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 00202 // The text is set here, and not in the UI file, because the i18n context is not 00203 // properly extracted from the UI file. 00204 mWidget->mAddButton->setText( i18n( "Add a new alarm to the alarm list.", "&Add" ) ); 00205 00206 mWidget->mAlarmList->setResizeMode( TQListView::LastColumn ); 00207 mWidget->mAlarmList->setColumnWidthMode( 0, TQListView::Maximum ); 00208 mWidget->mAlarmList->setColumnWidthMode( 1, TQListView::Maximum ); 00209 connect( mWidget->mAlarmList, TQT_SIGNAL( selectionChanged( TQListViewItem * ) ), 00210 TQT_SLOT( selectionChanged( TQListViewItem * ) ) ); 00211 connect( mWidget->mAddButton, TQT_SIGNAL( clicked() ), TQT_SLOT( slotAdd() ) ); 00212 connect( mWidget->mRemoveButton, TQT_SIGNAL( clicked() ), TQT_SLOT( slotRemove() ) ); 00213 connect( mWidget->mDuplicateButton, TQT_SIGNAL( clicked() ), TQT_SLOT( slotDuplicate() ) ); 00214 00215 connect( mWidget->mAlarmOffset, TQT_SIGNAL( valueChanged( int ) ), TQT_SLOT( changed() ) ); 00216 connect( mWidget->mOffsetUnit, TQT_SIGNAL( activated( int ) ), TQT_SLOT( changed() ) ); 00217 connect( mWidget->mBeforeAfter, TQT_SIGNAL( activated( int ) ), TQT_SLOT( changed() ) ); 00218 connect( mWidget->mRepeats, TQT_SIGNAL( toggled( bool ) ), TQT_SLOT( changed() ) ); 00219 connect( mWidget->mRepeatCount, TQT_SIGNAL( valueChanged( int ) ), TQT_SLOT( changed() ) ); 00220 connect( mWidget->mRepeatInterval, TQT_SIGNAL( valueChanged( int ) ), TQT_SLOT( changed() ) ); 00221 connect( mWidget->mAlarmType, TQT_SIGNAL(clicked(int)), TQT_SLOT( changed() ) ); 00222 connect( mWidget->mDisplayText, TQT_SIGNAL( textChanged() ), TQT_SLOT( changed() ) ); 00223 connect( mWidget->mSoundFile, TQT_SIGNAL( textChanged( const TQString & ) ), TQT_SLOT( changed() ) ); 00224 connect( mWidget->mApplication, TQT_SIGNAL( textChanged( const TQString & ) ), TQT_SLOT( changed() ) ); 00225 connect( mWidget->mAppArguments, TQT_SIGNAL( textChanged( const TQString & ) ), TQT_SLOT( changed() ) ); 00226 connect( mWidget->mEmailAddress, TQT_SIGNAL( textChanged( const TQString & ) ), TQT_SLOT( changed() ) ); 00227 connect( mWidget->mEmailText, TQT_SIGNAL( textChanged() ), TQT_SLOT( changed() ) ); 00228 00229 init(); 00230 00231 //TODO: backport email reminders from trunk 00232 mWidget->mTypeEmailRadio->hide(); //email reminders not implemented yet 00233 00234 mWidget->setMinimumSize( 500, 500 ); 00235 } 00236 00237 KOEditorAlarms::~KOEditorAlarms() 00238 { 00239 } 00240 00241 void KOEditorAlarms::changed() 00242 { 00243 if ( !mInitializing && mCurrentItem ) { 00244 KCal::Alarm *alarm = mCurrentItem->alarm(); 00245 00246 // Based on settings, provide default sound file for audio alarms 00247 if ( alarm->audioFile().isEmpty() && 00248 KOPrefs::instance()->defaultAudioFileReminders() ) { 00249 alarm->setAudioFile( KOPrefs::instance()->audioFilePath() ); 00250 mWidget->mSoundFile->setURL( KOPrefs::instance()->audioFilePath() ); 00251 } 00252 00253 writeAlarm( alarm ); 00254 mCurrentItem->construct(); 00255 } 00256 } 00257 00258 void KOEditorAlarms::readAlarm( KCal::Alarm *alarm ) 00259 { 00260 if ( !alarm ) return; 00261 00262 mInitializing = true; 00263 00264 // Offsets 00265 int offset; 00266 int beforeafterpos = 0; 00267 if ( mType == "Todo" ) { 00268 if ( !alarm->hasStartOffset() ) { 00269 beforeafterpos = 2; 00270 } 00271 } 00272 if ( alarm->hasEndOffset() ) { 00273 beforeafterpos = 2; 00274 offset = alarm->endOffset().asSeconds(); 00275 } else { 00276 // TODO: Also allow alarms at fixed times, not relative to start/end 00277 offset = alarm->startOffset().asSeconds(); 00278 } 00279 // Negative offset means before the start/end... 00280 if ( offset <= 0 ) { 00281 offset = -offset; 00282 } else { 00283 ++beforeafterpos; 00284 } 00285 mWidget->mBeforeAfter->setCurrentItem( beforeafterpos ); 00286 00287 offset = offset / 60; // make minutes 00288 int useoffset = offset; 00289 00290 if ( offset % (24*60) == 0 && offset>0 ) { // divides evenly into days? 00291 useoffset = offset / (24*60); 00292 mWidget->mOffsetUnit->setCurrentItem( 2 ); 00293 } else if (offset % 60 == 0 && offset>0 ) { // divides evenly into hours? 00294 useoffset = offset / 60; 00295 mWidget->mOffsetUnit->setCurrentItem( 1 ); 00296 } else { 00297 useoffset = offset; 00298 mWidget->mOffsetUnit->setCurrentItem( 0 ); 00299 } 00300 mWidget->mAlarmOffset->setValue( useoffset ); 00301 00302 00303 // Repeating 00304 mWidget->mRepeats->setChecked( alarm->repeatCount()>0 ); 00305 if ( alarm->repeatCount()>0 ) { 00306 mWidget->mRepeatCount->setValue( alarm->repeatCount() ); 00307 mWidget->mRepeatInterval->setValue( alarm->snoozeTime().asSeconds() / 60 ); // show as minutes 00308 } 00309 00310 switch ( alarm->type() ) { 00311 case KCal::Alarm::Audio: 00312 mWidget->mAlarmType->setButton( 1 ); 00313 mWidget->mSoundFile->setURL( alarm->audioFile() ); 00314 break; 00315 case KCal::Alarm::Procedure: 00316 mWidget->mAlarmType->setButton( 2 ); 00317 mWidget->mApplication->setURL( alarm->programFile() ); 00318 mWidget->mAppArguments->setText( alarm->programArguments() ); 00319 break; 00320 case KCal::Alarm::Email: { 00321 mWidget->mAlarmType->setButton( 3 ); 00322 TQValueList<KCal::Person> addresses = alarm->mailAddresses(); 00323 TQStringList add; 00324 for ( TQValueList<KCal::Person>::ConstIterator it = addresses.begin(); 00325 it != addresses.end(); ++it ) { 00326 add << (*it).fullName(); 00327 } 00328 mWidget->mEmailAddress->setText( add.join(", ") ); 00329 mWidget->mEmailText->setText( alarm->mailText() ); 00330 break;} 00331 case KCal::Alarm::Display: 00332 case KCal::Alarm::Invalid: 00333 default: 00334 mWidget->mAlarmType->setButton( 0 ); 00335 mWidget->mDisplayText->setText( alarm->text() ); 00336 break; 00337 } 00338 00339 mWidget->mTypeStack->raiseWidget( mWidget->mAlarmType->selectedId() ); 00340 00341 mInitializing = false; 00342 } 00343 00344 void KOEditorAlarms::writeAlarm( KCal::Alarm *alarm ) 00345 { 00346 // Offsets 00347 int offset = mWidget->mAlarmOffset->value()*60; // minutes 00348 int offsetunit = mWidget->mOffsetUnit->currentItem(); 00349 if ( offsetunit >= 1 ) offset *= 60; // hours 00350 if ( offsetunit >= 2 ) offset *= 24; // days 00351 if ( offsetunit >= 3 ) offset *= 7; // weeks 00352 00353 int beforeafterpos = mWidget->mBeforeAfter->currentItem(); 00354 if ( beforeafterpos % 2 == 0 ) { // before -> negative 00355 offset = -offset; 00356 } 00357 00358 // TODO: Add possibility to specify a given time for the reminder 00359 if ( beforeafterpos / 2 == 0 ) { // start offset 00360 alarm->setStartOffset( KCal::Duration( offset ) ); 00361 } else { 00362 alarm->setEndOffset( KCal::Duration( offset ) ); 00363 } 00364 00365 // Repeating 00366 if ( mWidget->mRepeats->isChecked() ) { 00367 alarm->setRepeatCount( mWidget->mRepeatCount->value() ); 00368 alarm->setSnoozeTime( KCal::Duration( mWidget->mRepeatInterval->value() * 60 ) ); // convert back to seconds 00369 } else { 00370 alarm->setRepeatCount( 0 ); 00371 } 00372 00373 switch ( mWidget->mAlarmType->selectedId() ) { 00374 case 1: // Audio 00375 alarm->setAudioAlarm( mWidget->mSoundFile->url() ); 00376 break; 00377 case 2: // Procedure 00378 alarm->setProcedureAlarm( mWidget->mApplication->url(), mWidget->mAppArguments->text() ); 00379 break; 00380 case 3: { // Email 00381 TQStringList addresses = KPIM::splitEmailAddrList( mWidget->mEmailAddress->text() ); 00382 TQValueList<KCal::Person> add; 00383 for ( TQStringList::Iterator it = addresses.begin(); it != addresses.end(); 00384 ++it ) { 00385 add << KCal::Person( *it ); 00386 } 00387 // TODO: Add a subject line and possibilities for attachments 00388 alarm->setEmailAlarm( TQString(), mWidget->mEmailText->text(), 00389 add ); 00390 break; } 00391 case 0: // Display 00392 default: 00393 alarm->setDisplayAlarm( mWidget->mDisplayText->text() ); 00394 break; 00395 } 00396 } 00397 00398 void KOEditorAlarms::selectionChanged( TQListViewItem *listviewitem ) 00399 { 00400 AlarmListViewItem *item = dynamic_cast<AlarmListViewItem*>(listviewitem); 00401 mCurrentItem = item; 00402 mWidget->mTimeGroup->setEnabled( item ); 00403 mWidget->mTypeGroup->setEnabled( item ); 00404 if ( item ) { 00405 readAlarm( item->alarm() ); 00406 } 00407 } 00408 00409 void KOEditorAlarms::slotOk() 00410 { 00411 // save the current item settings, if any 00412 changed(); 00413 00414 // copy the mAlarms list 00415 if ( mAlarms ) { 00416 mAlarms->clear(); 00417 TQListViewItemIterator it( mWidget->mAlarmList ); 00418 while ( it.current() ) { 00419 AlarmListViewItem *item = dynamic_cast<AlarmListViewItem*>(*it); 00420 if ( item ) { 00421 mAlarms->append( new KCal::Alarm( *(item->alarm()) ) ); 00422 } 00423 ++it; 00424 } 00425 } 00426 accept(); 00427 } 00428 00429 void KOEditorAlarms::slotAdd() 00430 { 00431 mCurrentItem = new AlarmListViewItem( mWidget->mAlarmList, 0, mType ); 00432 mWidget->mAlarmList->setCurrentItem( mCurrentItem ); 00433 } 00434 00435 void KOEditorAlarms::slotDuplicate() 00436 { 00437 if ( mCurrentItem ) { 00438 mCurrentItem = new AlarmListViewItem( mWidget->mAlarmList, mCurrentItem->alarm(), mType ); 00439 mWidget->mAlarmList->setCurrentItem( mCurrentItem ); 00440 } 00441 } 00442 00443 void KOEditorAlarms::slotRemove() 00444 { 00445 if ( mCurrentItem ) { 00446 delete mCurrentItem; 00447 mCurrentItem = dynamic_cast<AlarmListViewItem*>( mWidget->mAlarmList->currentItem() ); 00448 mWidget->mAlarmList->setSelected( mCurrentItem, true ); 00449 } 00450 } 00451 00452 void KOEditorAlarms::init() 00453 { 00454 mInitializing = true; 00455 00456 // Tweak some UI stuff depending on the Incidence type 00457 if ( mType == "Todo" ) { 00458 // Replace before/after end datetime with before/after due datetime 00459 mWidget->mBeforeAfter->clear(); 00460 mWidget->mBeforeAfter->insertItem( i18n( "before the to-do starts" ), 0 ); 00461 mWidget->mBeforeAfter->insertItem( i18n( "after the to-do starts" ), 1 ); 00462 mWidget->mBeforeAfter->insertItem( i18n( "before the to-do is due" ), 2 ); 00463 mWidget->mBeforeAfter->insertItem( i18n( "after the to-do is due" ), 3 ); 00464 TQToolTip::add( 00465 mWidget->mBeforeAfter, 00466 i18n( "Select the reminder trigger relative to the start or due time" ) ); 00467 TQWhatsThis::add( 00468 mWidget->mBeforeAfter, 00469 i18n( "Use this combobox to specify if you want the reminder to " 00470 "trigger before or after the start or due time." ) ); 00471 00472 mWidget->mBeforeAfter->setCurrentItem( 2 ); // default is before due start 00473 } 00474 00475 // Fill-in existing alarms 00476 KCal::Alarm::List::ConstIterator it; 00477 for ( it = mAlarms->begin(); it != mAlarms->end(); ++it ) { 00478 new AlarmListViewItem( mWidget->mAlarmList, *it, mType ); 00479 } 00480 mWidget->mAlarmList->setSelected( mWidget->mAlarmList->firstChild(), true ); 00481 mInitializing = false; 00482 } 00483 00484 #include "koeditoralarms.moc"