korganizer

koeditoralarms.cpp
1 /*
2  This file is part of KOrganizer.
3 
4  Copyright (c) 2003 Cornelius Schumacher <schumacher@kde.org>
5  Copyright (C) 2004 Reinhold Kainhofer <reinhold@kainhofer.com>
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 
21  As a special exception, permission is given to link this program
22  with any edition of TQt, and distribute the resulting executable,
23  without including the source code for TQt in the source distribution.
24 */
25 
26 #include "koeditoralarms_base.h"
27 #include "koeditoralarms.h"
28 #include "koprefs.h"
29 
30 #include <libkcal/duration.h>
31 
32 #include <tqlayout.h>
33 #include <tqlistview.h>
34 #include <tqpushbutton.h>
35 #include <tqspinbox.h>
36 #include <tqcombobox.h>
37 #include <tqcheckbox.h>
38 #include <tqbuttongroup.h>
39 #include <tqtextedit.h>
40 #include <tqwidgetstack.h>
41 #include <tqradiobutton.h>
42 #include <tqtooltip.h>
43 #include <tqwhatsthis.h>
44 
45 #include <kurlrequester.h>
46 #include <tdelocale.h>
47 #include <kdebug.h>
48 
49 #include <libkcal/alarm.h>
50 #include <libkcal/incidence.h>
51 
52 #include <libemailfunctions/email.h>
53 
54 class AlarmListViewItem : public TQListViewItem
55 {
56  public:
57  AlarmListViewItem( TQListView *parent, KCal::Alarm *alarm, const TQCString &inctype );
58  virtual ~AlarmListViewItem();
59  KCal::Alarm *alarm() const { return mAlarm; }
60  void construct();
61  enum AlarmViewColumns { ColAlarmType=0, ColAlarmOffset, ColAlarmRepeat };
62 
63  protected:
64  KCal::Alarm *mAlarm;
65 
66  private:
67  TQCString mIncType;
68 };
69 
70 AlarmListViewItem::AlarmListViewItem( TQListView *parent, KCal::Alarm *alarm,
71  const TQCString &inctype )
72  : TQListViewItem( parent ), mIncType( inctype )
73 {
74  if ( alarm ) {
75  mAlarm = new KCal::Alarm( *alarm );
76  } else {
77  mAlarm = new KCal::Alarm( 0 );
78  mAlarm->setType( KCal::Alarm::Display );
79  int duration; // in secs
80  switch( KOPrefs::instance()->mReminderTimeUnits ) {
81  default:
82  case 0: // mins
83  duration = KOPrefs::instance()->mReminderTime * 60;
84  break;
85  case 1: // hours
86  duration = KOPrefs::instance()->mReminderTime * 60 * 60;
87  break;
88  case 2: // days
89  duration = KOPrefs::instance()->mReminderTime * 60 * 60 * 24;
90  break;
91  }
92  if ( mIncType == "Event" ) {
93  mAlarm->setStartOffset( KCal::Duration( -duration ) );
94  } else {
95  mAlarm->setEndOffset( KCal::Duration( -duration ) );
96  }
97  }
98  construct();
99 }
100 
101 AlarmListViewItem::~AlarmListViewItem()
102 {
103  delete mAlarm;
104 }
105 
106 void AlarmListViewItem::construct()
107 {
108  if ( mAlarm ) {
109  // Alarm type:
110  TQString type;
111  switch ( mAlarm->type() ) {
112  case KCal::Alarm::Display:
113  type = i18n("Reminder Dialog");
114  break;
115  case KCal::Alarm::Procedure:
116  type = i18n("Program");
117  break;
118  case KCal::Alarm::Email:
119  type = i18n("Email");
120  break;
121  case KCal::Alarm::Audio:
122  type = i18n("Audio");
123  break;
124  default:
125  type = i18n("Unknown");
126  break;
127  }
128  setText( ColAlarmType, type );
129 
130  // Alarm offset:
131  TQString offsetstr;
132  int offset = 0;
133  if ( mAlarm->hasStartOffset() ) {
134  offset = mAlarm->startOffset().asSeconds();
135  if ( offset <= 0 ) {
136  offsetstr = i18n( "N days/hours/minutes before/after the start/end",
137  "%1 before the start" );
138  offset = -offset;
139  } else {
140  offsetstr = i18n( "N days/hours/minutes before/after the start/end",
141  "%1 after the start" );
142  }
143  } else if ( mAlarm->hasEndOffset() ) {
144  offset = mAlarm->endOffset().asSeconds();
145  if ( offset <= 0 ) {
146  if ( mIncType == "Todo" ) {
147  offsetstr = i18n( "N days/hours/minutes before/after the due date",
148  "%1 before the to-do is due" );
149  } else {
150  offsetstr = i18n( "N days/hours/minutes before/after the start/end",
151  "%1 before the end" );
152  }
153  offset = -offset;
154  } else {
155  if ( mIncType == "Todo" ) {
156  offsetstr = i18n( "N days/hours/minutes before/after the due date",
157  "%1 after the to-do is due" );
158  } else {
159  offsetstr = i18n( "N days/hours/minutes before/after the start/end",
160  "%1 after the end" );
161  }
162  }
163  }
164 
165  offset = offset / 60; // make minutes
166  int useoffset = offset;
167 
168  if ( offset % (24*60) == 0 && offset>0 ) { // divides evenly into days?
169  useoffset = offset / (24*60);
170  offsetstr = offsetstr.arg( i18n("1 day", "%n days", useoffset ) );
171  } else if (offset % 60 == 0 && offset>0 ) { // divides evenly into hours?
172  useoffset = offset / 60;
173  offsetstr = offsetstr.arg( i18n("1 hour", "%n hours", useoffset ) );
174  } else {
175  useoffset = offset;
176  offsetstr = offsetstr.arg( i18n("1 minute", "%n minutes", useoffset ) );
177  }
178  setText( ColAlarmOffset, offsetstr );
179 
180  // Alarm repeat
181  if ( mAlarm->repeatCount()>0 ) {
182  setText( ColAlarmRepeat, i18n("Yes") );
183  } else {
184  setText( ColAlarmRepeat, i18n("No") );
185  }
186  }
187 }
188 
189 
190 KOEditorAlarms::KOEditorAlarms( const TQCString &type,
191  KCal::Alarm::List *alarms, TQWidget *parent,
192  const char *name )
193  : KDialogBase( parent, name, true, i18n("Advanced Reminders"), Ok | Cancel ),
194  mType( type ), mAlarms( alarms ),mCurrentItem( 0 )
195 {
196  if ( mType != "Todo" ) {
197  // only Todos and Events can have reminders
198  mType = "Event";
199  }
200  setMainWidget( mWidget = new KOEditorAlarms_base( this ) );
201 
202  // The text is set here, and not in the UI file, because the i18n context is not
203  // properly extracted from the UI file.
204  mWidget->mAddButton->setText( i18n( "Add a new alarm to the alarm list.", "&Add" ) );
205 
206  mWidget->mAlarmList->setResizeMode( TQListView::LastColumn );
207  mWidget->mAlarmList->setColumnWidthMode( 0, TQListView::Maximum );
208  mWidget->mAlarmList->setColumnWidthMode( 1, TQListView::Maximum );
209  connect( mWidget->mAlarmList, TQ_SIGNAL( selectionChanged( TQListViewItem * ) ),
210  TQ_SLOT( selectionChanged( TQListViewItem * ) ) );
211  connect( mWidget->mAddButton, TQ_SIGNAL( clicked() ), TQ_SLOT( slotAdd() ) );
212  connect( mWidget->mRemoveButton, TQ_SIGNAL( clicked() ), TQ_SLOT( slotRemove() ) );
213  connect( mWidget->mDuplicateButton, TQ_SIGNAL( clicked() ), TQ_SLOT( slotDuplicate() ) );
214 
215  connect( mWidget->mAlarmOffset, TQ_SIGNAL( valueChanged( int ) ), TQ_SLOT( changed() ) );
216  connect( mWidget->mOffsetUnit, TQ_SIGNAL( activated( int ) ), TQ_SLOT( changed() ) );
217  connect( mWidget->mBeforeAfter, TQ_SIGNAL( activated( int ) ), TQ_SLOT( changed() ) );
218  connect( mWidget->mRepeats, TQ_SIGNAL( toggled( bool ) ), TQ_SLOT( changed() ) );
219  connect( mWidget->mRepeatCount, TQ_SIGNAL( valueChanged( int ) ), TQ_SLOT( changed() ) );
220  connect( mWidget->mRepeatInterval, TQ_SIGNAL( valueChanged( int ) ), TQ_SLOT( changed() ) );
221  connect( mWidget->mAlarmType, TQ_SIGNAL(clicked(int)), TQ_SLOT( changed() ) );
222  connect( mWidget->mDisplayText, TQ_SIGNAL( textChanged() ), TQ_SLOT( changed() ) );
223  connect( mWidget->mSoundFile, TQ_SIGNAL( textChanged( const TQString & ) ), TQ_SLOT( changed() ) );
224  connect( mWidget->mApplication, TQ_SIGNAL( textChanged( const TQString & ) ), TQ_SLOT( changed() ) );
225  connect( mWidget->mAppArguments, TQ_SIGNAL( textChanged( const TQString & ) ), TQ_SLOT( changed() ) );
226  connect( mWidget->mEmailAddress, TQ_SIGNAL( textChanged( const TQString & ) ), TQ_SLOT( changed() ) );
227  connect( mWidget->mEmailText, TQ_SIGNAL( textChanged() ), TQ_SLOT( changed() ) );
228 
229  init();
230 
231  //TODO: backport email reminders from trunk
232  mWidget->mTypeEmailRadio->hide(); //email reminders not implemented yet
233 
234  mWidget->setMinimumSize( 500, 500 );
235 }
236 
237 KOEditorAlarms::~KOEditorAlarms()
238 {
239 }
240 
241 void KOEditorAlarms::changed()
242 {
243  if ( !mInitializing && mCurrentItem ) {
244  KCal::Alarm *alarm = mCurrentItem->alarm();
245 
246  // Based on settings, provide default sound file for audio alarms
247  if ( alarm->audioFile().isEmpty() &&
248  KOPrefs::instance()->defaultAudioFileReminders() ) {
249  alarm->setAudioFile( KOPrefs::instance()->audioFilePath() );
250  mWidget->mSoundFile->setURL( KOPrefs::instance()->audioFilePath() );
251  }
252 
253  writeAlarm( alarm );
254  mCurrentItem->construct();
255  }
256 }
257 
258 void KOEditorAlarms::readAlarm( KCal::Alarm *alarm )
259 {
260  if ( !alarm ) return;
261 
262  mInitializing = true;
263 
264  // Offsets
265  int offset;
266  int beforeafterpos = 0;
267  if ( mType == "Todo" ) {
268  if ( !alarm->hasStartOffset() ) {
269  beforeafterpos = 2;
270  }
271  }
272  if ( alarm->hasEndOffset() ) {
273  beforeafterpos = 2;
274  offset = alarm->endOffset().asSeconds();
275  } else {
276  // TODO: Also allow alarms at fixed times, not relative to start/end
277  offset = alarm->startOffset().asSeconds();
278  }
279  // Negative offset means before the start/end...
280  if ( offset <= 0 ) {
281  offset = -offset;
282  } else {
283  ++beforeafterpos;
284  }
285  mWidget->mBeforeAfter->setCurrentItem( beforeafterpos );
286 
287  offset = offset / 60; // make minutes
288  int useoffset = offset;
289 
290  if ( offset % (24*60) == 0 && offset>0 ) { // divides evenly into days?
291  useoffset = offset / (24*60);
292  mWidget->mOffsetUnit->setCurrentItem( 2 );
293  } else if (offset % 60 == 0 && offset>0 ) { // divides evenly into hours?
294  useoffset = offset / 60;
295  mWidget->mOffsetUnit->setCurrentItem( 1 );
296  } else {
297  useoffset = offset;
298  mWidget->mOffsetUnit->setCurrentItem( 0 );
299  }
300  mWidget->mAlarmOffset->setValue( useoffset );
301 
302 
303  // Repeating
304  mWidget->mRepeats->setChecked( alarm->repeatCount()>0 );
305  if ( alarm->repeatCount()>0 ) {
306  mWidget->mRepeatCount->setValue( alarm->repeatCount() );
307  mWidget->mRepeatInterval->setValue( alarm->snoozeTime().asSeconds() / 60 ); // show as minutes
308  }
309 
310  switch ( alarm->type() ) {
311  case KCal::Alarm::Audio:
312  mWidget->mAlarmType->setButton( 1 );
313  mWidget->mSoundFile->setURL( alarm->audioFile() );
314  break;
315  case KCal::Alarm::Procedure:
316  mWidget->mAlarmType->setButton( 2 );
317  mWidget->mApplication->setURL( alarm->programFile() );
318  mWidget->mAppArguments->setText( alarm->programArguments() );
319  break;
320  case KCal::Alarm::Email: {
321  mWidget->mAlarmType->setButton( 3 );
322  TQValueList<KCal::Person> addresses = alarm->mailAddresses();
323  TQStringList add;
324  for ( TQValueList<KCal::Person>::ConstIterator it = addresses.begin();
325  it != addresses.end(); ++it ) {
326  add << (*it).fullName();
327  }
328  mWidget->mEmailAddress->setText( add.join(", ") );
329  mWidget->mEmailText->setText( alarm->mailText() );
330  break;}
331  case KCal::Alarm::Display:
332  case KCal::Alarm::Invalid:
333  default:
334  mWidget->mAlarmType->setButton( 0 );
335  mWidget->mDisplayText->setText( alarm->text() );
336  break;
337  }
338 
339  mWidget->mTypeStack->raiseWidget( mWidget->mAlarmType->selectedId() );
340 
341  mInitializing = false;
342 }
343 
344 void KOEditorAlarms::writeAlarm( KCal::Alarm *alarm )
345 {
346  // Offsets
347  int offset = mWidget->mAlarmOffset->value()*60; // minutes
348  int offsetunit = mWidget->mOffsetUnit->currentItem();
349  if ( offsetunit >= 1 ) offset *= 60; // hours
350  if ( offsetunit >= 2 ) offset *= 24; // days
351  if ( offsetunit >= 3 ) offset *= 7; // weeks
352 
353  int beforeafterpos = mWidget->mBeforeAfter->currentItem();
354  if ( beforeafterpos % 2 == 0 ) { // before -> negative
355  offset = -offset;
356  }
357 
358  // TODO: Add possibility to specify a given time for the reminder
359  if ( beforeafterpos / 2 == 0 ) { // start offset
360  alarm->setStartOffset( KCal::Duration( offset ) );
361  } else {
362  alarm->setEndOffset( KCal::Duration( offset ) );
363  }
364 
365  // Repeating
366  if ( mWidget->mRepeats->isChecked() ) {
367  alarm->setRepeatCount( mWidget->mRepeatCount->value() );
368  alarm->setSnoozeTime( KCal::Duration( mWidget->mRepeatInterval->value() * 60 ) ); // convert back to seconds
369  } else {
370  alarm->setRepeatCount( 0 );
371  }
372 
373  switch ( mWidget->mAlarmType->selectedId() ) {
374  case 1: // Audio
375  alarm->setAudioAlarm( mWidget->mSoundFile->url() );
376  break;
377  case 2: // Procedure
378  alarm->setProcedureAlarm( mWidget->mApplication->url(), mWidget->mAppArguments->text() );
379  break;
380  case 3: { // Email
381  TQStringList addresses = KPIM::splitEmailAddrList( mWidget->mEmailAddress->text() );
382  TQValueList<KCal::Person> add;
383  for ( TQStringList::Iterator it = addresses.begin(); it != addresses.end();
384  ++it ) {
385  add << KCal::Person( *it );
386  }
387  // TODO: Add a subject line and possibilities for attachments
388  alarm->setEmailAlarm( TQString(), mWidget->mEmailText->text(),
389  add );
390  break; }
391  case 0: // Display
392  default:
393  alarm->setDisplayAlarm( mWidget->mDisplayText->text() );
394  break;
395  }
396 }
397 
398 void KOEditorAlarms::selectionChanged( TQListViewItem *listviewitem )
399 {
400  AlarmListViewItem *item = dynamic_cast<AlarmListViewItem*>(listviewitem);
401  mCurrentItem = item;
402  mWidget->mTimeGroup->setEnabled( item );
403  mWidget->mTypeGroup->setEnabled( item );
404  if ( item ) {
405  readAlarm( item->alarm() );
406  }
407 }
408 
409 void KOEditorAlarms::slotOk()
410 {
411  // save the current item settings, if any
412  changed();
413 
414  // copy the mAlarms list
415  if ( mAlarms ) {
416  mAlarms->clear();
417  TQListViewItemIterator it( mWidget->mAlarmList );
418  while ( it.current() ) {
419  AlarmListViewItem *item = dynamic_cast<AlarmListViewItem*>(*it);
420  if ( item ) {
421  mAlarms->append( new KCal::Alarm( *(item->alarm()) ) );
422  }
423  ++it;
424  }
425  }
426  accept();
427 }
428 
429 void KOEditorAlarms::slotAdd()
430 {
431  mCurrentItem = new AlarmListViewItem( mWidget->mAlarmList, 0, mType );
432  mWidget->mAlarmList->setCurrentItem( mCurrentItem );
433 }
434 
435 void KOEditorAlarms::slotDuplicate()
436 {
437  if ( mCurrentItem ) {
438  mCurrentItem = new AlarmListViewItem( mWidget->mAlarmList, mCurrentItem->alarm(), mType );
439  mWidget->mAlarmList->setCurrentItem( mCurrentItem );
440  }
441 }
442 
443 void KOEditorAlarms::slotRemove()
444 {
445  if ( mCurrentItem ) {
446  delete mCurrentItem;
447  mCurrentItem = dynamic_cast<AlarmListViewItem*>( mWidget->mAlarmList->currentItem() );
448  mWidget->mAlarmList->setSelected( mCurrentItem, true );
449  }
450 }
451 
452 void KOEditorAlarms::init()
453 {
454  mInitializing = true;
455 
456  // Tweak some UI stuff depending on the Incidence type
457  if ( mType == "Todo" ) {
458  // Replace before/after end datetime with before/after due datetime
459  mWidget->mBeforeAfter->clear();
460  mWidget->mBeforeAfter->insertItem( i18n( "before the to-do starts" ), 0 );
461  mWidget->mBeforeAfter->insertItem( i18n( "after the to-do starts" ), 1 );
462  mWidget->mBeforeAfter->insertItem( i18n( "before the to-do is due" ), 2 );
463  mWidget->mBeforeAfter->insertItem( i18n( "after the to-do is due" ), 3 );
464  TQToolTip::add(
465  mWidget->mBeforeAfter,
466  i18n( "Select the reminder trigger relative to the start or due time" ) );
467  TQWhatsThis::add(
468  mWidget->mBeforeAfter,
469  i18n( "Use this combobox to specify if you want the reminder to "
470  "trigger before or after the start or due time." ) );
471 
472  mWidget->mBeforeAfter->setCurrentItem( 2 ); // default is before due start
473  }
474 
475  // Fill-in existing alarms
476  KCal::Alarm::List::ConstIterator it;
477  for ( it = mAlarms->begin(); it != mAlarms->end(); ++it ) {
478  new AlarmListViewItem( mWidget->mAlarmList, *it, mType );
479  }
480  mWidget->mAlarmList->setSelected( mWidget->mAlarmList->firstChild(), true );
481  mInitializing = false;
482 }
483 
484 #include "koeditoralarms.moc"
void setAudioAlarm(const TQString &audioFile=TQString())
bool hasStartOffset() const
TQString audioFile() const
Duration snoozeTime() const
TQString programFile() const
void setRepeatCount(int alarmRepeatCount)
TQString text() const
Duration endOffset() const
bool hasEndOffset() const
void setDisplayAlarm(const TQString &text=TQString())
Duration startOffset() const
void setSnoozeTime(const Duration &alarmSnoozeTime)
TQString programArguments() const
void setEndOffset(const Duration &)
void setEmailAlarm(const TQString &subject, const TQString &text, const TQValueList< Person > &addressees, const TQStringList &attachments=TQStringList())
TQString mailText() const
void setStartOffset(const Duration &)
TQValueList< Person > mailAddresses() const
Type type() const
void setAudioFile(const TQString &audioFile)
void setProcedureAlarm(const TQString &programFile, const TQString &arguments=TQString())
int repeatCount() const
int asSeconds() const