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