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