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