SVN_SILENT made messages (.desktop file)
[kdepim.git] / korgac / alarmdialog.cpp
blob6bbd867c987abe40e2de0e40c5c39a7af018c9b8
1 /*
2 This file is part of the KDE reminder agent.
4 Copyright (c) 2000,2003 Cornelius Schumacher <schumacher@kde.org>
5 Copyright (c) 2008-2009 Allen Winter <winter@kde.org>
6 Copyright (c) 2009-2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.net>
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
18 You should have received a copy of the GNU General Public License along
19 with this program; if not, write to the Free Software Foundation, Inc.,
20 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 As a special exception, permission is given to link this program
23 with any edition of Qt, and distribute the resulting executable,
24 without including the source code for Qt in the source distribution.
27 #include "alarmdialog.h"
28 #include "korganizer_interface.h"
29 #include "mailclient.h"
31 #include <calendarsupport/next/incidenceviewer.h>
32 #include <calendarsupport/kcalprefs.h>
33 #include <calendarsupport/identitymanager.h>
34 #include <calendarsupport/utils.h>
36 #include <incidenceeditor-ng/incidencedialog.h>
37 #include <incidenceeditor-ng/incidencedialogfactory.h>
39 #include <KCalCore/Event>
40 #include <KCalCore/Todo>
41 #include <KCalUtils/IncidenceFormatter>
43 #include <KPIMIdentities/Identity>
44 #include <KPIMIdentities/IdentityManager>
46 #include <Akonadi/Item>
48 #include <Mailtransport/TransportManager>
50 #include <KComboBox>
51 #include <KDebug>
52 #include <KHBox>
53 #include <KLocale>
54 #include <KMessageBox>
55 #include <KNotification>
56 #include <KSharedConfig>
57 #include <KSystemTimeZone>
58 #include <KToolInvocation>
59 #include <KWindowSystem>
61 #include <phonon/mediaobject.h>
62 #include <QLabel>
63 #include <QKeyEvent>
64 #include <QSpinBox>
65 #include <QTreeWidget>
66 #include <QVBoxLayout>
68 using namespace KPIMIdentities;
69 using namespace KCalCore;
70 using namespace KCalUtils;
72 static int defSuspendVal = 5;
73 static int defSuspendUnit = 0; // 0=>minutes, 1=>hours, 2=>days, 3=>weeks
75 class ReminderTree : public QTreeWidget
77 public:
78 ReminderTree( QWidget *parent ) : QTreeWidget( parent )
82 /*reimp*/
83 void mouseReleaseEvent( QMouseEvent *event );
86 void ReminderTree::mouseReleaseEvent( QMouseEvent *event )
88 QTreeWidgetItem *item = itemAt( event->pos() );
89 if ( item ) {
90 if ( event->button() == Qt::LeftButton ) {
91 emit itemActivated( item, 0 );
92 } else if ( event->button() == Qt::RightButton ) {
93 emit customContextMenuRequested( event->pos() );
98 class ReminderTreeItem : public QTreeWidgetItem
100 public:
101 ReminderTreeItem( const Akonadi::Item &incidence, QTreeWidget *parent )
102 : QTreeWidgetItem( parent ), mIncidence( incidence ), mNotified( false )
105 bool operator<( const QTreeWidgetItem & other ) const;
107 QString mDisplayText;
109 const Akonadi::Item mIncidence;
110 QDateTime mRemindAt;
111 KDateTime mTrigger;
112 KDateTime mHappening;
113 bool mNotified;
116 struct ConfItem {
117 QString uid;
118 KUrl akonadiUrl;
119 QDateTime remindAt;
122 bool ReminderTreeItem::operator<( const QTreeWidgetItem &other ) const
124 switch( treeWidget()->sortColumn() ) {
125 case 1: // happening datetime
127 const ReminderTreeItem *item = static_cast<const ReminderTreeItem *>( &other );
128 return item->mHappening.secsTo( mHappening );
130 case 2: // trigger datetime
132 const ReminderTreeItem *item = static_cast<const ReminderTreeItem *>( &other );
133 return item->mTrigger.secsTo( mTrigger );
135 default:
136 return QTreeWidgetItem::operator < ( other );
140 typedef QList<ReminderTreeItem *> ReminderList;
142 AlarmDialog::AlarmDialog( const Akonadi::ETMCalendar::Ptr &calendar, QWidget *parent )
143 : KDialog( parent, Qt::WindowStaysOnTopHint ),
144 mCalendar( calendar ), mSuspendTimer( this )
146 // User1 => Edit...
147 // User2 => Dismiss All
148 // User3 => Dismiss Selected
149 // Ok => Suspend
151 connect( calendar.data(), SIGNAL(calendarChanged()), SLOT(slotCalendarChanged()) );
153 KIconLoader::global()->addAppDir( QLatin1String("korgac") );
155 KSharedConfig::Ptr config = KGlobal::config();
156 KConfigGroup generalConfig( config, "General" );
157 QPoint pos = generalConfig.readEntry( "Position", QPoint( 0, 0 ) );
159 QWidget *topBox = new QWidget( this );
160 if ( !pos.isNull() ) {
161 mPos = pos;
162 topBox->move( mPos );
164 setMainWidget( topBox );
165 setCaption( i18nc( "@title:window", "Reminders" ) );
166 setWindowIcon( KIcon( QLatin1String("korgac") ) );
167 setButtons( Ok | User1 | User2 | User3 );
168 setDefaultButton( NoDefault );
169 setButtonText( User3, i18nc( "@action:button", "Dismiss Reminder" ) );
170 setButtonToolTip( User3, i18nc( "@info:tooltip",
171 "Dismiss the reminders for the selected incidences" ) );
172 setButtonText( User2, i18nc( "@action:button", "Dismiss All" ) );
173 setButtonToolTip( User2, i18nc( "@info:tooltip",
174 "Dismiss the reminders for all listed incidences" ) );
175 setButtonText( User1, i18nc( "@action:button", "Edit..." ) );
176 setButtonToolTip( User1, i18nc( "@info:tooltip",
177 "Edit the selected incidence" ) );
178 setButtonText( Ok, i18nc( "@action:button", "Suspend" ) );
179 setButtonToolTip( Ok, i18nc( "@info:tooltip",
180 "Suspend the reminders for the selected incidences "
181 "by the specified interval" ) );
183 // Try to keep the dialog small and non-obtrusive.
184 setMinimumWidth( 575 );
185 setMinimumHeight( 300 );
187 QVBoxLayout *mTopLayout = new QVBoxLayout( topBox );
189 QLabel *label = new QLabel(
190 i18nc( "@label",
191 "Reminders: "
192 "Click on a title to toggle the details viewer for that item" ),
193 topBox );
194 mTopLayout->addWidget( label );
196 mIncidenceTree = new ReminderTree( topBox );
197 mIncidenceTree->setColumnCount( 3 );
198 mIncidenceTree->setSortingEnabled( true );
199 const QStringList headerLabels =
200 ( QStringList( i18nc( "@title:column reminder title", "Title" ) )
201 << i18nc( "@title:column happens at date/time", "Date Time" )
202 << i18nc( "@title:column trigger date/time", "Trigger Time" ) );
203 mIncidenceTree->setHeaderLabels( headerLabels );
204 mIncidenceTree->headerItem()->setToolTip(
206 i18nc( "@info:tooltip", "The event or to-do title" ) );
207 mIncidenceTree->headerItem()->setToolTip(
209 i18nc( "@info:tooltip", "The reminder is set for this date/time" ) );
210 mIncidenceTree->headerItem()->setToolTip(
212 i18nc( "@info:tooltip", "The date/time the reminder was triggered" ) );
214 mIncidenceTree->setWordWrap( true );
215 mIncidenceTree->setAllColumnsShowFocus( true );
216 mIncidenceTree->setSelectionMode( QAbstractItemView::ExtendedSelection );
217 mIncidenceTree->setRootIsDecorated( false );
219 mTopLayout->addWidget( mIncidenceTree );
221 connect( mIncidenceTree, SIGNAL(itemActivated(QTreeWidgetItem*,int)),
222 SLOT(update()) );
223 connect( mIncidenceTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
224 SLOT(edit()) );
225 connect( mIncidenceTree, SIGNAL(customContextMenuRequested(QPoint)), SLOT(popupItemMenu(QPoint)) );
227 mDetailView = new CalendarSupport::IncidenceViewer( mCalendar.data(), topBox );
228 QString s;
229 s = i18nc( "@info default incidence details string",
230 "<emphasis>Select an event or to-do from the list above "
231 "to view its details here.</emphasis>" );
232 mDetailView->setDefaultMessage( s );
233 mTopLayout->addWidget( mDetailView );
234 mDetailView->hide();
235 mLastItem = 0;
237 KHBox *suspendBox = new KHBox( topBox );
238 suspendBox->setSpacing( spacingHint() );
239 mTopLayout->addWidget( suspendBox );
241 QLabel *l = new QLabel( i18nc( "@label:spinbox", "Suspend &duration:" ), suspendBox );
243 mSuspendSpin = new QSpinBox( suspendBox );
244 mSuspendSpin->setRange( 1, 9999 );
245 mSuspendSpin->setValue( defSuspendVal ); // default suspend duration
246 mSuspendSpin->setToolTip(
247 i18nc( "@info:tooltip",
248 "Suspend the reminders by this amount of time" ) );
249 mSuspendSpin->setWhatsThis(
250 i18nc( "@info:whatsthis",
251 "Each reminder for the selected incidences will be suspended "
252 "by this number of time units. You can choose the time units "
253 "(typically minutes) in the adjacent selector." ) );
255 l->setBuddy( mSuspendSpin );
257 mSuspendUnit = new KComboBox( suspendBox );
258 mSuspendUnit->addItem( i18nc( "@item:inlistbox suspend in terms of minutes", "minute(s)" ) );
259 mSuspendUnit->addItem( i18nc( "@item:inlistbox suspend in terms of hours", "hour(s)" ) );
260 mSuspendUnit->addItem( i18nc( "@item:inlistbox suspend in terms of days", "day(s)" ) );
261 mSuspendUnit->addItem( i18nc( "@item:inlistbox suspend in terms of weeks", "week(s)" ) );
262 mSuspendUnit->setToolTip(
263 i18nc( "@info:tooltip",
264 "Suspend the reminders using this time unit" ) );
265 mSuspendUnit->setWhatsThis(
266 i18nc( "@info:whatsthis",
267 "Each reminder for the selected incidences will be suspended "
268 "using this time unit. You can set the number of time units "
269 "in the adjacent number entry input." ) );
271 mSuspendUnit->setCurrentIndex( defSuspendUnit );
273 connect( &mSuspendTimer, SIGNAL(timeout()), SLOT(wakeUp()) );
275 connect( this, SIGNAL(okClicked()), this, SLOT(slotOk()) );
276 connect( this, SIGNAL(user1Clicked()), this, SLOT(slotUser1()) );
277 connect( this, SIGNAL(user2Clicked()), this, SLOT(slotUser2()) );
278 connect( this, SIGNAL(user3Clicked()), this, SLOT(slotUser3()) );
280 mIdentityManager = new CalendarSupport::IdentityManager;
283 AlarmDialog::~AlarmDialog()
285 mIncidenceTree->clear();
286 delete mIdentityManager;
289 ReminderTreeItem *AlarmDialog::searchByItem( const Akonadi::Item &incidence )
291 ReminderTreeItem *found = 0;
292 QTreeWidgetItemIterator it( mIncidenceTree );
293 while ( *it ) {
294 ReminderTreeItem *item = static_cast<ReminderTreeItem *>( *it );
295 if ( item->mIncidence == incidence ) {
296 found = item;
297 break;
299 ++it;
301 return found;
304 static QString cleanSummary( const QString &summary )
306 static QString etc = i18nc( "@label an elipsis", "..." );
307 int maxLen = 30;
308 QString retStr = summary;
309 retStr.replace( QLatin1Char('\n'), QLatin1Char(' ') );
310 if ( retStr.length() > maxLen ) {
311 maxLen -= etc.length();
312 retStr = retStr.left( maxLen );
313 retStr += etc;
315 return retStr;
318 void AlarmDialog::addIncidence( const Akonadi::Item &incidenceitem,
319 const QDateTime &reminderAt,
320 const QString &displayText )
322 Incidence::Ptr incidence = CalendarSupport::incidence( incidenceitem );
323 ReminderTreeItem *item = searchByItem( incidenceitem );
324 if ( !item ) {
325 item = new ReminderTreeItem( incidenceitem, mIncidenceTree );
327 item->mNotified = false;
328 item->mHappening = KDateTime();
329 item->mRemindAt = reminderAt;
330 item->mTrigger = KDateTime::currentLocalDateTime();
331 item->mDisplayText = displayText;
332 item->setText( 0, cleanSummary( incidence->summary() ) );
334 Event::Ptr event;
335 Todo::Ptr todo;
337 QString displayStr;
338 const KDateTime dateTime = triggerDateForIncidence( incidence, reminderAt,
339 displayStr );
341 if ( incidence->type() == Incidence::TypeEvent ) {
342 item->setIcon( 0, SmallIcon( QLatin1String("view-calendar-day") ) );
343 } else if ( incidence->type() == Incidence::TypeTodo ) {
344 item->setIcon( 0, SmallIcon( QLatin1String("view-calendar-tasks")) );
347 item->mHappening = dateTime;
348 item->setText( 1, displayStr );
350 item->setText( 2, IncidenceFormatter::dateTimeToString(
351 item->mTrigger, false, true, KDateTime::Spec::LocalZone() ) );
352 QString tip =
353 IncidenceFormatter::toolTipStr(
354 CalendarSupport::displayName( mCalendar.data(), incidenceitem.parentCollection() ),
355 incidence,
356 item->mRemindAt.date(), true,
357 KDateTime::Spec::LocalZone() );
358 if ( !item->mDisplayText.isEmpty() ) {
359 tip += QLatin1String("<br>") + item->mDisplayText;
361 item->setToolTip( 0, tip );
362 item->setToolTip( 1, tip );
363 item->setToolTip( 2, tip );
364 item->setData( 0, QTreeWidgetItem::UserType, false );
366 mIncidenceTree->setCurrentItem( item );
367 showDetails( item );
368 slotSave();
371 void AlarmDialog::slotOk()
373 suspend();
376 void AlarmDialog::slotUser1()
378 edit();
381 void AlarmDialog::slotUser2()
383 dismissAll();
386 void AlarmDialog::slotUser3()
388 dismissCurrent();
391 void AlarmDialog::dismissCurrent()
393 dismiss( selectedItems() );
395 if ( activeCount() == 0 ) {
396 accept();
397 } else {
398 update();
400 emit reminderCount( activeCount() );
403 void AlarmDialog::dismissAll()
405 ReminderList selections;
407 QTreeWidgetItemIterator it( mIncidenceTree );
408 while ( *it ) {
409 if ( !(*it)->isDisabled() ) { //do not disable suspended reminders
410 selections.append( static_cast<ReminderTreeItem *>( *it ) );
412 ++it;
414 dismiss( selections );
416 setTimer();
417 accept();
418 emit reminderCount( activeCount() );
421 void AlarmDialog::dismiss( ReminderList selections )
423 QList<Akonadi::Item::Id> ids;
424 for ( ReminderList::Iterator it = selections.begin(); it != selections.end(); ++it ) {
425 kDebug() << "removing " << CalendarSupport::incidence( (*it)->mIncidence )->summary();
426 if ( mIncidenceTree->itemBelow( *it ) ) {
427 mIncidenceTree->setCurrentItem( mIncidenceTree->itemBelow( *it ) );
428 } else if ( mIncidenceTree->itemAbove( *it ) ) {
429 mIncidenceTree->setCurrentItem( mIncidenceTree->itemAbove( *it ) );
431 mIncidenceTree->removeItemWidget( *it, 0 );
432 ids.append( (*it)->mIncidence.id() );
433 delete *it;
436 removeFromConfig( ids );
439 void AlarmDialog::edit()
441 ReminderList selection = selectedItems();
442 if ( selection.count() != 1 ) {
443 return;
445 Incidence::Ptr incidence = CalendarSupport::incidence( selection.first()->mIncidence );
446 if ( !mCalendar->hasRight( selection.first()->mIncidence, Akonadi::Collection::CanChangeItem ) ) {
447 KMessageBox::sorry(
448 this,
449 i18nc( "@info",
450 "\"%1\" is a read-only item so modifications are not possible.",
451 cleanSummary( incidence->summary() ) ) );
452 return;
455 #if !defined(KDEPIM_MOBILE_UI)
456 openIncidenceEditorNG( selection.first()->mIncidence );
457 #else
458 openIncidenceEditorThroughKOrganizer( incidence );
459 #endif
462 void AlarmDialog::suspend()
464 if ( !isVisible() ) { //do nothing if the dialog is hidden
465 return;
468 int unit = 1;
469 switch ( mSuspendUnit->currentIndex() ) {
470 case 3: // weeks
471 unit *= 7;
472 case 2: // days
473 unit *= 24;
474 case 1: // hours
475 unit *= 60;
476 case 0: // minutes
477 unit *= 60;
478 default:
479 break;
482 ReminderTreeItem *selitem = 0;
483 QTreeWidgetItemIterator it( mIncidenceTree );
484 while ( *it ) {
485 if ( (*it)->isSelected() && !(*it)->isDisabled() ) { //suspend selected, non-suspended reminders
486 (*it)->setSelected( false );
487 (*it)->setDisabled( true );
488 ReminderTreeItem *item = static_cast<ReminderTreeItem *>( *it );
489 item->mRemindAt = QDateTime::currentDateTime().addSecs( unit * mSuspendSpin->value() );
490 item->mHappening = KDateTime( item->mRemindAt, KDateTime::Spec::LocalZone() );
491 item->mNotified = false;
492 (*it)->setText( 1, KGlobal::locale()->formatDateTime( item->mHappening ) );
493 selitem = item;
495 ++it;
498 if ( selitem ) {
499 if ( mIncidenceTree->itemBelow( selitem ) ) {
500 mIncidenceTree->setCurrentItem( mIncidenceTree->itemBelow( selitem ) );
501 } else if ( mIncidenceTree->itemAbove( selitem ) ) {
502 mIncidenceTree->setCurrentItem( mIncidenceTree->itemAbove( selitem ) );
506 // save suspended alarms too so they can be restored on restart
507 // kolab/issue4108
508 slotSave();
510 setTimer();
511 if ( activeCount() == 0 ) {
512 accept();
513 } else {
514 update();
516 emit reminderCount( activeCount() );
519 void AlarmDialog::setTimer()
521 int nextReminderAt = -1;
523 QTreeWidgetItemIterator it( mIncidenceTree );
524 while ( *it ) {
525 ReminderTreeItem *item = static_cast<ReminderTreeItem *>( *it );
526 if ( item->mRemindAt > QDateTime::currentDateTime() ) {
527 const int secs = QDateTime::currentDateTime().secsTo( item->mRemindAt );
528 nextReminderAt = nextReminderAt <= 0 ? secs : qMin( nextReminderAt, secs );
530 ++it;
533 if ( nextReminderAt >= 0 ) {
534 mSuspendTimer.stop();
535 mSuspendTimer.start( 1000 * ( nextReminderAt + 1 ) );
536 mSuspendTimer.setSingleShot( true );
540 void AlarmDialog::show()
542 mIncidenceTree->resizeColumnToContents( 0 );
543 mIncidenceTree->resizeColumnToContents( 1 );
544 mIncidenceTree->resizeColumnToContents( 2 );
545 mIncidenceTree->sortItems( 1, Qt::AscendingOrder );
547 // select the first item that hasn't already been notified
548 QTreeWidgetItemIterator it( mIncidenceTree );
549 while ( *it ) {
550 ReminderTreeItem *item = static_cast<ReminderTreeItem *>( *it );
551 if ( !item->mNotified ) {
552 (*it)->setSelected( true );
553 break;
555 ++it;
558 // reset the default suspend time
559 // Allen: commented-out the following lines on 17 Sept 2013
560 // mSuspendSpin->setValue( defSuspendVal );
561 // mSuspendUnit->setCurrentIndex( defSuspendUnit );
563 KDialog::show();
564 if ( !mPos.isNull() ) {
565 KDialog::move( mPos );
567 KWindowSystem::unminimizeWindow( winId(), false );
568 KWindowSystem::setState( winId(), NET::KeepAbove | NET::DemandsAttention );
569 KWindowSystem::setOnAllDesktops( winId(), true );
570 KWindowSystem::activateWindow( winId() );
572 // Audio, Procedure, and EMail alarms
573 eventNotification();
576 void AlarmDialog::suspendAll()
578 mIncidenceTree->clearSelection();
579 QTreeWidgetItemIterator it( mIncidenceTree );
581 // first, select all non-suspended reminders
582 while ( *it ) {
583 if ( !(*it)->isDisabled() ) { //do not suspend suspended reminders
584 (*it)->setSelected( true );
586 ++it;
589 //suspend all selected reminders
590 suspend();
593 void AlarmDialog::eventNotification()
595 bool beeped = false;
596 bool found = false;
598 ReminderList list;
600 QTreeWidgetItemIterator it( mIncidenceTree );
601 while ( *it ) {
602 ReminderTreeItem *item = static_cast<ReminderTreeItem *>( *it );
603 ++it;
604 if ( item->isDisabled() || item->mNotified ) {
605 //skip suspended reminders or reminders that have been notified
606 continue;
608 found = true;
609 item->mNotified = true;
610 Incidence::Ptr incidence = CalendarSupport::incidence( item->mIncidence );
611 Alarm::List alarms = incidence->alarms();
612 Alarm::List::ConstIterator ait;
613 for ( ait = alarms.constBegin(); ait != alarms.constEnd(); ++ait ) {
614 Alarm::Ptr alarm = *ait;
615 // FIXME: Check whether this should be done for all multiple alarms
616 if ( alarm->type() == Alarm::Procedure ) {
617 // FIXME: Add a message box asking whether the procedure should really be executed
618 kDebug() << "Starting program: '" << alarm->programFile() << "'";
620 QString program = alarm->programFile();
622 // if the program name contains spaces escape it
623 if ( program.contains( QLatin1Char(' ') ) &&
624 !( program.startsWith( QLatin1Char('\"') ) &&
625 program.endsWith( QLatin1Char('\"') ) ) ) {
626 program = QLatin1Char('\"') + program + QLatin1Char('\"');
629 QProcess::startDetached( program + QLatin1Char(' ') + alarm->programArguments() );
630 } else if ( alarm->type() == Alarm::Audio ) {
631 beeped = true;
632 Phonon::MediaObject *player =
633 Phonon::createPlayer( Phonon::NotificationCategory,
634 KUrl( alarm->audioFile() ) );
635 player->setParent( this );
636 connect( player, SIGNAL(finished()), player, SLOT(deleteLater()) );
637 player->play();
638 } else if ( alarm->type() == Alarm::Email ) {
639 QString from = CalendarSupport::KCalPrefs::instance()->email();
640 Identity id = mIdentityManager->identityForAddress( from );
641 QString to;
642 if ( alarm->mailAddresses().isEmpty() ) {
643 to = from;
644 } else {
645 const Person::List addresses = alarm->mailAddresses();
646 QStringList add;
647 for ( Person::List::ConstIterator it = addresses.constBegin();
648 it != addresses.constEnd(); ++it ) {
649 add << (*it)->fullName();
651 to = add.join( QLatin1String(", ") );
654 QString subject;
656 Akonadi::Item parentItem = mCalendar->item( alarm->parentUid() );
657 Incidence::Ptr parent = CalendarSupport::incidence( parentItem );
659 if ( alarm->mailSubject().isEmpty() ) {
660 if ( parent->summary().isEmpty() ) {
661 subject = i18nc( "@title", "Reminder" );
662 } else {
663 subject = i18nc( "@title", "Reminder: %1", cleanSummary( parent->summary() ) );
665 } else {
666 subject = i18nc( "@title", "Reminder: %1", alarm->mailSubject() );
669 QString body =
670 IncidenceFormatter::mailBodyStr(
671 parent.staticCast<IncidenceBase>(), KSystemTimeZones::local() );
672 if ( !alarm->mailText().isEmpty() ) {
673 body += QLatin1Char('\n') + alarm->mailText();
675 //TODO: support attachments
676 KOrg::MailClient mailer;
677 mailer.send( id, from, to, QString(), subject, body, true, false, QString(),
678 MailTransport::TransportManager::self()->defaultTransportName() );
683 if ( !beeped && found ) {
684 KNotification::beep();
688 void AlarmDialog::wakeUp()
690 bool activeReminders = false;
691 QTreeWidgetItemIterator it( mIncidenceTree );
692 QTreeWidgetItem *firstItem = 0;
693 while ( *it ) {
694 if ( !firstItem ) {
695 firstItem = *it;
697 ReminderTreeItem *item = static_cast<ReminderTreeItem *>( *it );
698 Incidence::Ptr incidence = CalendarSupport::incidence( item->mIncidence );
700 if ( item->mRemindAt <= QDateTime::currentDateTime() ) {
701 if ( item->isDisabled() ) { //do not wakeup non-suspended reminders
702 item->setDisabled( false );
703 item->setSelected( false );
705 activeReminders = true;
706 } else {
707 item->setDisabled( true );
710 ++it;
713 if ( activeReminders ) {
714 show();
716 setTimer();
717 showDetails( firstItem );
718 emit reminderCount( activeCount() );
721 void AlarmDialog::slotSave()
723 KSharedConfig::Ptr config = KGlobal::config();
724 KConfigGroup generalConfig( config, "General" );
725 int numReminders = 0;
727 QTreeWidgetItemIterator it( mIncidenceTree );
728 while ( *it ) {
729 ReminderTreeItem *item = static_cast<ReminderTreeItem *>( *it );
730 KConfigGroup incidenceConfig( config,
731 QString::fromLatin1( "Incidence-%1" ).arg( numReminders + 1 ) );
733 Incidence::Ptr incidence = CalendarSupport::incidence( item->mIncidence );
734 incidenceConfig.writeEntry( "AkonadiUrl", item->mIncidence.url() );
735 incidenceConfig.writeEntry( "RemindAt", item->mRemindAt );
736 ++numReminders;
737 ++it;
740 generalConfig.writeEntry( "Reminders", numReminders );
741 generalConfig.writeEntry( "Position", pos() );
742 config->sync();
745 ReminderList AlarmDialog::selectedItems() const
747 ReminderList list;
749 QTreeWidgetItemIterator it( mIncidenceTree );
750 while ( *it ) {
751 if ( (*it)->isSelected() ) {
752 list.append( static_cast<ReminderTreeItem *>( *it ) );
754 ++it;
756 return list;
759 int AlarmDialog::activeCount()
761 int count = 0;
762 QTreeWidgetItemIterator it( mIncidenceTree );
763 while ( *it ) {
764 if ( !(*it)->isDisabled() ) { //suspended reminders are non-active
765 ++count;
767 ++it;
769 kDebug() << "computed " << count << " active reminders";
770 return count;
773 void AlarmDialog::closeEvent( QCloseEvent * )
775 slotSave();
776 accept();
779 void AlarmDialog::updateButtons()
781 const int count = selectedItems().count();
782 kDebug() << "selected items=" << count;
783 enableButton( User3, count > 0 ); // enable Dismiss, if >1 selected
784 enableButton( User1, count == 1 ); // enable Edit, if only 1 selected
785 enableButton( Ok, count > 0 ); // enable Suspend, if >1 selected
788 void AlarmDialog::toggleDetails( QTreeWidgetItem *item )
790 if ( !item ) {
791 return;
794 if ( !mDetailView->isHidden() ) {
795 if ( mLastItem == item ) {
796 resize( size().width(), size().height() - mDetailView->height() - 50 );
797 mDetailView->hide();
798 } else {
799 showDetails( item );
801 } else {
802 resize( size().width(), size().height() + mDetailView->height() + 50 );
803 showDetails( item );
804 mDetailView->show();
806 mLastItem = item;
809 void AlarmDialog::showDetails( QTreeWidgetItem *item )
811 if ( !item ) {
812 return;
815 ReminderTreeItem *reminderItem = dynamic_cast<ReminderTreeItem *>( item );
817 if ( !reminderItem ) {
818 mDetailView->setIncidence( Akonadi::Item() );
819 } else {
820 if ( !reminderItem->mDisplayText.isEmpty() ) {
821 QString txt = QLatin1String("<qt><p><b>") + reminderItem->mDisplayText + QLatin1String("</b></p></qt>");
822 mDetailView->setHeaderText( txt );
823 } else {
824 mDetailView->setHeaderText( QString() );
826 Incidence::Ptr incidence = CalendarSupport::incidence( reminderItem->mIncidence );
827 mDetailView->setIncidence( reminderItem->mIncidence, reminderItem->mRemindAt.date() );
831 void AlarmDialog::update()
833 updateButtons();
834 if ( !mIncidenceTree->selectedItems().isEmpty() ) {
835 QTreeWidgetItem *item = mIncidenceTree->selectedItems().first();
836 toggleDetails( item );
840 void AlarmDialog::popupItemMenu( const QPoint &point )
842 QTreeWidgetItem *item = mIncidenceTree->itemAt( point );
843 if ( !item ) {
844 return;
847 ReminderTreeItem *reminderItem = dynamic_cast<ReminderTreeItem *>( item );
848 if ( reminderItem ) {
849 Incidence::Ptr incidence = CalendarSupport::incidence( reminderItem->mIncidence );
854 void AlarmDialog::accept()
856 if ( activeCount() == 0 ) {
857 mPos = pos();
858 hide();
862 /** static */
863 KDateTime AlarmDialog::triggerDateForIncidence( const Incidence::Ptr &incidence,
864 const QDateTime &reminderAt,
865 QString &displayStr )
867 KDateTime result;
869 if ( incidence->alarms().isEmpty() ) {
870 return result;
873 Alarm::Ptr alarm = incidence->alarms().first();
875 if ( incidence->recurs() ) {
876 result = incidence->recurrence()->getNextDateTime(
877 KDateTime( reminderAt, KDateTime::Spec::LocalZone( ) ) );
879 displayStr = KGlobal::locale()->formatDateTime( result.toLocalZone() );
882 if ( !result.isValid() ) {
883 result = incidence->dateTime( Incidence::RoleAlarm );
884 displayStr = IncidenceFormatter::dateTimeToString( result, false,
885 true,
886 KDateTime::Spec::LocalZone() );
889 return result;
892 void AlarmDialog::slotCalendarChanged()
894 KCalCore::Incidence::List incidences = mCalendar->incidences();
895 Akonadi::Item::List items = mCalendar->itemList( incidences );
896 for ( Akonadi::Item::List::ConstIterator it = items.constBegin();
897 it != items.constEnd(); ++it ) {
898 ReminderTreeItem *item = searchByItem( *it );
900 if ( item ) {
901 Incidence::Ptr incidence = CalendarSupport::incidence( *it );
902 QString displayStr;
904 // Yes, alarms can be empty, if someone edited the incidence and removed all alarms
905 if ( !incidence->alarms().isEmpty() ) {
906 const KDateTime dateTime = triggerDateForIncidence( incidence,
907 item->mRemindAt,
908 displayStr );
910 const QString summary = cleanSummary( incidence->summary() );
912 if ( displayStr != item->text( 1 ) || summary != item->text( 0 ) ) {
913 item->setText( 1, displayStr );
914 item->setText( 0, summary );
921 void AlarmDialog::keyPressEvent( QKeyEvent *e )
923 const int key = e->key() | e->modifiers();
925 if ( key == Qt::Key_Enter || key == Qt::Key_Return ) {
926 e->ignore();
927 return;
930 KDialog::keyPressEvent( e );
933 bool AlarmDialog::openIncidenceEditorThroughKOrganizer( const Incidence::Ptr &incidence )
935 if ( !QDBusConnection::sessionBus().interface()->isServiceRegistered( QLatin1String("org.kde.korganizer") ) ) {
936 if ( KToolInvocation::startServiceByDesktopName( QLatin1String("korganizer"), QString() ) ) {
937 KMessageBox::error(
938 this,
939 i18nc( "@info",
940 "Could not start KOrganizer so editing is not possible." ) );
941 return false;
944 org::kde::korganizer::Korganizer korganizer(
945 QLatin1String("org.kde.korganizer"), QLatin1String("/Korganizer"), QDBusConnection::sessionBus() );
947 kDebug() << "editing incidence " << incidence->summary();
948 if ( !korganizer.editIncidence( incidence->uid() ) ) {
949 KMessageBox::error(
950 this,
951 i18nc( "@info",
952 "An internal KOrganizer error occurred attempting to modify \"%1\"",
953 cleanSummary( incidence->summary() ) ) );
956 // get desktop # where korganizer (or kontact) runs
957 QString object =
958 QDBusConnection::sessionBus().interface()->isServiceRegistered( QLatin1String("org.kde.kontact") ) ?
959 QLatin1String("kontact/MainWindow_1") : QLatin1String("korganizer/MainWindow_1");
960 QDBusInterface korganizerObj( QLatin1String("org.kde.korganizer"), QLatin1Char('/') + object );
961 #ifdef Q_WS_X11
962 QDBusReply<int> reply = korganizerObj.call( QLatin1String("winId") );
963 if ( reply.isValid() ) {
964 int window = reply;
965 int desktop = KWindowSystem::windowInfo( window, NET::WMDesktop ).desktop();
966 if ( KWindowSystem::currentDesktop() == desktop ) {
967 KWindowSystem::minimizeWindow( winId(), false );
968 } else {
969 KWindowSystem::setCurrentDesktop( desktop );
971 KWindowSystem::activateWindow( KWindowSystem::transientFor( window ) );
973 #elif defined(Q_WS_WIN)
974 // WId is a typedef to a void* on windows
975 QDBusReply<qlonglong> reply = korganizerObj.call( QLatin1String("winId") );
976 if ( reply.isValid() ) {
977 qlonglong window = reply;
978 KWindowSystem::minimizeWindow( winId(), false );
979 KWindowSystem::allowExternalProcessWindowActivation();
980 KWindowSystem::activateWindow( reinterpret_cast<WId>(window) );
982 #else
983 // TODO (mac)
984 #endif
985 return true;
988 bool AlarmDialog::openIncidenceEditorNG( const Akonadi::Item &item )
990 Incidence::Ptr incidence = CalendarSupport::incidence( item );
991 IncidenceEditorNG::IncidenceDialog *dialog =
992 IncidenceEditorNG::IncidenceDialogFactory::create(
993 false, /*doesn't need initial saving*/
994 incidence->type(), 0, this );
995 dialog->load( item );
996 return true;
999 void AlarmDialog::removeFromConfig( const QList<Akonadi::Item::Id> &ids )
1001 KSharedConfig::Ptr config = KGlobal::config();
1002 KConfigGroup genGroup( config, "General" );
1004 const int oldNumReminders = genGroup.readEntry( "Reminders", 0 );
1006 QList<ConfItem> newReminders;
1007 // Delete everything
1008 for ( int i = 1; i <= oldNumReminders; ++i ) {
1009 const QString group( QString::fromLatin1( "Incidence-%1" ).arg( i ) );
1010 KConfigGroup incGroup( config, group );
1011 const QString uid = incGroup.readEntry( "UID" );
1012 const QDateTime remindAtDate = incGroup.readEntry( "RemindAt", QDateTime() );
1013 const KUrl akonadiUrl = incGroup.readEntry( "AkonadiUrl" );
1014 const Akonadi::Item::Id id = Akonadi::Item::fromUrl( akonadiUrl ).id();
1015 if ( !ids.contains( id ) ) {
1016 ConfItem ci;
1017 ci.akonadiUrl = akonadiUrl;
1018 ci.remindAt = remindAtDate;
1019 ci.uid = uid;
1020 newReminders.append( ci );
1022 config->deleteGroup( group );
1025 genGroup.writeEntry( "Reminders", newReminders.count() );
1027 //Write everything except those which have an uid we don't want
1028 for ( int i = 0; i < newReminders.count(); ++i ) {
1029 const QString group( QString::fromLatin1( "Incidence-%1" ).arg( i + 1 ) );
1030 KConfigGroup incGroup( config, group );
1031 incGroup.writeEntry( "UID", newReminders[i].uid );
1032 incGroup.writeEntry( "RemindAt", newReminders[i].remindAt );
1033 incGroup.writeEntry( "AkonadiUrl", newReminders[i].akonadiUrl );
1034 incGroup.sync();
1036 genGroup.sync();