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