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 #include <qtooltip.h>
00026 #include <qlayout.h>
00027 #include <qlabel.h>
00028 #include <qcombobox.h>
00029 #include <qpushbutton.h>
00030 #include <qvaluevector.h>
00031 #include <qwhatsthis.h>
00032
00033 #include <kdebug.h>
00034 #include <klocale.h>
00035 #include <kiconloader.h>
00036 #include <kmessagebox.h>
00037
00038 #ifndef KORG_NOKABC
00039 #include <kabc/addresseedialog.h>
00040 #include <kabc/vcardconverter.h>
00041 #include <libkdepim/addressesdialog.h>
00042 #include <libkdepim/addresseelineedit.h>
00043 #include <libkdepim/distributionlist.h>
00044 #include <kabc/stdaddressbook.h>
00045 #endif
00046
00047 #include <libkcal/event.h>
00048 #include <libkcal/freebusy.h>
00049
00050 #include <libemailfunctions/email.h>
00051
00052 #include <kdgantt/KDGanttView.h>
00053 #include <kdgantt/KDGanttViewTaskItem.h>
00054 #include <kdgantt/KDGanttViewSubwidgets.h>
00055
00056 #include "koprefs.h"
00057 #include "koglobals.h"
00058 #include "kogroupware.h"
00059 #include "freebusymanager.h"
00060 #include "freebusyurldialog.h"
00061
00062 #include "koeditorfreebusy.h"
00063
00064
00065
00066
00067
00068
00069 class FreeBusyItem : public KDGanttViewTaskItem
00070 {
00071 public:
00072 FreeBusyItem( Attendee *attendee, KDGanttView *parent ) :
00073 KDGanttViewTaskItem( parent, parent->lastItem() ), mAttendee( attendee ), mTimerID( 0 ),
00074 mIsDownloading( false )
00075 {
00076 Q_ASSERT( attendee );
00077 updateItem();
00078 setFreeBusyPeriods( 0 );
00079 setDisplaySubitemsAsGroup( true );
00080 if ( listView () )
00081 listView ()->setRootIsDecorated( false );
00082 }
00083 ~FreeBusyItem() {}
00084
00085 void updateItem();
00086
00087 Attendee *attendee() const { return mAttendee; }
00088 void setFreeBusy( KCal::FreeBusy *fb ) { mFreeBusy = fb; }
00089 KCal::FreeBusy* freeBusy() const { return mFreeBusy; }
00090
00091 void setFreeBusyPeriods( FreeBusy *fb );
00092
00093 QString key( int column, bool ) const
00094 {
00095 QMap<int,QString>::ConstIterator it = mKeyMap.find( column );
00096 if ( it == mKeyMap.end() ) return listViewText( column );
00097 else return *it;
00098 }
00099
00100 void setSortKey( int column, const QString &key )
00101 {
00102 mKeyMap.insert( column, key );
00103 }
00104
00105 QString email() const { return mAttendee->email(); }
00106 void setUpdateTimerID( int id ) { mTimerID = id; }
00107 int updateTimerID() const { return mTimerID; }
00108
00109 void startDownload( bool forceDownload ) {
00110 mIsDownloading = true;
00111 FreeBusyManager *m = KOGroupware::instance()->freeBusyManager();
00112 if ( !m->retrieveFreeBusy( attendee()->email(), forceDownload ) )
00113 mIsDownloading = false;
00114 }
00115 void setIsDownloading( bool d ) { mIsDownloading = d; }
00116 bool isDownloading() const { return mIsDownloading; }
00117
00118 private:
00119 Attendee *mAttendee;
00120 KCal::FreeBusy *mFreeBusy;
00121
00122 QMap<int,QString> mKeyMap;
00123
00124
00125 int mTimerID;
00126
00127
00128 bool mIsDownloading;
00129 };
00130
00131 void FreeBusyItem::updateItem()
00132 {
00133 setListViewText( 0, mAttendee->fullName() );
00134 switch ( mAttendee->status() ) {
00135 case Attendee::Accepted:
00136 setPixmap( 0, KOGlobals::self()->smallIcon( "ok" ) );
00137 break;
00138 case Attendee::Declined:
00139 setPixmap( 0, KOGlobals::self()->smallIcon( "no" ) );
00140 break;
00141 case Attendee::NeedsAction:
00142 case Attendee::InProcess:
00143 setPixmap( 0, KOGlobals::self()->smallIcon( "help" ) );
00144 break;
00145 case Attendee::Tentative:
00146 setPixmap( 0, KOGlobals::self()->smallIcon( "apply" ) );
00147 break;
00148 case Attendee::Delegated:
00149 setPixmap( 0, KOGlobals::self()->smallIcon( "mail_forward" ) );
00150 break;
00151 default:
00152 setPixmap( 0, QPixmap() );
00153 }
00154 }
00155
00156
00157
00158 void FreeBusyItem::setFreeBusyPeriods( FreeBusy* fb )
00159 {
00160 if( fb ) {
00161
00162 for( KDGanttViewItem* it = firstChild(); it; it = firstChild() )
00163 delete it;
00164
00165
00166 QValueList<KCal::Period> busyPeriods = fb->busyPeriods();
00167 for( QValueList<KCal::Period>::Iterator it = busyPeriods.begin();
00168 it != busyPeriods.end(); ++it ) {
00169 KDGanttViewTaskItem* newSubItem = new KDGanttViewTaskItem( this );
00170 newSubItem->setStartTime( (*it).start() );
00171 newSubItem->setEndTime( (*it).end() );
00172 newSubItem->setColors( Qt::red, Qt::red, Qt::red );
00173 QString toolTip;
00174 if ( !(*it).summary().isEmpty() )
00175 toolTip += "<b>" + (*it).summary() + "</b><br/>";
00176 if ( !(*it).location().isEmpty() )
00177 toolTip += i18n( "Location: %1" ).arg( (*it).location() );
00178 if ( !toolTip.isEmpty() )
00179 newSubItem->setTooltipText( toolTip );
00180 }
00181 setFreeBusy( fb );
00182 setShowNoInformation( false );
00183 } else {
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197 setFreeBusy( 0 );
00198 setShowNoInformation( true );
00199 }
00200
00201
00202 mIsDownloading = false;
00203 }
00204
00206
00207 KOEditorFreeBusy::KOEditorFreeBusy( int spacing, QWidget *parent,
00208 const char *name )
00209 : KOAttendeeEditor( parent, name )
00210 {
00211 QVBoxLayout *topLayout = new QVBoxLayout( this );
00212 topLayout->setSpacing( spacing );
00213
00214 initOrganizerWidgets( this, topLayout );
00215
00216
00217
00218 mIsOrganizer = false;
00219 mStatusSummaryLabel = new QLabel( this );
00220 mStatusSummaryLabel->setPalette( QToolTip::palette() );
00221 mStatusSummaryLabel->setFrameStyle( QFrame::Plain | QFrame::Box );
00222 mStatusSummaryLabel->setLineWidth( 1 );
00223 mStatusSummaryLabel->hide();
00224 topLayout->addWidget( mStatusSummaryLabel );
00225
00226
00227 QBoxLayout *controlLayout = new QHBoxLayout( topLayout );
00228
00229 QString whatsThis = i18n("Sets the zoom level on the Gantt chart. "
00230 "'Hour' shows a range of several hours, "
00231 "'Day' shows a range of a few days, "
00232 "'Week' shows a range of a few months, "
00233 "and 'Month' shows a range of a few years, "
00234 "while 'Automatic' selects the range most "
00235 "appropriate for the current event or to-do.");
00236 QLabel *label = new QLabel( i18n( "Scale: " ), this );
00237 QWhatsThis::add( label, whatsThis );
00238 controlLayout->addWidget( label );
00239
00240 scaleCombo = new QComboBox( this );
00241 QWhatsThis::add( scaleCombo, whatsThis );
00242 scaleCombo->insertItem( i18n( "Hour" ) );
00243 scaleCombo->insertItem( i18n( "Day" ) );
00244 scaleCombo->insertItem( i18n( "Week" ) );
00245 scaleCombo->insertItem( i18n( "Month" ) );
00246 scaleCombo->insertItem( i18n( "Automatic" ) );
00247 scaleCombo->setCurrentItem( 0 );
00248 connect( scaleCombo, SIGNAL( activated( int ) ),
00249 SLOT( slotScaleChanged( int ) ) );
00250 controlLayout->addWidget( scaleCombo );
00251
00252 QPushButton *button = new QPushButton( i18n( "Center on Start" ), this );
00253 QWhatsThis::add( button,
00254 i18n("Centers the Gantt chart on the start time "
00255 "and day of this event.") );
00256 connect( button, SIGNAL( clicked() ), SLOT( slotCenterOnStart() ) );
00257 controlLayout->addWidget( button );
00258
00259 controlLayout->addStretch( 1 );
00260
00261 button = new QPushButton( i18n( "Pick Date" ), this );
00262 QWhatsThis::add( button,
00263 i18n("Moves the event to a date and time when all the "
00264 "attendees are free.") );
00265 connect( button, SIGNAL( clicked() ), SLOT( slotPickDate() ) );
00266 controlLayout->addWidget( button );
00267
00268 controlLayout->addStretch( 1 );
00269
00270 button = new QPushButton( i18n("Reload"), this );
00271 QWhatsThis::add( button,
00272 i18n("Reloads Free/Busy data for all attendees from "
00273 "the corresponding servers.") );
00274 controlLayout->addWidget( button );
00275 connect( button, SIGNAL( clicked() ), SLOT( manualReload() ) );
00276
00277 mGanttView = new KDGanttView( this, "mGanttView" );
00278 QWhatsThis::add( mGanttView,
00279 i18n("Shows the free/busy status of all attendees. "
00280 "Double-clicking on an attendees entry in the "
00281 "list will allow you to enter the location of their "
00282 "Free/Busy Information.") );
00283 topLayout->addWidget( mGanttView );
00284
00285 mGanttView->removeColumn( 0 );
00286 mGanttView->addColumn( i18n("Attendee") );
00287 if ( KOPrefs::instance()->mCompactDialogs ) {
00288 mGanttView->setFixedHeight( 78 );
00289 }
00290 mGanttView->setHeaderVisible( true );
00291 mGanttView->setScale( KDGanttView::Hour );
00292 mGanttView->setShowHeaderPopupMenu( false, false, false, false, false, false );
00293
00294
00295 QDateTime horizonStart = QDateTime( QDateTime::currentDateTime()
00296 .addDays( -15 ).date() );
00297 QDateTime horizonEnd = QDateTime::currentDateTime().addDays( 15 );
00298 mGanttView->setHorizonStart( horizonStart );
00299 mGanttView->setHorizonEnd( horizonEnd );
00300 mGanttView->setCalendarMode( true );
00301
00302 mGanttView->setShowLegendButton( false );
00303
00304 mGanttView->centerTimelineAfterShow( QDateTime::currentDateTime() );
00305 if ( KGlobal::locale()->use12Clock() )
00306 mGanttView->setHourFormat( KDGanttView::Hour_12 );
00307 else
00308 mGanttView->setHourFormat( KDGanttView::Hour_24_FourDigit );
00309
00310
00311 mEventRectangle = new KDIntervalColorRectangle( mGanttView );
00312 mEventRectangle->setColor( Qt::magenta );
00313 mGanttView->addIntervalBackgroundColor( mEventRectangle );
00314
00315 connect( mGanttView, SIGNAL ( timeIntervalSelected( const QDateTime &,
00316 const QDateTime & ) ),
00317 mGanttView, SLOT( zoomToSelection( const QDateTime &,
00318 const QDateTime & ) ) );
00319 connect( mGanttView, SIGNAL( lvItemDoubleClicked( KDGanttViewItem * ) ),
00320 SLOT( editFreeBusyUrl( KDGanttViewItem * ) ) );
00321 connect( mGanttView, SIGNAL( intervalColorRectangleMoved( const QDateTime&, const QDateTime& ) ),
00322 this, SLOT( slotIntervalColorRectangleMoved( const QDateTime&, const QDateTime& ) ) );
00323
00324 connect( mGanttView, SIGNAL(lvSelectionChanged(KDGanttViewItem*)),
00325 this, SLOT(updateAttendeeInput()) );
00326 connect( mGanttView, SIGNAL(lvItemLeftClicked(KDGanttViewItem*)),
00327 this, SLOT(showAttendeeStatusMenu()) );
00328 connect( mGanttView, SIGNAL(lvItemRightClicked(KDGanttViewItem*)),
00329 this, SLOT(showAttendeeStatusMenu()) );
00330 connect( mGanttView, SIGNAL(lvMouseButtonClicked(int, KDGanttViewItem*, const QPoint&, int)),
00331 this, SLOT(listViewClicked(int, KDGanttViewItem*)) );
00332
00333 FreeBusyManager *m = KOGroupware::instance()->freeBusyManager();
00334 connect( m, SIGNAL( freeBusyRetrieved( KCal::FreeBusy *, const QString & ) ),
00335 SLOT( slotInsertFreeBusy( KCal::FreeBusy *, const QString & ) ) );
00336
00337 connect( &mReloadTimer, SIGNAL( timeout() ), SLOT( autoReload() ) );
00338
00339 initEditWidgets( this, topLayout );
00340 connect( mRemoveButton, SIGNAL(clicked()),
00341 SLOT(removeAttendee()) );
00342
00343 slotOrganizerChanged( mOrganizerCombo->currentText() );
00344 connect( mOrganizerCombo, SIGNAL( activated(const QString&) ),
00345 this, SLOT( slotOrganizerChanged(const QString&) ) );
00346
00347
00348 mGanttView->timeHeaderWidget()->installEventFilter( this );
00349 }
00350
00351 KOEditorFreeBusy::~KOEditorFreeBusy()
00352 {
00353 }
00354
00355 void KOEditorFreeBusy::removeAttendee( Attendee *attendee )
00356 {
00357 FreeBusyItem *anItem =
00358 static_cast<FreeBusyItem *>( mGanttView->firstChild() );
00359 while( anItem ) {
00360 if( anItem->attendee() == attendee ) {
00361 if ( anItem->updateTimerID() != 0 )
00362 killTimer( anItem->updateTimerID() );
00363 delete anItem;
00364 updateStatusSummary();
00365 break;
00366 }
00367 anItem = static_cast<FreeBusyItem *>( anItem->nextSibling() );
00368 }
00369 }
00370
00371 void KOEditorFreeBusy::insertAttendee( Attendee *attendee, bool readFBList )
00372 {
00373 FreeBusyItem* item = new FreeBusyItem( attendee, mGanttView );
00374 if ( readFBList )
00375 updateFreeBusyData( item );
00376 else {
00377 clearSelection();
00378 mGanttView->setSelected( item, true );
00379 }
00380 updateStatusSummary();
00381 emit updateAttendeeSummary( mGanttView->childCount() );
00382 }
00383
00384 void KOEditorFreeBusy::clearAttendees()
00385 {
00386 mGanttView->clear();
00387 }
00388
00389
00390 void KOEditorFreeBusy::setUpdateEnabled( bool enabled )
00391 {
00392 mGanttView->setUpdateEnabled( enabled );
00393 }
00394
00395 bool KOEditorFreeBusy::updateEnabled() const
00396 {
00397 return mGanttView->getUpdateEnabled();
00398 }
00399
00400
00401 void KOEditorFreeBusy::readEvent( Event *event )
00402 {
00403 bool block = updateEnabled();
00404 setUpdateEnabled( false );
00405 clearAttendees();
00406
00407 setDateTimes( event->dtStart(), event->dtEnd() );
00408 mIsOrganizer = KOPrefs::instance()->thatIsMe( event->organizer().email() );
00409 updateStatusSummary();
00410 clearSelection();
00411 KOAttendeeEditor::readEvent( event );
00412
00413 setUpdateEnabled( block );
00414 emit updateAttendeeSummary( mGanttView->childCount() );
00415 }
00416
00417 void KOEditorFreeBusy::slotIntervalColorRectangleMoved( const QDateTime& start, const QDateTime& end )
00418 {
00419 kdDebug() << k_funcinfo << "slotIntervalColorRectangleMoved " << start << "," << end << endl;
00420 mDtStart = start;
00421 mDtEnd = end;
00422 emit dateTimesChanged( start, end );
00423 }
00424
00425 void KOEditorFreeBusy::setDateTimes( const QDateTime &start, const QDateTime &end )
00426 {
00427 slotUpdateGanttView( start, end );
00428 }
00429
00430 void KOEditorFreeBusy::slotScaleChanged( int newScale )
00431 {
00432
00433 KDGanttView::Scale scale = static_cast<KDGanttView::Scale>( newScale+1 );
00434 mGanttView->setScale( scale );
00435 slotCenterOnStart();
00436 }
00437
00438 void KOEditorFreeBusy::slotCenterOnStart()
00439 {
00440 mGanttView->centerTimeline( mDtStart );
00441 }
00442
00443 void KOEditorFreeBusy::slotZoomToTime()
00444 {
00445 mGanttView->zoomToFit();
00446 }
00447
00448 void KOEditorFreeBusy::updateFreeBusyData( FreeBusyItem* item )
00449 {
00450 if ( item->isDownloading() )
00451
00452 return;
00453
00454 if ( item->updateTimerID() != 0 )
00455
00456 killTimer( item->updateTimerID() );
00457
00458
00459
00460 item->setUpdateTimerID( startTimer( 5000 ) );
00461 }
00462
00463 void KOEditorFreeBusy::timerEvent( QTimerEvent* event )
00464 {
00465 killTimer( event->timerId() );
00466 FreeBusyItem *item = static_cast<FreeBusyItem *>( mGanttView->firstChild() );
00467 while( item ) {
00468 if( item->updateTimerID() == event->timerId() ) {
00469 item->setUpdateTimerID( 0 );
00470 item->startDownload( mForceDownload );
00471 return;
00472 }
00473 item = static_cast<FreeBusyItem *>( item->nextSibling() );
00474 }
00475 }
00476
00477
00478
00479 void KOEditorFreeBusy::slotInsertFreeBusy( KCal::FreeBusy *fb,
00480 const QString &email )
00481 {
00482 kdDebug(5850) << "KOEditorFreeBusy::slotInsertFreeBusy() " << email << endl;
00483
00484 if( fb )
00485 fb->sortList();
00486 bool block = mGanttView->getUpdateEnabled();
00487 mGanttView->setUpdateEnabled( false );
00488 for( KDGanttViewItem *it = mGanttView->firstChild(); it;
00489 it = it->nextSibling() ) {
00490 FreeBusyItem *item = static_cast<FreeBusyItem *>( it );
00491 if( item->email() == email )
00492 item->setFreeBusyPeriods( fb );
00493 }
00494 mGanttView->setUpdateEnabled( block );
00495 }
00496
00497
00502 void KOEditorFreeBusy::slotUpdateGanttView( const QDateTime &dtFrom, const QDateTime &dtTo )
00503 {
00504 mDtStart = dtFrom;
00505 mDtEnd = dtTo;
00506 bool block = mGanttView->getUpdateEnabled( );
00507 mGanttView->setUpdateEnabled( false );
00508 QDateTime horizonStart = QDateTime( dtFrom.addDays( -15 ).date() );
00509 mGanttView->setHorizonStart( horizonStart );
00510 mGanttView->setHorizonEnd( dtTo.addDays( 15 ) );
00511 mEventRectangle->setDateTimes( dtFrom, dtTo );
00512 mGanttView->setUpdateEnabled( block );
00513 mGanttView->centerTimelineAfterShow( dtFrom );
00514 }
00515
00516
00520 void KOEditorFreeBusy::slotPickDate()
00521 {
00522 QDateTime start = mDtStart;
00523 QDateTime end = mDtEnd;
00524 bool success = findFreeSlot( start, end );
00525
00526 if( success ) {
00527 if ( start == mDtStart && end == mDtEnd ) {
00528 KMessageBox::information( this,
00529 i18n( "The meeting already has suitable start/end times." ), QString::null,
00530 "MeetingTimeOKFreeBusy" );
00531 } else {
00532 emit dateTimesChanged( start, end );
00533 slotUpdateGanttView( start, end );
00534 KMessageBox::information( this,
00535 i18n( "The meeting has been moved to\nStart: %1\nEnd: %2." )
00536 .arg( start.toString() ).arg( end.toString() ), QString::null,
00537 "MeetingMovedFreeBusy" );
00538 }
00539 } else
00540 KMessageBox::sorry( this, i18n( "No suitable date found." ) );
00541 }
00542
00543
00548 bool KOEditorFreeBusy::findFreeSlot( QDateTime &dtFrom, QDateTime &dtTo )
00549 {
00550 if( tryDate( dtFrom, dtTo ) )
00551
00552 return true;
00553
00554 QDateTime tryFrom = dtFrom;
00555 QDateTime tryTo = dtTo;
00556
00557
00558
00559 if( tryFrom < QDateTime::currentDateTime() ) {
00560
00561 int secs = tryFrom.secsTo( tryTo );
00562 tryFrom = QDateTime::currentDateTime();
00563 tryTo = tryFrom.addSecs( secs );
00564 }
00565
00566 bool found = false;
00567 while( !found ) {
00568 found = tryDate( tryFrom, tryTo );
00569
00570 if( !found && dtFrom.daysTo( tryFrom ) > 365 )
00571 break;
00572 }
00573
00574 dtFrom = tryFrom;
00575 dtTo = tryTo;
00576
00577 return found;
00578 }
00579
00580
00589 bool KOEditorFreeBusy::tryDate( QDateTime& tryFrom, QDateTime& tryTo )
00590 {
00591 FreeBusyItem* currentItem = static_cast<FreeBusyItem*>( mGanttView->firstChild() );
00592 while( currentItem ) {
00593 if( !tryDate( currentItem, tryFrom, tryTo ) ) {
00594
00595 return false;
00596 }
00597
00598 currentItem = static_cast<FreeBusyItem*>( currentItem->nextSibling() );
00599 }
00600
00601 return true;
00602 }
00603
00611 bool KOEditorFreeBusy::tryDate( FreeBusyItem *attendee,
00612 QDateTime &tryFrom, QDateTime &tryTo )
00613 {
00614
00615
00616
00617 KCal::FreeBusy *fb = attendee->freeBusy();
00618 if( !fb )
00619 return true;
00620
00621 QValueList<KCal::Period> busyPeriods = fb->busyPeriods();
00622 for( QValueList<KCal::Period>::Iterator it = busyPeriods.begin();
00623 it != busyPeriods.end(); ++it ) {
00624 if( (*it).end() <= tryFrom ||
00625 (*it).start() >= tryTo )
00626 continue;
00627 else {
00628
00629
00630 int secsDuration = tryFrom.secsTo( tryTo );
00631 tryFrom = (*it).end();
00632 tryTo = tryFrom.addSecs( secsDuration );
00633
00634 tryDate( attendee, tryFrom, tryTo );
00635
00636 return false;
00637 }
00638 }
00639
00640 return true;
00641 }
00642
00643 void KOEditorFreeBusy::updateStatusSummary()
00644 {
00645 FreeBusyItem *aItem =
00646 static_cast<FreeBusyItem *>( mGanttView->firstChild() );
00647 int total = 0;
00648 int accepted = 0;
00649 int tentative = 0;
00650 int declined = 0;
00651 while( aItem ) {
00652 ++total;
00653 switch( aItem->attendee()->status() ) {
00654 case Attendee::Accepted:
00655 ++accepted;
00656 break;
00657 case Attendee::Tentative:
00658 ++tentative;
00659 break;
00660 case Attendee::Declined:
00661 ++declined;
00662 break;
00663 case Attendee::NeedsAction:
00664 case Attendee::Delegated:
00665 case Attendee::Completed:
00666 case Attendee::InProcess:
00667
00668 break;
00669 }
00670 aItem = static_cast<FreeBusyItem *>( aItem->nextSibling() );
00671 }
00672 if( total > 1 && mIsOrganizer ) {
00673 mStatusSummaryLabel->show();
00674 mStatusSummaryLabel->setText(
00675 i18n( "Of the %1 participants, %2 have accepted, %3"
00676 " have tentatively accepted, and %4 have declined.")
00677 .arg( total ).arg( accepted ).arg( tentative ).arg( declined ) );
00678 } else {
00679 mStatusSummaryLabel->hide();
00680 }
00681 mStatusSummaryLabel->adjustSize();
00682 }
00683
00684 void KOEditorFreeBusy::triggerReload()
00685 {
00686 mReloadTimer.start( 1000, true );
00687 }
00688
00689 void KOEditorFreeBusy::cancelReload()
00690 {
00691 mReloadTimer.stop();
00692 }
00693
00694 void KOEditorFreeBusy::manualReload()
00695 {
00696 mForceDownload = true;
00697 reload();
00698 }
00699
00700 void KOEditorFreeBusy::autoReload()
00701 {
00702 mForceDownload = false;
00703 reload();
00704 }
00705
00706 void KOEditorFreeBusy::reload()
00707 {
00708 kdDebug(5850) << "KOEditorFreeBusy::reload()" << endl;
00709
00710 FreeBusyItem *item = static_cast<FreeBusyItem *>( mGanttView->firstChild() );
00711 while( item ) {
00712 if ( mForceDownload )
00713 item->startDownload( mForceDownload );
00714 else
00715 updateFreeBusyData( item );
00716
00717 item = static_cast<FreeBusyItem *>( item->nextSibling() );
00718 }
00719 }
00720
00721 void KOEditorFreeBusy::editFreeBusyUrl( KDGanttViewItem *i )
00722 {
00723 FreeBusyItem *item = static_cast<FreeBusyItem *>( i );
00724 if ( !item ) return;
00725
00726 Attendee *attendee = item->attendee();
00727
00728 FreeBusyUrlDialog dialog( attendee, this );
00729 dialog.exec();
00730 }
00731
00732 void KOEditorFreeBusy::writeEvent(KCal::Event * event)
00733 {
00734 event->clearAttendees();
00735 QValueVector<FreeBusyItem*> toBeDeleted;
00736 for ( FreeBusyItem *item = static_cast<FreeBusyItem *>( mGanttView->firstChild() ); item;
00737 item = static_cast<FreeBusyItem*>( item->nextSibling() ) )
00738 {
00739 Attendee *attendee = item->attendee();
00740 Q_ASSERT( attendee );
00741
00742 if ( attendee->email().isEmpty() ) {
00743 KPIM::DistributionList list =
00744 KPIM::DistributionList::findByName( KABC::StdAddressBook::self(), attendee->name() );
00745 if ( !list.isEmpty() ) {
00746 toBeDeleted.push_back( item );
00747 KPIM::DistributionList::Entry::List entries = list.entries( KABC::StdAddressBook::self() );
00748 KPIM::DistributionList::Entry::List::Iterator it( entries.begin() );
00749 while ( it != entries.end() ) {
00750 KPIM::DistributionList::Entry &e = ( *it );
00751 ++it;
00752
00753 insertAttendeeFromAddressee( e.addressee, attendee );
00754
00755 }
00756 }
00757 } else {
00758 bool skip = false;
00759 if ( attendee->email().endsWith( "example.net" ) ) {
00760 if ( KMessageBox::warningYesNo( this, i18n("%1 does not look like a valid email address. "
00761 "Are you sure you want to invite this participant?").arg( attendee->email() ),
00762 i18n("Invalid email address") ) != KMessageBox::Yes ) {
00763 skip = true;
00764 }
00765 }
00766 if ( !skip ) {
00767 event->addAttendee( new Attendee( *attendee ) );
00768 }
00769 }
00770 }
00771
00772 KOAttendeeEditor::writeEvent( event );
00773
00774
00775 QValueVector<FreeBusyItem*>::iterator it;
00776 for( it = toBeDeleted.begin(); it != toBeDeleted.end(); ++it ) {
00777 delete *it;
00778 }
00779 }
00780
00781 KCal::Attendee * KOEditorFreeBusy::currentAttendee() const
00782 {
00783 KDGanttViewItem *item = mGanttView->selectedItem();
00784 FreeBusyItem *aItem = static_cast<FreeBusyItem*>( item );
00785 if ( !aItem )
00786 return 0;
00787 return aItem->attendee();
00788 }
00789
00790 void KOEditorFreeBusy::updateCurrentItem()
00791 {
00792 FreeBusyItem* item = static_cast<FreeBusyItem*>( mGanttView->selectedItem() );
00793 if ( item ) {
00794 item->updateItem();
00795 updateFreeBusyData( item );
00796 updateStatusSummary();
00797 }
00798 }
00799
00800 void KOEditorFreeBusy::removeAttendee()
00801 {
00802 FreeBusyItem *item = static_cast<FreeBusyItem*>( mGanttView->selectedItem() );
00803 if ( !item )
00804 return;
00805
00806 Attendee *delA = new Attendee( item->attendee()->name(), item->attendee()->email(),
00807 item->attendee()->RSVP(), item->attendee()->status(),
00808 item->attendee()->role(), item->attendee()->uid() );
00809 mdelAttendees.append( delA );
00810 delete item;
00811
00812 updateStatusSummary();
00813 updateAttendeeInput();
00814 emit updateAttendeeSummary( mGanttView->childCount() );
00815 }
00816
00817 void KOEditorFreeBusy::clearSelection() const
00818 {
00819 KDGanttViewItem *item = mGanttView->selectedItem();
00820 if ( item )
00821 mGanttView->setSelected( item, false );
00822 mGanttView->repaint();
00823 item->repaint();
00824 }
00825
00826 void KOEditorFreeBusy::changeStatusForMe(KCal::Attendee::PartStat status)
00827 {
00828 const QStringList myEmails = KOPrefs::instance()->allEmails();
00829 for ( FreeBusyItem *item = static_cast<FreeBusyItem *>( mGanttView->firstChild() ); item;
00830 item = static_cast<FreeBusyItem*>( item->nextSibling() ) )
00831 {
00832 for ( QStringList::ConstIterator it2( myEmails.begin() ), end( myEmails.end() ); it2 != end; ++it2 ) {
00833 if ( item->attendee()->email() == *it2 ) {
00834 item->attendee()->setStatus( status );
00835 item->updateItem();
00836 }
00837 }
00838 }
00839 }
00840
00841 void KOEditorFreeBusy::showAttendeeStatusMenu()
00842 {
00843 if ( mGanttView->mapFromGlobal( QCursor::pos() ).x() > 22 )
00844 return;
00845 QPopupMenu popup;
00846 popup.insertItem( SmallIcon( "help" ), Attendee::statusName( Attendee::NeedsAction ), Attendee::NeedsAction );
00847 popup.insertItem( KOGlobals::self()->smallIcon( "ok" ), Attendee::statusName( Attendee::Accepted ), Attendee::Accepted );
00848 popup.insertItem( KOGlobals::self()->smallIcon( "no" ), Attendee::statusName( Attendee::Declined ), Attendee::Declined );
00849 popup.insertItem( KOGlobals::self()->smallIcon( "apply" ), Attendee::statusName( Attendee::Tentative ), Attendee::Tentative );
00850 popup.insertItem( KOGlobals::self()->smallIcon( "mail_forward" ), Attendee::statusName( Attendee::Delegated ), Attendee::Delegated );
00851 popup.insertItem( Attendee::statusName( Attendee::Completed ), Attendee::Completed );
00852 popup.insertItem( KOGlobals::self()->smallIcon( "help" ), Attendee::statusName( Attendee::InProcess ), Attendee::InProcess );
00853 popup.setItemChecked( currentAttendee()->status(), true );
00854 int status = popup.exec( QCursor::pos() );
00855 if ( status >= 0 ) {
00856 currentAttendee()->setStatus( (Attendee::PartStat)status );
00857 updateCurrentItem();
00858 updateAttendeeInput();
00859 }
00860 }
00861
00862 void KOEditorFreeBusy::listViewClicked(int button, KDGanttViewItem * item)
00863 {
00864 if ( button == Qt::LeftButton && item == 0 )
00865 addNewAttendee();
00866 }
00867
00868 void KOEditorFreeBusy::slotOrganizerChanged(const QString & newOrganizer)
00869 {
00870 if (newOrganizer==mCurrentOrganizer) return;
00871
00872 QString name;
00873 QString email;
00874 bool success = KPIM::getNameAndMail( newOrganizer, name, email );
00875
00876 if (!success) return;
00877
00878
00879 Attendee *currentOrganizerAttendee = 0;
00880 Attendee *newOrganizerAttendee = 0;
00881
00882 FreeBusyItem *anItem =
00883 static_cast<FreeBusyItem *>( mGanttView->firstChild() );
00884 while( anItem ) {
00885 Attendee *attendee = anItem->attendee();
00886 if( attendee->fullName() == mCurrentOrganizer )
00887 currentOrganizerAttendee = attendee;
00888
00889 if( attendee->fullName() == newOrganizer )
00890 newOrganizerAttendee = attendee;
00891
00892 anItem = static_cast<FreeBusyItem *>( anItem->nextSibling() );
00893 }
00894
00895 int answer = KMessageBox::No;
00896
00897 if (currentOrganizerAttendee) {
00898 answer = KMessageBox::questionYesNo( this, i18n("You are changing the organiser of "
00899 "this event, who is also attending, "
00900 "do you want to change that attendee "
00901 "as well?") );
00902 } else {
00903 answer = KMessageBox::Yes;
00904 }
00905
00906 if (answer==KMessageBox::Yes) {
00907 if (currentOrganizerAttendee) {
00908 removeAttendee( currentOrganizerAttendee );
00909 }
00910
00911 if (!newOrganizerAttendee) {
00912 Attendee *a = new Attendee( name, email, true );
00913 insertAttendee( a, false );
00914 updateAttendee();
00915 }
00916 }
00917
00918 mCurrentOrganizer = newOrganizer;
00919 }
00920
00921 bool KOEditorFreeBusy::eventFilter( QObject *watched, QEvent *event )
00922 {
00923 if ( watched == mGanttView->timeHeaderWidget() &&
00924 event->type() >= QEvent::MouseButtonPress && event->type() <= QEvent::MouseMove ) {
00925 return true;
00926 } else {
00927 return KOAttendeeEditor::eventFilter( watched, event );
00928 }
00929 }
00930
00931 QListViewItem* KOEditorFreeBusy::hasExampleAttendee() const
00932 {
00933 for ( FreeBusyItem *item = static_cast<FreeBusyItem *>( mGanttView->firstChild() ); item;
00934 item = static_cast<FreeBusyItem*>( item->nextSibling() ) ) {
00935 Attendee *attendee = item->attendee();
00936 Q_ASSERT( attendee );
00937 if ( isExampleAttendee( attendee ) )
00938 return item;
00939 }
00940 return 0;
00941 }
00942
00943 #include "koeditorfreebusy.moc"