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,
00100 WType_TopLevel | WStyle_Customize | WStyle_StaysOnTop | 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 setMainWidget( mIncidenceListView );
00163 mIncidenceListView->setMinimumSize( 500, 200 );
00164 }
00165
00166 AlarmDialog::~AlarmDialog()
00167 {
00168 mIncidenceListView->clear();
00169 }
00170
00171 AlarmListItem *AlarmDialog::searchByUid( const QString &uid )
00172 {
00173 AlarmListItem *found = 0;
00174 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ) {
00175 AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00176 if ( item->mUid == uid ) {
00177 found = item;
00178 break;
00179 }
00180 ++it;
00181 }
00182 return found;
00183 }
00184
00185 static QString etc = i18n( "elipsis", "..." );
00186 static QString cleanSummary( const QString &summary )
00187 {
00188 uint maxLen = 45;
00189 QString retStr = summary;
00190 retStr.replace( '\n', ' ' );
00191 if ( retStr.length() > maxLen ) {
00192 maxLen -= etc.length();
00193 retStr = retStr.left( maxLen );
00194 retStr += etc;
00195 }
00196 return retStr;
00197 }
00198
00199 void AlarmDialog::addIncidence( Incidence *incidence,
00200 const QDateTime &reminderAt,
00201 const QString &displayText )
00202 {
00203 AlarmListItem *item = searchByUid( incidence->uid() );
00204 if ( !item ) {
00205 item = new AlarmListItem( incidence->uid(), mIncidenceListView );
00206 }
00207 item->mNotified = false;
00208 item->mHappening = QDateTime();
00209 item->mRemindAt = reminderAt;
00210 item->mDisplayText = displayText;
00211 item->setText( 0, cleanSummary( incidence->summary() ) );
00212 item->setText( 1, QString() );
00213
00214 Event *event;
00215 Todo *todo;
00216 Alarm *alarm = incidence->alarms().first();
00217 if ( incidence->type() == "Event" ) {
00218 item->setPixmap( 0, SmallIcon( "appointment" ) );
00219 event = static_cast<Event *>( incidence );
00220 if ( event->doesRecur() ) {
00221 QDateTime nextStart = event->recurrence()->getNextDateTime( reminderAt );
00222 if ( nextStart.isValid() ) {
00223 item->mHappening = nextStart;
00224 item->setText( 1, KGlobal::locale()->formatDateTime( nextStart ) );
00225 }
00226 }
00227 if ( item->text( 1 ).isEmpty() ) {
00228 QDateTime qdt;
00229 if ( alarm->hasStartOffset() ) {
00230 qdt = event->dtStart();
00231 } else {
00232 qdt = event->dtEnd();
00233 }
00234 item->mHappening = qdt;
00235 item->setText( 1, IncidenceFormatter::dateTimeToString( qdt, false, true ) );
00236 }
00237 } else if ( incidence->type() == "Todo" ) {
00238 item->setPixmap( 0, SmallIcon( "todo" ) );
00239 todo = static_cast<Todo *>( incidence );
00240 if ( todo->doesRecur() ) {
00241 QDateTime nextStart = todo->recurrence()->getNextDateTime( reminderAt );
00242 if ( nextStart.isValid() ) {
00243 item->mHappening = nextStart;
00244 item->setText( 1, KGlobal::locale()->formatDateTime( nextStart ) );
00245 }
00246 }
00247 if ( item->text( 1 ).isEmpty() ) {
00248 QDateTime qdt;
00249 if ( alarm->hasStartOffset() && todo->dtStart().isValid() ) {
00250 qdt = todo->dtStart();
00251 } else {
00252 qdt = todo->dtDue();
00253 }
00254 item->mHappening = qdt;
00255 item->setText( 1, IncidenceFormatter::dateTimeToString( qdt, false, true ) );
00256 }
00257 }
00258
00259 if ( activeCount() == 1 ) {
00260 mIncidenceListView->clearSelection();
00261 item->setSelected( true );
00262 }
00263 showDetails();
00264 }
00265
00266 void AlarmDialog::slotOk()
00267 {
00268 suspend();
00269 }
00270
00271 void AlarmDialog::slotUser1()
00272 {
00273 edit();
00274 }
00275
00276 void AlarmDialog::slotUser2()
00277 {
00278 dismissAll();
00279 }
00280
00281 void AlarmDialog::slotUser3()
00282 {
00283 dismissCurrent();
00284 }
00285
00286 void AlarmDialog::dismissCurrent()
00287 {
00288 ItemList selection = selectedItems();
00289 for ( ItemList::Iterator it = selection.begin(); it != selection.end(); ++it ) {
00290 if ( (*it)->itemBelow() )
00291 (*it)->itemBelow()->setSelected( true );
00292 else if ( (*it)->itemAbove() )
00293 (*it)->itemAbove()->setSelected( true );
00294 delete *it;
00295 }
00296 if ( activeCount() == 0 ) {
00297 accept();
00298 } else {
00299 updateButtons();
00300 showDetails();
00301 }
00302 emit reminderCount( activeCount() );
00303 }
00304
00305 void AlarmDialog::dismissAll()
00306 {
00307 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ) {
00308 AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00309 if ( !item->isVisible() ) {
00310 ++it;
00311 continue;
00312 }
00313 mIncidenceListView->takeItem( item );
00314 delete item;
00315 }
00316 setTimer();
00317 accept();
00318 emit reminderCount( activeCount() );
00319 }
00320
00321 void AlarmDialog::edit()
00322 {
00323 ItemList selection = selectedItems();
00324 if ( selection.count() != 1 ) {
00325 return;
00326 }
00327 Incidence *incidence = mCalendar->incidence( selection.first()->mUid );
00328 if ( !incidence ) {
00329 return;
00330 }
00331 QDate dt = selection.first()->mRemindAt.date();
00332
00333 if ( incidence->isReadOnly() ) {
00334 KMessageBox::sorry(
00335 this,
00336 i18n( "\"%1\" is a read-only item so modifications are not possible." ).
00337 arg( cleanSummary( incidence->summary() ) ) );
00338 return;
00339 }
00340
00341 if ( !ensureKorganizerRunning() ) {
00342 KMessageBox::error(
00343 this,
00344 i18n( "Could not start KOrganizer so editing is not possible." ) );
00345 return;
00346 }
00347
00348 QByteArray data;
00349 QDataStream arg( data, IO_WriteOnly );
00350 arg << incidence->uid();
00351 arg << dt;
00352
00353 if ( !kapp->dcopClient()->send( "korganizer", "KOrganizerIface",
00354 "editIncidence(QString,QDate)",
00355 data ) ) {
00356 KMessageBox::error(
00357 this,
00358 i18n( "An internal KOrganizer error occurred attempting to start the incidence editor" ) );
00359 return;
00360 }
00361
00362
00363 QByteArray replyData;
00364 QCString object, replyType;
00365 object = kapp->dcopClient()->isApplicationRegistered( "kontact" ) ?
00366 "kontact-mainwindow#1" : "KOrganizer MainWindow";
00367 if (!kapp->dcopClient()->call( "korganizer", object,
00368 "getWinID()", 0, replyType, replyData, true, -1 ) ) {
00369 }
00370
00371 if ( replyType == "int" ) {
00372 int desktop, window;
00373 QDataStream ds( replyData, IO_ReadOnly );
00374 ds >> window;
00375 desktop = KWin::windowInfo( window ).desktop();
00376
00377 if ( KWin::currentDesktop() == desktop ) {
00378 KWin::iconifyWindow( winId(), false );
00379 } else {
00380 KWin::setCurrentDesktop( desktop );
00381 }
00382 KWin::activateWindow( KWin::transientFor( window ) );
00383 }
00384 }
00385
00386 void AlarmDialog::suspend()
00387 {
00388 if ( !isVisible() )
00389 return;
00390
00391 int unit=1;
00392 switch (mSuspendUnit->currentItem()) {
00393 case 3:
00394 unit *= 7;
00395 case 2:
00396 unit *= 24;
00397 case 1:
00398 unit *= 60;
00399 case 0:
00400 unit *= 60;
00401 default:
00402 break;
00403 }
00404
00405 AlarmListItem *selitem = 0;
00406 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00407 AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00408 if ( item->isSelected() && item->isVisible() ) {
00409 item->setVisible( false );
00410 item->setSelected( false );
00411 item->mRemindAt = QDateTime::currentDateTime().addSecs( unit * mSuspendSpin->value() );
00412 item->mNotified = false;
00413 selitem = item;
00414 }
00415 }
00416 if ( selitem ) {
00417 if ( selitem->itemBelow() ) {
00418 selitem->itemBelow()->setSelected( true );
00419 } else if ( selitem->itemAbove() ) {
00420 selitem->itemAbove()->setSelected( true );
00421 }
00422 }
00423
00424
00425
00426 slotSave();
00427
00428 setTimer();
00429 if ( activeCount() == 0 ) {
00430 accept();
00431 } else {
00432 updateButtons();
00433 showDetails();
00434 }
00435 emit reminderCount( activeCount() );
00436 }
00437
00438 void AlarmDialog::setTimer()
00439 {
00440 int nextReminderAt = -1;
00441 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00442 AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00443 if ( item->mRemindAt > QDateTime::currentDateTime() ) {
00444 int secs = QDateTime::currentDateTime().secsTo( item->mRemindAt );
00445 nextReminderAt = nextReminderAt <= 0 ? secs : QMIN( nextReminderAt, secs );
00446 }
00447 }
00448
00449 if ( nextReminderAt >= 0 ) {
00450 mSuspendTimer.stop();
00451 mSuspendTimer.start( 1000 * (nextReminderAt + 1), true );
00452 }
00453 }
00454
00455 void AlarmDialog::show()
00456 {
00457 mIncidenceListView->sort();
00458
00459 mIncidenceListView->clearSelection();
00460 if ( mIncidenceListView->firstChild() )
00461 mIncidenceListView->firstChild()->setSelected( true );
00462
00463 updateButtons();
00464 showDetails();
00465
00466
00467 mSuspendSpin->setValue( defSuspendVal );
00468 mSuspendUnit->setCurrentItem( defSuspendUnit );
00469
00470 KDialogBase::show();
00471 KWin::deIconifyWindow( winId(), false );
00472 KWin::setState( winId(), NET::KeepAbove );
00473 KWin::setOnAllDesktops( winId(), true );
00474 eventNotification();
00475 }
00476
00477 void AlarmDialog::eventNotification()
00478 {
00479 bool beeped = false, found = false;
00480
00481 QValueList<AlarmListItem*> list;
00482 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00483 AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00484 if ( !item->isVisible() || item->mNotified ) {
00485 continue;
00486 }
00487 Incidence *incidence = mCalendar->incidence( item->mUid );
00488 if ( !incidence ) {
00489 continue;
00490 }
00491 found = true;
00492 item->mNotified = true;
00493 Alarm::List alarms = incidence->alarms();
00494 Alarm::List::ConstIterator it;
00495 for ( it = alarms.begin(); it != alarms.end(); ++it ) {
00496 Alarm *alarm = *it;
00497
00498 if (alarm->type() == Alarm::Procedure) {
00499
00500 kdDebug(5890) << "Starting program: '" << alarm->programFile() << "'" << endl;
00501 KProcess proc;
00502 proc << QFile::encodeName(alarm->programFile());
00503 proc.start(KProcess::DontCare);
00504 }
00505 else if (alarm->type() == Alarm::Audio) {
00506 beeped = true;
00507 KAudioPlayer::play(QFile::encodeName(alarm->audioFile()));
00508 }
00509 }
00510 }
00511
00512 if ( !beeped && found ) {
00513 KNotifyClient::beep();
00514 }
00515 }
00516
00517 void AlarmDialog::wakeUp()
00518 {
00519 bool activeReminders = false;
00520 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00521 AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00522 Incidence *incidence = mCalendar->incidence( item->mUid );
00523 if ( !incidence ) {
00524 delete item;
00525 continue;
00526 }
00527
00528 if ( item->mRemindAt <= QDateTime::currentDateTime() ) {
00529 if ( !item->isVisible() ) {
00530 item->setVisible( true );
00531 item->setSelected( false );
00532 }
00533 activeReminders = true;
00534 } else {
00535 item->setVisible( false );
00536 }
00537 }
00538
00539 if ( activeReminders )
00540 show();
00541 setTimer();
00542 showDetails();
00543 emit reminderCount( activeCount() );
00544 }
00545
00546 void AlarmDialog::slotSave()
00547 {
00548 KConfig *config = kapp->config();
00549 KLockFile::Ptr lock = config->lockFile();
00550 if ( lock.data()->lock() != KLockFile::LockOK )
00551 return;
00552
00553 config->setGroup( "General" );
00554 int numReminders = config->readNumEntry("Reminders", 0);
00555
00556 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00557 AlarmListItem *item = static_cast<AlarmListItem*>( it.current() );
00558 Incidence *incidence = mCalendar->incidence( item->mUid );
00559 if ( !incidence ) {
00560 continue;
00561 }
00562 config->setGroup( QString("Incidence-%1").arg(numReminders + 1) );
00563 config->writeEntry( "UID", incidence->uid() );
00564 config->writeEntry( "RemindAt", item->mRemindAt );
00565 ++numReminders;
00566 }
00567
00568 config->setGroup( "General" );
00569 config->writeEntry( "Reminders", numReminders );
00570 config->sync();
00571 lock.data()->unlock();
00572 }
00573
00574 void AlarmDialog::updateButtons()
00575 {
00576 ItemList selection = selectedItems();
00577 enableButton( User1, selection.count() == 1 );
00578 enableButton( User3, selection.count() > 0 );
00579 enableButton( Ok, selection.count() > 0 );
00580 }
00581
00582 QValueList< AlarmListItem * > AlarmDialog::selectedItems() const
00583 {
00584 QValueList<AlarmListItem*> list;
00585 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00586 if ( it.current()->isSelected() )
00587 list.append( static_cast<AlarmListItem*>( it.current() ) );
00588 }
00589 return list;
00590 }
00591
00592 int AlarmDialog::activeCount()
00593 {
00594 int count = 0;
00595 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00596 AlarmListItem * item = static_cast<AlarmListItem*>( it.current() );
00597 if ( item->isVisible() )
00598 ++count;
00599 }
00600 return count;
00601 }
00602
00603 void AlarmDialog::suspendAll()
00604 {
00605 mIncidenceListView->clearSelection();
00606 for ( QListViewItemIterator it( mIncidenceListView ) ; it.current() ; ++it ) {
00607 if ( it.current()->isVisible() )
00608 it.current()->setSelected( true );
00609 }
00610 suspend();
00611 }
00612
00613 void AlarmDialog::showDetails()
00614 {
00615 mDetailView->clearEvents( true );
00616 mDetailView->clear();
00617 AlarmListItem *item = static_cast<AlarmListItem*>( mIncidenceListView->currentItem() );
00618 if ( !item || !item->isVisible() )
00619 return;
00620
00621 Incidence *incidence = mCalendar->incidence( item->mUid );
00622 if ( !incidence ) {
00623 return;
00624 }
00625
00626 if ( !item->mDisplayText.isEmpty() ) {
00627 QString txt = "<qt><p><b>" + item->mDisplayText + "</b></p></qt>";
00628 mDetailView->addText( txt );
00629 }
00630 item->setText( 0, cleanSummary( incidence->summary() ) );
00631 mDetailView->appendIncidence( incidence, item->mRemindAt.date() );
00632 }
00633
00634 bool AlarmDialog::ensureKorganizerRunning() const
00635 {
00636 QString error;
00637 QCString dcopService;
00638
00639 int result = KDCOPServiceStarter::self()->findServiceFor(
00640 "DCOP/Organizer", QString::null, QString::null, &error, &dcopService );
00641
00642 if ( result == 0 ) {
00643
00644
00645
00646 static const char* const dcopObjectId = "KOrganizerIface";
00647 QCString dummy;
00648 if ( !kapp->dcopClient()->findObject(
00649 dcopService, dcopObjectId, "", QByteArray(), dummy, dummy ) ) {
00650 DCOPRef ref( dcopService, dcopService );
00651 DCOPReply reply = ref.call( "load()" );
00652 if ( reply.isValid() && (bool)reply ) {
00653 Q_ASSERT( kapp->dcopClient()->findObject(
00654 dcopService, dcopObjectId, "", QByteArray(), dummy, dummy ) );
00655 } else {
00656 kdWarning() << "Error loading " << dcopService << endl;
00657 }
00658 }
00659
00660
00661 return true;
00662
00663 } else {
00664 kdWarning() << "Couldn't start DCOP/Organizer: " << dcopService
00665 << " " << error << endl;
00666 }
00667 return false;
00668 }