00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include <qhbox.h>
00027 #include <qvbox.h>
00028 #include <qlabel.h>
00029 #include <qfile.h>
00030 #include <qspinbox.h>
00031 #include <qlayout.h>
00032 #include <qpushbutton.h>
00033 #include <qcstring.h>
00034 #include <qdatastream.h>
00035
00036 #include <dcopclient.h>
00037 #include <dcopref.h>
00038 #include <kapplication.h>
00039 #include <kconfig.h>
00040 #include <kdcopservicestarter.h>
00041 #include <kiconloader.h>
00042 #include <klocale.h>
00043 #include <kprocess.h>
00044 #include <kaudioplayer.h>
00045 #include <kdebug.h>
00046 #include <kmessagebox.h>
00047 #include <knotifyclient.h>
00048 #include <kcombobox.h>
00049 #include <klistview.h>
00050 #include <kwin.h>
00051 #include <klockfile.h>
00052
00053 #include <libkcal/event.h>
00054 #include <libkcal/incidenceformatter.h>
00055
00056 #include "koeventviewer.h"
00057
00058 #include "alarmdialog.h"
00059 #include "alarmdialog.moc"
00060
00061 static int defSuspendVal = 5;
00062 static int defSuspendUnit = 0;
00063 static QDateTime defModDateTime = QDateTime( QDate( 1970, 1, 1 ), QTime( 0, 0, 0 ) );
00064
00065 class AlarmListItem : public KListViewItem
00066 {
00067 public:
00068 AlarmListItem( const QString &uid, QListView *parent ) :
00069 KListViewItem( parent ),
00070 mUid( uid ),
00071 mNotified( false )
00072 {}
00073
00074 ~AlarmListItem()
00075 {
00076 }
00077
00078 int compare( KListViewItem *item, int iCol, bool bAscending ) const;
00079
00080 QString mDisplayText;
00081
00082 QString mUid;
00083 int mLastRevision;
00084 QDateTime mLastModified;
00085 QDateTime mRemindAt;
00086 QDateTime mHappening;
00087 bool mNotified;
00088 };
00089
00090 int AlarmListItem::compare( KListViewItem *item, int iCol, bool bAscending ) const
00091 {
00092 if ( iCol == 1 ) {
00093 AlarmListItem *pItem = static_cast<AlarmListItem *>( item );
00094 return pItem->mHappening.secsTo( mHappening );
00095 } else {
00096 return KListViewItem::compare( item, iCol, bAscending );
00097 }
00098 }
00099
00100 typedef QValueList<AlarmListItem*> ItemList;
00101
00102 AlarmDialog::AlarmDialog( KCal::CalendarResources *calendar, QWidget *parent, const char *name )
00103 : KDialogBase( Plain, WType_TopLevel | WStyle_Customize | WStyle_StaysOnTop |
00104 WStyle_DialogBorder,
00105 parent, name, false, i18n("Reminder"),
00106 Ok | User1 | User2 | User3, NoDefault,
00107 false, i18n("Edit..."), i18n("Dismiss All"), i18n("Dismiss Reminder") ),
00108 mCalendar( calendar ), mSuspendTimer(this)
00109 {
00110
00111
00112
00113
00114
00115 KGlobal::iconLoader()->addAppDir( "kdepim" );
00116 setButtonOK( i18n( "Suspend" ) );
00117
00118 QWidget *topBox = plainPage();
00119 QBoxLayout *topLayout = new QVBoxLayout( topBox );
00120 topLayout->setSpacing( spacingHint() );
00121
00122 QLabel *label = new QLabel( i18n("The following items triggered reminders:"),
00123 topBox );
00124 topLayout->addWidget( label );
00125
00126 mIncidenceListView = new KListView( topBox );
00127 mIncidenceListView->addColumn( i18n( "Summary" ) );
00128 mIncidenceListView->addColumn( i18n( "Due" ) );
00129 mIncidenceListView->setSorting( 0, true );
00130 mIncidenceListView->setSorting( 1, true );
00131 mIncidenceListView->setSortColumn( 1 );
00132 mIncidenceListView->setShowSortIndicator( true );
00133 mIncidenceListView->setAllColumnsShowFocus( true );
00134 mIncidenceListView->setSelectionMode( QListView::Extended );
00135 topLayout->addWidget( mIncidenceListView );
00136 connect( mIncidenceListView, SIGNAL(selectionChanged()), SLOT(updateButtons()) );
00137 connect( mIncidenceListView, SIGNAL(doubleClicked(QListViewItem*)), SLOT(edit()) );
00138 connect( mIncidenceListView, SIGNAL(currentChanged(QListViewItem*)), SLOT(showDetails()) );
00139 connect( mIncidenceListView, SIGNAL(selectionChanged()), SLOT(showDetails()) );
00140
00141 mDetailView = new KOEventViewer( mCalendar, topBox );
00142 mDetailView->setFocus();
00143
00144 topLayout->addWidget( mDetailView );
00145
00146 QHBox *suspendBox = new QHBox( topBox );
00147 suspendBox->setSpacing( spacingHint() );
00148 topLayout->addWidget( suspendBox );
00149
00150 QLabel *l = new QLabel( i18n("Suspend &duration:"), suspendBox );
00151 mSuspendSpin = new QSpinBox( 1, 9999, 1, suspendBox );
00152 mSuspendSpin->setValue( defSuspendVal );
00153 l->setBuddy( mSuspendSpin );
00154
00155 mSuspendUnit = new KComboBox( suspendBox );
00156 mSuspendUnit->insertItem( i18n("minute(s)") );
00157 mSuspendUnit->insertItem( i18n("hour(s)") );
00158 mSuspendUnit->insertItem( i18n("day(s)") );
00159 mSuspendUnit->insertItem( i18n("week(s)") );
00160 mSuspendUnit->setCurrentItem( defSuspendUnit );
00161
00162 connect( &mSuspendTimer, SIGNAL(timeout()), SLOT(wakeUp()) );
00163
00164 setMinimumSize( 300, 200 );
00165 }
00166
00167 AlarmDialog::~AlarmDialog()
00168 {
00169 mIncidenceListView->clear();
00170 }
00171
00172 void AlarmDialog::addIncidence( Incidence *incidence,
00173 const QDateTime &reminderAt,
00174 const QString &displayText )
00175 {
00176 AlarmListItem *item = new AlarmListItem( incidence->uid(), mIncidenceListView );
00177 item->setText( 0, incidence->summary() );
00178 item->mRemindAt = reminderAt;
00179 item->mDisplayText = displayText;
00180 item->mLastRevision = incidence->revision();
00181 if ( incidence->lastModified().isValid() ) {
00182 item->mLastModified = incidence->lastModified();
00183 } else {
00184 item->mLastModified = defModDateTime;
00185 }
00186
00187 Event *event;
00188 Todo *todo;
00189 Alarm *alarm = incidence->alarms().first();
00190 if ( incidence->type() == "Event" ) {
00191 item->setPixmap( 0, SmallIcon( "appointment" ) );
00192 event = static_cast<Event *>( incidence );
00193 if ( event->doesRecur() ) {
00194 QDateTime nextStart = event->recurrence()->getNextDateTime( reminderAt );
00195 if ( nextStart.isValid() ) {
00196 item->mHappening = nextStart;
00197 item->setText( 1, KGlobal::locale()->formatDateTime( nextStart ) );
00198 }
00199 }
00200 if ( item->text( 1 ).isEmpty() ) {
00201 QDateTime qdt;
00202 if ( alarm->hasStartOffset() ) {
00203 qdt = event->dtStart();
00204 } else {
00205 qdt = event->dtEnd();
00206 }
00207 item->mHappening = qdt;
00208 item->setText( 1, IncidenceFormatter::dateTimeToString( qdt, false, true ) );
00209 }
00210 } else if ( incidence->type() == "Todo" ) {
00211 item->setPixmap( 0, SmallIcon( "todo" ) );
00212 todo = static_cast<Todo *>( incidence );
00213 if ( todo->doesRecur() ) {
00214 QDateTime nextStart = todo->recurrence()->getNextDateTime( reminderAt );
00215 if ( nextStart.isValid() ) {
00216 item->mHappening = nextStart;
00217 item->setText( 1, KGlobal::locale()->formatDateTime( nextStart ) );
00218 }
00219 }
00220 if ( item->text( 1 ).isEmpty() ) {
00221 QDateTime qdt;
00222 if ( alarm->hasStartOffset() && todo->dtStart().isValid() ) {
00223 qdt = todo->dtStart();
00224 } else {
00225 qdt = todo->dtDue();
00226 }
00227 item->mHappening = qdt;
00228 item->setText( 1, IncidenceFormatter::dateTimeToString( qdt, false, true ) );
00229 }
00230 }
00231
00232 if ( activeCount() == 1 ) {
00233 mIncidenceListView->clearSelection();
00234 item->setSelected( true );
00235 }
00236 showDetails();
00237 }
00238
00239 void AlarmDialog::slotOk()
00240 {
00241 suspend();
00242 }
00243
00244 void AlarmDialog::slotUser1()
00245 {
00246 edit();
00247 }
00248
00249 void AlarmDialog::slotUser2()
00250 {
00251 dismissAll();
00252 }
00253
00254 void AlarmDialog::slotUser3()
00255 {
00256 dismissCurrent();
00257 }
00258
00259 void AlarmDialog::dismissCurrent()
00260 {
00261 ItemList selection = selectedItems();
00262 for ( ItemList::Iterator it = selection.begin(); it != selection.end(); ++it ) {
00263 if ( (*it)->itemBelow() )
00264 (*it)->itemBelow()->setSelected( true );
00265 else if ( (*it)->itemAbove() )
00266 (*it)->itemAbove()->setSelected( true );
00267 delete *it;
00268 }
00269 if ( activeCount() == 0 ) {
00270 accept();
00271 } else {
00272 updateButtons();
00273 showDetails();
00274 }
00275 emit reminderCount( activeCount() );
00276 }
00277
00278 void AlarmDialog::dismissAll()
00279 {
00280 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ) {
00281 AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00282 if ( !item->isVisible() ) {
00283 ++it;
00284 continue;
00285 }
00286 delete item;
00287 }
00288 setTimer();
00289 accept();
00290 emit reminderCount( activeCount() );
00291 }
00292
00293 void AlarmDialog::edit()
00294 {
00295 ItemList selection = selectedItems();
00296 if ( selection.count() != 1 ) {
00297 return;
00298 }
00299 Incidence *incidence = mCalendar->incidence( selection.first()->mUid );
00300 if ( !incidence ) {
00301 return;
00302 }
00303 QDate dt = selection.first()->mRemindAt.date();
00304
00305 if ( incidence->isReadOnly() ) {
00306 KMessageBox::sorry(
00307 this,
00308 i18n( "\"%1\" is a read-only item so modifications are not possible." ).
00309 arg( incidence->summary() ) );
00310 return;
00311 }
00312
00313 if ( !ensureKorganizerRunning() ) {
00314 KMessageBox::error(
00315 this,
00316 i18n( "Could not start KOrganizer so editing is not possible." ) );
00317 return;
00318 }
00319
00320 QByteArray data;
00321 QDataStream arg( data, IO_WriteOnly );
00322 arg << incidence->uid();
00323 arg << dt;
00324
00325 if ( !kapp->dcopClient()->send( "korganizer", "KOrganizerIface",
00326 "editIncidence(QString,QDate)",
00327 data ) ) {
00328 KMessageBox::error(
00329 this,
00330 i18n( "An internal KOrganizer error occurred attempting to start the incidence editor" ) );
00331 return;
00332 }
00333
00334
00335 QByteArray replyData;
00336 QCString object, replyType;
00337 object = kapp->dcopClient()->isApplicationRegistered( "kontact" ) ?
00338 "kontact-mainwindow#1" : "KOrganizer MainWindow";
00339 if (!kapp->dcopClient()->call( "korganizer", object,
00340 "getWinID()", 0, replyType, replyData, true, -1 ) ) {
00341 }
00342
00343 if ( replyType == "int" ) {
00344 int desktop, window;
00345 QDataStream ds( replyData, IO_ReadOnly );
00346 ds >> window;
00347 desktop = KWin::windowInfo( window ).desktop();
00348
00349 if ( KWin::currentDesktop() == desktop ) {
00350 KWin::iconifyWindow( winId(), false );
00351 } else {
00352 KWin::setCurrentDesktop( desktop );
00353 }
00354 KWin::activateWindow( KWin::transientFor( window ) );
00355 }
00356 }
00357
00358 void AlarmDialog::suspend()
00359 {
00360 if ( !isVisible() )
00361 return;
00362
00363 int unit=1;
00364 switch (mSuspendUnit->currentItem()) {
00365 case 3:
00366 unit *= 7;
00367 case 2:
00368 unit *= 24;
00369 case 1:
00370 unit *= 60;
00371 case 0:
00372 unit *= 60;
00373 default:
00374 break;
00375 }
00376
00377 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00378 AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00379 if ( item->isSelected() && item->isVisible() ) {
00380 item->setVisible( false );
00381 item->setSelected( false );
00382 item->mRemindAt = QDateTime::currentDateTime().addSecs( unit * mSuspendSpin->value() );
00383 item->mNotified = false;
00384 }
00385 }
00386
00387
00388
00389 slotSave();
00390
00391 setTimer();
00392 if ( activeCount() == 0 ) {
00393 accept();
00394 } else {
00395 updateButtons();
00396 showDetails();
00397 }
00398 emit reminderCount( activeCount() );
00399 }
00400
00401 void AlarmDialog::setTimer()
00402 {
00403 int nextReminderAt = -1;
00404 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00405 AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00406 if ( item->mRemindAt > QDateTime::currentDateTime() ) {
00407 int secs = QDateTime::currentDateTime().secsTo( item->mRemindAt );
00408 nextReminderAt = nextReminderAt <= 0 ? secs : QMIN( nextReminderAt, secs );
00409 }
00410 }
00411
00412 if ( nextReminderAt >= 0 ) {
00413 mSuspendTimer.stop();
00414 mSuspendTimer.start( 1000 * (nextReminderAt + 1), true );
00415 }
00416 }
00417
00418 void AlarmDialog::show()
00419 {
00420 mIncidenceListView->sort();
00421
00422 mIncidenceListView->clearSelection();
00423 if ( mIncidenceListView->firstChild() )
00424 mIncidenceListView->firstChild()->setSelected( true );
00425
00426 updateButtons();
00427 showDetails();
00428
00429
00430 mSuspendSpin->setValue( defSuspendVal );
00431 mSuspendUnit->setCurrentItem( defSuspendUnit );
00432
00433 KDialogBase::show();
00434 KWin::deIconifyWindow( winId(), false );
00435 KWin::setState( winId(), NET::KeepAbove );
00436 KWin::setOnAllDesktops( winId(), true );
00437 eventNotification();
00438 }
00439
00440 void AlarmDialog::eventNotification()
00441 {
00442 bool beeped = false, found = false;
00443
00444 QValueList<AlarmListItem*> list;
00445 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00446 AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00447 if ( !item->isVisible() || item->mNotified ) {
00448 continue;
00449 }
00450 Incidence *incidence = mCalendar->incidence( item->mUid );
00451 if ( !incidence ) {
00452 continue;
00453 }
00454 found = true;
00455 item->mNotified = true;
00456 Alarm::List alarms = incidence->alarms();
00457 Alarm::List::ConstIterator it;
00458 for ( it = alarms.begin(); it != alarms.end(); ++it ) {
00459 Alarm *alarm = *it;
00460
00461 if (alarm->type() == Alarm::Procedure) {
00462
00463 kdDebug(5890) << "Starting program: '" << alarm->programFile() << "'" << endl;
00464 KProcess proc;
00465 proc << QFile::encodeName(alarm->programFile());
00466 proc.start(KProcess::DontCare);
00467 }
00468 else if (alarm->type() == Alarm::Audio) {
00469 beeped = true;
00470 KAudioPlayer::play(QFile::encodeName(alarm->audioFile()));
00471 }
00472 }
00473 }
00474
00475 if ( !beeped && found ) {
00476 KNotifyClient::beep();
00477 }
00478 }
00479
00480 void AlarmDialog::wakeUp()
00481 {
00482 bool activeReminders = false;
00483 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00484 AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00485 Incidence *incidence = mCalendar->incidence( item->mUid );
00486 if ( !incidence ) {
00487 delete item;
00488 continue;
00489 }
00490
00491 QDateTime ilm;
00492 if ( incidence->lastModified().isValid() ) {
00493 ilm = incidence->lastModified();
00494 } else {
00495 ilm = defModDateTime;
00496 }
00497
00498
00499
00500
00501 if ( item->mRemindAt <= QDateTime::currentDateTime() &&
00502 incidence->revision() <= item->mLastRevision &&
00503 ilm <= item->mLastModified ) {
00504 if ( !item->isVisible() ) {
00505 item->setVisible( true );
00506 item->setSelected( false );
00507 }
00508 activeReminders = true;
00509 } else {
00510 item->setVisible( false );
00511 }
00512 item->mLastRevision = incidence->revision();
00513 item->mLastModified = incidence->lastModified();
00514 }
00515
00516 if ( activeReminders )
00517 show();
00518 setTimer();
00519 showDetails();
00520 emit reminderCount( activeCount() );
00521 }
00522
00523 void AlarmDialog::slotSave()
00524 {
00525 KConfig *config = kapp->config();
00526 KLockFile::Ptr lock = config->lockFile();
00527 if ( lock.data()->lock() != KLockFile::LockOK )
00528 return;
00529
00530 config->setGroup( "General" );
00531 int numReminders = config->readNumEntry("Reminders", 0);
00532
00533 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00534 AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00535 Incidence *incidence = mCalendar->incidence( item->mUid );
00536 if ( !incidence ) {
00537 continue;
00538 }
00539 config->setGroup( QString("Incidence-%1").arg(numReminders + 1) );
00540 config->writeEntry( "UID", incidence->uid() );
00541 config->writeEntry( "RemindAt", item->mRemindAt );
00542 ++numReminders;
00543 }
00544
00545 config->setGroup( "General" );
00546 config->writeEntry( "Reminders", numReminders );
00547 config->sync();
00548 lock.data()->unlock();
00549 }
00550
00551 void AlarmDialog::updateButtons()
00552 {
00553 ItemList selection = selectedItems();
00554 enableButton( User1, selection.count() == 1 );
00555 enableButton( User3, selection.count() > 0 );
00556 enableButton( Ok, selection.count() > 0 );
00557 }
00558
00559 QValueList< AlarmListItem * > AlarmDialog::selectedItems() const
00560 {
00561 QValueList<AlarmListItem*> list;
00562 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00563 if ( it.current()->isSelected() )
00564 list.append( static_cast<AlarmListItem*>( it.current() ) );
00565 }
00566 return list;
00567 }
00568
00569 int AlarmDialog::activeCount()
00570 {
00571 int count = 0;
00572 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00573 AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00574 if ( item->isVisible() )
00575 ++count;
00576 }
00577 return count;
00578 }
00579
00580 void AlarmDialog::suspendAll()
00581 {
00582 mIncidenceListView->clearSelection();
00583 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00584 if ( it.current()->isVisible() )
00585 it.current()->setSelected( true );
00586 }
00587 suspend();
00588 }
00589
00590 void AlarmDialog::showDetails()
00591 {
00592 mDetailView->clearEvents( true );
00593 mDetailView->clear();
00594 AlarmListItem *item = static_cast<AlarmListItem*>( mIncidenceListView->currentItem() );
00595 if ( !item || !item->isVisible() )
00596 return;
00597
00598 Incidence *incidence = mCalendar->incidence( item->mUid );
00599 if ( !incidence ) {
00600 return;
00601 }
00602
00603 if ( !item->mDisplayText.isEmpty() ) {
00604 QString txt = "<qt><p><b>" + item->mDisplayText + "</b></p></qt>";
00605 mDetailView->addText( txt );
00606 }
00607 item->setText( 0, incidence->summary() );
00608 mDetailView->appendIncidence( incidence, item->mRemindAt.date() );
00609 }
00610
00611 bool AlarmDialog::ensureKorganizerRunning() const
00612 {
00613 QString error;
00614 QCString dcopService;
00615
00616 int result = KDCOPServiceStarter::self()->findServiceFor(
00617 "DCOP/Organizer", QString::null, QString::null, &error, &dcopService );
00618
00619 if ( result == 0 ) {
00620
00621
00622
00623 static const char* const dcopObjectId = "KOrganizerIface";
00624 QCString dummy;
00625 if ( !kapp->dcopClient()->findObject(
00626 dcopService, dcopObjectId, "", QByteArray(), dummy, dummy ) ) {
00627 DCOPRef ref( dcopService, dcopService );
00628 DCOPReply reply = ref.call( "load()" );
00629 if ( reply.isValid() && (bool)reply ) {
00630 Q_ASSERT( kapp->dcopClient()->findObject(
00631 dcopService, dcopObjectId, "", QByteArray(), dummy, dummy ) );
00632 } else {
00633 kdWarning() << "Error loading " << dcopService << endl;
00634 }
00635 }
00636
00637
00638 return true;
00639
00640 } else {
00641 kdWarning() << "Couldn't start DCOP/Organizer: " << dcopService
00642 << " " << error << endl;
00643 }
00644 return false;
00645 }