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
54class 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
70AlarmListViewItem::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
101AlarmListViewItem::~AlarmListViewItem()
102{
103 delete mAlarm;
104}
105
106void 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
190KOEditorAlarms::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
237KOEditorAlarms::~KOEditorAlarms()
238{
239}
240
241void 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
258void 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
344void 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
398void 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
409void 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
429void KOEditorAlarms::slotAdd()
430{
431 mCurrentItem = new AlarmListViewItem( mWidget->mAlarmList, 0, mType );
432 mWidget->mAlarmList->setCurrentItem( mCurrentItem );
433}
434
435void KOEditorAlarms::slotDuplicate()
436{
437 if ( mCurrentItem ) {
438 mCurrentItem = new AlarmListViewItem( mWidget->mAlarmList, mCurrentItem->alarm(), mType );
439 mWidget->mAlarmList->setCurrentItem( mCurrentItem );
440 }
441}
442
443void 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
452void 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