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