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
00031
#include <kdebug.h>
00032
#include <klocale.h>
00033
#include <kiconloader.h>
00034
#include <kmessagebox.h>
00035
00036
#include <libkcal/event.h>
00037
#include <libkcal/freebusy.h>
00038
00039
#include <kdgantt/KDGanttView.h>
00040
#include <kdgantt/KDGanttViewTaskItem.h>
00041
00042
#include "koprefs.h"
00043
#include "koglobals.h"
00044
#include "kogroupware.h"
00045
#include "freebusymanager.h"
00046
#include "freebusyurldialog.h"
00047
00048
#include "koeditorfreebusy.h"
00049
00050
00051
00052
00053
class FreeBusyItem :
public KDGanttViewTaskItem
00054 {
00055
public:
00056 FreeBusyItem( Attendee *attendee, KDGanttView *parent ) :
00057 KDGanttViewTaskItem( parent ), mAttendee( attendee )
00058 {
00059 Q_ASSERT( attendee );
00060 updateItem();
00061 setFreeBusyPeriods( 0 );
00062 setDisplaySubitemsAsGroup(
true );
00063
if ( listView () )
00064 listView ()->setRootIsDecorated(
false );
00065 }
00066 ~FreeBusyItem() {}
00067
00068
void updateItem();
00069
00070 Attendee *attendee()
const {
return mAttendee; }
00071
void setFreeBusy( KCal::FreeBusy *fb ) { mFreeBusy = fb; }
00072 KCal::FreeBusy* freeBusy()
const {
return mFreeBusy; }
00073
00074
void setFreeBusyPeriods( FreeBusy *fb );
00075
00076
QString key(
int column,
bool )
const
00077
{
00078
QMap<int,QString>::ConstIterator it = mKeyMap.find( column );
00079
if ( it == mKeyMap.end() )
return listViewText( column );
00080
else return *it;
00081 }
00082
00083
void setSortKey(
int column,
const QString &key )
00084 {
00085 mKeyMap.insert( column, key );
00086 }
00087
00088
QString email()
const {
return mAttendee->email(); }
00089
00090
private:
00091 Attendee *mAttendee;
00092 KCal::FreeBusy *mFreeBusy;
00093
00094
QMap<int,QString> mKeyMap;
00095 };
00096
00097
void FreeBusyItem::updateItem()
00098 {
00099 setListViewText( 0, mAttendee->name() );
00100 setListViewText( 1, mAttendee->email() );
00101 setListViewText( 2, mAttendee->roleStr() );
00102 setListViewText( 3, mAttendee->statusStr() );
00103
if ( mAttendee->RSVP() && !mAttendee->email().isEmpty() )
00104 setPixmap( 4, KOGlobals::self()->smallIcon(
"mailappt" ) );
00105
else
00106 setPixmap( 4, KOGlobals::self()->smallIcon(
"nomailappt" ) );
00107 }
00108
00109
00110
00111
void FreeBusyItem::setFreeBusyPeriods( FreeBusy* fb )
00112 {
00113
if( fb ) {
00114
00115
for( KDGanttViewItem* it = firstChild(); it; it = firstChild() )
00116
delete it;
00117
00118
00119
QValueList<KCal::Period> busyPeriods = fb->busyPeriods();
00120
for(
QValueList<KCal::Period>::Iterator it = busyPeriods.begin();
00121 it != busyPeriods.end(); ++it ) {
00122 KDGanttViewTaskItem* newSubItem =
new KDGanttViewTaskItem(
this );
00123 newSubItem->setStartTime( (*it).start() );
00124 newSubItem->setEndTime( (*it).end() );
00125 newSubItem->setColors( Qt::red, Qt::red, Qt::red );
00126 }
00127 setFreeBusy( fb );
00128 setShowNoInformation(
false );
00129 }
else {
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143 setFreeBusy( 0 );
00144 setShowNoInformation(
true );
00145 }
00146 }
00147
00148
00149 KOEditorFreeBusy::KOEditorFreeBusy(
int spacing,
QWidget *parent,
00150
const char *name )
00151 :
QWidget( parent, name )
00152 {
00153
QVBoxLayout *topLayout =
new QVBoxLayout(
this );
00154 topLayout->setSpacing( spacing );
00155
00156
QString organizer = KOPrefs::instance()->email();
00157 mOrganizerLabel =
new QLabel( i18n(
"Organizer: %1").arg( organizer ),
this );
00158 mIsOrganizer =
true;
00159 topLayout->addWidget( mOrganizerLabel );
00160
00161
00162
00163 mStatusSummaryLabel =
new QLabel(
this );
00164 mStatusSummaryLabel->setPalette( QToolTip::palette() );
00165 mStatusSummaryLabel->setFrameStyle( QFrame::Plain | QFrame::Box );
00166 mStatusSummaryLabel->setLineWidth( 1 );
00167 topLayout->addWidget( mStatusSummaryLabel );
00168
00169
00170
QBoxLayout *controlLayout =
new QHBoxLayout( topLayout );
00171
00172
QLabel *label =
new QLabel( i18n(
"Scale: " ),
this );
00173 controlLayout->
addWidget( label );
00174
00175 scaleCombo =
new QComboBox(
this );
00176 scaleCombo->insertItem( i18n(
"Hour" ) );
00177 scaleCombo->insertItem( i18n(
"Day" ) );
00178 scaleCombo->insertItem( i18n(
"Week" ) );
00179 scaleCombo->insertItem( i18n(
"Month" ) );
00180 scaleCombo->insertItem( i18n(
"Automatic" ) );
00181 scaleCombo->setCurrentItem( 0 );
00182 connect( scaleCombo, SIGNAL( activated(
int ) ),
00183 SLOT( slotScaleChanged(
int ) ) );
00184 controlLayout->
addWidget( scaleCombo );
00185
00186
QPushButton *button =
new QPushButton( i18n(
"Center on Start" ),
this );
00187 connect( button, SIGNAL( clicked() ), SLOT( slotCenterOnStart() ) );
00188 controlLayout->
addWidget( button );
00189
00190 button =
new QPushButton( i18n(
"Zoom to Fit" ),
this );
00191 connect( button, SIGNAL( clicked() ), SLOT( slotZoomToTime() ) );
00192 controlLayout->
addWidget( button );
00193
00194 controlLayout->addStretch( 1 );
00195
00196 button =
new QPushButton( i18n(
"Pick Date" ),
this );
00197 connect( button, SIGNAL( clicked() ), SLOT( slotPickDate() ) );
00198 controlLayout->
addWidget( button );
00199
00200 controlLayout->addStretch( 1 );
00201
00202 button =
new QPushButton( i18n(
"Reload"),
this );
00203 controlLayout->
addWidget( button );
00204 connect( button, SIGNAL( clicked() ), SLOT( reload() ) );
00205
00206 mGanttView =
new KDGanttView(
this,
"mGanttView" );
00207 topLayout->addWidget( mGanttView );
00208
00209 mGanttView->removeColumn( 0 );
00210 mGanttView->addColumn( i18n(
"Name"), 180 );
00211 mGanttView->addColumn( i18n(
"Email"), 180 );
00212 mGanttView->addColumn( i18n(
"Role"), 60 );
00213 mGanttView->addColumn( i18n(
"Status"), 100 );
00214 mGanttView->addColumn( i18n(
"RSVP"), 35 );
00215
if ( KOPrefs::instance()->mCompactDialogs ) {
00216 mGanttView->setFixedHeight( 78 );
00217 }
00218 mGanttView->setHeaderVisible(
true );
00219 mGanttView->setScale( KDGanttView::Hour );
00220 mGanttView->setShowHeaderPopupMenu(
true,
true,
true,
false,
false,
true );
00221
00222
00223
QDateTime horizonStart =
QDateTime( QDateTime::currentDateTime()
00224 .addDays( -15 ).date() );
00225
QDateTime horizonEnd = QDateTime::currentDateTime().addDays( 15 );
00226 mGanttView->setHorizonStart( horizonStart );
00227 mGanttView->setHorizonEnd( horizonEnd );
00228 mGanttView->setCalendarMode(
true );
00229
00230 mGanttView->setShowLegendButton(
false );
00231
00232 mGanttView->centerTimelineAfterShow( QDateTime::currentDateTime() );
00233
if ( KGlobal::locale()->use12Clock() )
00234 mGanttView->setHourFormat( KDGanttView::Hour_12 );
00235
else
00236 mGanttView->setHourFormat( KDGanttView::Hour_24_FourDigit );
00237 connect( mGanttView, SIGNAL ( timeIntervalSelected(
const QDateTime &,
00238
const QDateTime & ) ),
00239 mGanttView, SLOT( zoomToSelection(
const QDateTime &,
00240
const QDateTime & ) ) );
00241
00242
00243 connect( mGanttView, SIGNAL( lvItemDoubleClicked( KDGanttViewItem * ) ),
00244 SLOT( editFreeBusyUrl( KDGanttViewItem * ) ) );
00245
00246 FreeBusyManager *m = KOGroupware::instance()->freeBusyManager();
00247 connect( m, SIGNAL( freeBusyRetrieved( KCal::FreeBusy *,
const QString & ) ),
00248 SLOT( slotInsertFreeBusy( KCal::FreeBusy *,
const QString & ) ) );
00249
00250 connect( &mReloadTimer, SIGNAL( timeout() ), SLOT( reload() ) );
00251 }
00252
00253 KOEditorFreeBusy::~KOEditorFreeBusy()
00254 {
00255 }
00256
00257
void KOEditorFreeBusy::removeAttendee( Attendee *attendee )
00258 {
00259 FreeBusyItem *anItem =
00260 static_cast<FreeBusyItem *>( mGanttView->firstChild() );
00261
while( anItem ) {
00262
if( anItem->attendee() == attendee ) {
00263
delete anItem;
00264 updateStatusSummary();
00265
break;
00266 }
00267 anItem = static_cast<FreeBusyItem *>( anItem->nextSibling() );
00268 }
00269 }
00270
00271
void KOEditorFreeBusy::insertAttendee( Attendee *attendee )
00272 {
00273 (
void)
new FreeBusyItem( attendee, mGanttView );
00274
#if 0
00275
updateFreeBusyData( attendee );
00276
#endif
00277
updateStatusSummary();
00278 }
00279
00280
void KOEditorFreeBusy::updateAttendee( Attendee *attendee )
00281 {
00282 FreeBusyItem *anItem =
00283 static_cast<FreeBusyItem *>( mGanttView->firstChild() );
00284
while( anItem ) {
00285
if( anItem->attendee() == attendee ) {
00286 anItem->updateItem();
00287 updateFreeBusyData( attendee );
00288 updateStatusSummary();
00289
break;
00290 }
00291 anItem = static_cast<FreeBusyItem *>( anItem->nextSibling() );
00292 }
00293 }
00294
00295
void KOEditorFreeBusy::clearAttendees()
00296 {
00297 mGanttView->clear();
00298 }
00299
00300
00301
void KOEditorFreeBusy::setUpdateEnabled(
bool enabled )
00302 {
00303 mGanttView->setUpdateEnabled( enabled );
00304 }
00305
00306
bool KOEditorFreeBusy::updateEnabled()
const
00307
{
00308
return mGanttView->getUpdateEnabled();
00309 }
00310
00311
00312
void KOEditorFreeBusy::readEvent( Event *event )
00313 {
00314 setDateTimes( event->dtStart(), event->dtEnd() );
00315 }
00316
00317
00318
void KOEditorFreeBusy::setDateTimes( QDateTime start, QDateTime end )
00319 {
00320 mDtStart = start;
00321 mDtEnd = end;
00322
00323 mGanttView->centerTimelineAfterShow( start );
00324 mGanttView->clearBackgroundColor();
00325 mGanttView->setIntervalBackgroundColor( start, end, Qt::magenta );
00326 }
00327
00328
void KOEditorFreeBusy::slotScaleChanged(
int newScale )
00329 {
00330
00331 KDGanttView::Scale scale = static_cast<KDGanttView::Scale>( newScale+1 );
00332 mGanttView->setScale( scale );
00333 slotCenterOnStart();
00334 }
00335
00336
void KOEditorFreeBusy::slotCenterOnStart()
00337 {
00338 mGanttView->centerTimeline( mDtStart );
00339 }
00340
00341
void KOEditorFreeBusy::slotZoomToTime()
00342 {
00343 mGanttView->zoomToFit();
00344 }
00345
00346
void KOEditorFreeBusy::updateFreeBusyData( KDGanttViewItem *item )
00347 {
00348 FreeBusyItem *g = static_cast<FreeBusyItem *>( item );
00349 updateFreeBusyData( g->attendee() );
00350 }
00351
00352
void KOEditorFreeBusy::updateFreeBusyData( Attendee *attendee )
00353 {
00354
if( KOGroupware::instance() && attendee->name() !=
"(EmptyName)" ) {
00355 FreeBusyManager *m = KOGroupware::instance()->freeBusyManager();
00356 m->retrieveFreeBusy( attendee->email() );
00357 }
00358 }
00359
00360
00361
00362
void KOEditorFreeBusy::slotInsertFreeBusy( KCal::FreeBusy *fb,
00363
const QString &email )
00364 {
00365 kdDebug() <<
"KOEditorFreeBusy::slotInsertFreeBusy() " << email << endl;
00366
00367
if( fb )
00368 fb->sortList();
00369
bool block = mGanttView->getUpdateEnabled();
00370 mGanttView->setUpdateEnabled(
false );
00371
for( KDGanttViewItem *it = mGanttView->firstChild(); it;
00372 it = it->nextSibling() ) {
00373 FreeBusyItem *item = static_cast<FreeBusyItem *>( it );
00374
if( item->email() == email )
00375 item->setFreeBusyPeriods( fb );
00376 }
00377 mGanttView->setUpdateEnabled( block );
00378 }
00379
00380
00385
void KOEditorFreeBusy::slotUpdateGanttView( QDateTime dtFrom, QDateTime dtTo )
00386 {
00387
bool block = mGanttView->getUpdateEnabled( );
00388 mGanttView->setUpdateEnabled(
false );
00389 QDateTime horizonStart = QDateTime( dtFrom.addDays( -15 ).date() );
00390 mGanttView->setHorizonStart( horizonStart );
00391 mGanttView->setHorizonEnd( dtTo.addDays( 15 ) );
00392 mGanttView->clearBackgroundColor();
00393 mGanttView->setIntervalBackgroundColor( dtFrom, dtTo, Qt::magenta );
00394 mGanttView->setUpdateEnabled( block );
00395 mGanttView->centerTimelineAfterShow( dtFrom );
00396 }
00397
00398
00402
void KOEditorFreeBusy::slotPickDate()
00403 {
00404 QDateTime start = mDtStart;
00405 QDateTime end = mDtEnd;
00406
bool success = findFreeSlot( start, end );
00407
00408
if( success ) {
00409
if ( start == mDtStart && end == mDtEnd ) {
00410 KMessageBox::information(
this,
00411 i18n(
"The meeting has already suitable start/end times." ));
00412 }
else {
00413 emit dateTimesChanged( start, end );
00414 slotUpdateGanttView( start, end );
00415 KMessageBox::information(
this,
00416 i18n(
"The meeting has been moved to\nStart: %1\nEnd: %2." )
00417 .arg( start.toString() ).arg( end.toString() ) );
00418 }
00419 }
else
00420 KMessageBox::sorry(
this, i18n(
"No suitable date found." ) );
00421 }
00422
00423
00428
bool KOEditorFreeBusy::findFreeSlot( QDateTime &dtFrom, QDateTime &dtTo )
00429 {
00430
if( tryDate( dtFrom, dtTo ) )
00431
00432
return true;
00433
00434 QDateTime tryFrom = dtFrom;
00435 QDateTime tryTo = dtTo;
00436
00437
00438
00439
if( tryFrom < QDateTime::currentDateTime() ) {
00440
00441
int secs = tryFrom.secsTo( tryTo );
00442 tryFrom = QDateTime::currentDateTime();
00443 tryTo = tryFrom.addSecs( secs );
00444 }
00445
00446
bool found =
false;
00447
while( !found ) {
00448 found = tryDate( tryFrom, tryTo );
00449
00450
if( !found && dtFrom.daysTo( tryFrom ) > 365 )
00451
break;
00452 }
00453
00454 dtFrom = tryFrom;
00455 dtTo = tryTo;
00456
00457
return found;
00458 }
00459
00460
00469
bool KOEditorFreeBusy::tryDate( QDateTime& tryFrom, QDateTime& tryTo )
00470 {
00471 FreeBusyItem* currentItem = static_cast<FreeBusyItem*>( mGanttView->firstChild() );
00472
while( currentItem ) {
00473
if( !tryDate( currentItem, tryFrom, tryTo ) ) {
00474
00475
return false;
00476 }
00477
00478 currentItem = static_cast<FreeBusyItem*>( currentItem->nextSibling() );
00479 }
00480
00481
return true;
00482 }
00483
00491
bool KOEditorFreeBusy::tryDate( FreeBusyItem *attendee,
00492 QDateTime &tryFrom, QDateTime &tryTo )
00493 {
00494
00495
00496
00497 KCal::FreeBusy *fb = attendee->freeBusy();
00498
if( !fb )
00499
return true;
00500
00501
QValueList<KCal::Period> busyPeriods = fb->busyPeriods();
00502
for(
QValueList<KCal::Period>::Iterator it = busyPeriods.begin();
00503 it != busyPeriods.end(); ++it ) {
00504
if( (*it).end() <= tryFrom ||
00505 (*it).start() >= tryTo )
00506
continue;
00507
else {
00508
00509
00510
int secsDuration = tryFrom.secsTo( tryTo );
00511 tryFrom = (*it).end();
00512 tryTo = tryFrom.addSecs( secsDuration );
00513
00514 tryDate( attendee, tryFrom, tryTo );
00515
00516
return false;
00517 }
00518 }
00519
00520
return true;
00521 }
00522
00523
void KOEditorFreeBusy::updateStatusSummary()
00524 {
00525 FreeBusyItem *aItem =
00526 static_cast<FreeBusyItem *>( mGanttView->firstChild() );
00527
int total = 0;
00528
int accepted = 0;
00529
int tentative = 0;
00530
int declined = 0;
00531
while( aItem ) {
00532 ++total;
00533
switch( aItem->attendee()->status() ) {
00534
case Attendee::Accepted:
00535 ++accepted;
00536
break;
00537
case Attendee::Tentative:
00538 ++tentative;
00539
break;
00540
case Attendee::Declined:
00541 ++declined;
00542
break;
00543
case Attendee::NeedsAction:
00544
case Attendee::Delegated:
00545
case Attendee::Completed:
00546
case Attendee::InProcess:
00547
00548
break;
00549 }
00550 aItem = static_cast<FreeBusyItem *>( aItem->nextSibling() );
00551 }
00552
if( total > 1 && mIsOrganizer ) {
00553 mStatusSummaryLabel->show();
00554 mStatusSummaryLabel->setText(
00555 i18n(
"Of the %1 participants, %2 have accepted, %3"
00556
" have tentatively accepted, and %4 have declined.")
00557 .arg( total ).arg( accepted ).arg( tentative ).arg( declined ) );
00558 }
else {
00559 mStatusSummaryLabel->hide();
00560 mStatusSummaryLabel->setText(
"" );
00561 }
00562 mStatusSummaryLabel->adjustSize();
00563 }
00564
00565
void KOEditorFreeBusy::triggerReload()
00566 {
00567 mReloadTimer.start( 1000,
true );
00568 }
00569
00570
void KOEditorFreeBusy::cancelReload()
00571 {
00572 mReloadTimer.stop();
00573 }
00574
00575
void KOEditorFreeBusy::reload()
00576 {
00577 kdDebug() <<
"KOEditorFreeBusy::reload()" << endl;
00578
00579 FreeBusyItem *item = static_cast<FreeBusyItem *>( mGanttView->firstChild() );
00580
while( item ) {
00581 updateFreeBusyData( item->attendee() );
00582 item = static_cast<FreeBusyItem *>( item->nextSibling() );
00583 }
00584 }
00585
00586
void KOEditorFreeBusy::editFreeBusyUrl( KDGanttViewItem *i )
00587 {
00588 FreeBusyItem *item = static_cast<FreeBusyItem *>( i );
00589
if ( !item )
return;
00590
00591 Attendee *attendee = item->attendee();
00592
00593 FreeBusyUrlDialog dialog( attendee,
this );
00594 dialog.exec();
00595 }
00596
00597
#include "koeditorfreebusy.moc"