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
00157
00158 mIsOrganizer =
false;
00159 mStatusSummaryLabel =
new QLabel(
this );
00160 mStatusSummaryLabel->setPalette( QToolTip::palette() );
00161 mStatusSummaryLabel->setFrameStyle( QFrame::Plain | QFrame::Box );
00162 mStatusSummaryLabel->setLineWidth( 1 );
00163 mStatusSummaryLabel->hide();
00164 topLayout->addWidget( mStatusSummaryLabel );
00165
00166
00167
QBoxLayout *controlLayout =
new QHBoxLayout( topLayout );
00168
00169 QLabel *label =
new QLabel( i18n(
"Scale: " ),
this );
00170 controlLayout->
addWidget( label );
00171
00172 scaleCombo =
new QComboBox(
this );
00173 scaleCombo->insertItem( i18n(
"Hour" ) );
00174 scaleCombo->insertItem( i18n(
"Day" ) );
00175 scaleCombo->insertItem( i18n(
"Week" ) );
00176 scaleCombo->insertItem( i18n(
"Month" ) );
00177 scaleCombo->insertItem( i18n(
"Automatic" ) );
00178 scaleCombo->setCurrentItem( 0 );
00179 connect( scaleCombo, SIGNAL( activated(
int ) ),
00180 SLOT( slotScaleChanged(
int ) ) );
00181 controlLayout->
addWidget( scaleCombo );
00182
00183
QPushButton *button =
new QPushButton( i18n(
"Center on Start" ),
this );
00184 connect( button, SIGNAL( clicked() ), SLOT( slotCenterOnStart() ) );
00185 controlLayout->
addWidget( button );
00186
00187 button =
new QPushButton( i18n(
"Zoom to Fit" ),
this );
00188 connect( button, SIGNAL( clicked() ), SLOT( slotZoomToTime() ) );
00189 controlLayout->
addWidget( button );
00190
00191 controlLayout->addStretch( 1 );
00192
00193 button =
new QPushButton( i18n(
"Pick Date" ),
this );
00194 connect( button, SIGNAL( clicked() ), SLOT( slotPickDate() ) );
00195 controlLayout->
addWidget( button );
00196
00197 controlLayout->addStretch( 1 );
00198
00199 button =
new QPushButton( i18n(
"Reload"),
this );
00200 controlLayout->
addWidget( button );
00201 connect( button, SIGNAL( clicked() ), SLOT( reload() ) );
00202
00203 mGanttView =
new KDGanttView(
this,
"mGanttView" );
00204 topLayout->addWidget( mGanttView );
00205
00206 mGanttView->removeColumn( 0 );
00207 mGanttView->addColumn( i18n(
"Name"), 180 );
00208 mGanttView->addColumn( i18n(
"Email"), 180 );
00209 mGanttView->addColumn( i18n(
"Role"), 60 );
00210 mGanttView->addColumn( i18n(
"Status"), 100 );
00211 mGanttView->addColumn( i18n(
"RSVP"), 35 );
00212
if ( KOPrefs::instance()->mCompactDialogs ) {
00213 mGanttView->setFixedHeight( 78 );
00214 }
00215 mGanttView->setHeaderVisible(
true );
00216 mGanttView->setScale( KDGanttView::Hour );
00217 mGanttView->setShowHeaderPopupMenu(
true,
true,
true,
false,
false,
true );
00218
00219
00220
QDateTime horizonStart =
QDateTime( QDateTime::currentDateTime()
00221 .addDays( -15 ).date() );
00222 QDateTime horizonEnd = QDateTime::currentDateTime().addDays( 15 );
00223 mGanttView->setHorizonStart( horizonStart );
00224 mGanttView->setHorizonEnd( horizonEnd );
00225 mGanttView->setCalendarMode(
true );
00226
00227 mGanttView->setShowLegendButton(
false );
00228
00229 mGanttView->centerTimelineAfterShow( QDateTime::currentDateTime() );
00230
if ( KGlobal::locale()->use12Clock() )
00231 mGanttView->setHourFormat( KDGanttView::Hour_12 );
00232
else
00233 mGanttView->setHourFormat( KDGanttView::Hour_24_FourDigit );
00234 connect( mGanttView, SIGNAL ( timeIntervalSelected(
const QDateTime &,
00235
const QDateTime & ) ),
00236 mGanttView, SLOT( zoomToSelection(
const QDateTime &,
00237
const QDateTime & ) ) );
00238
00239
00240 connect( mGanttView, SIGNAL( lvItemDoubleClicked( KDGanttViewItem * ) ),
00241 SLOT( editFreeBusyUrl( KDGanttViewItem * ) ) );
00242
00243 FreeBusyManager *m = KOGroupware::instance()->freeBusyManager();
00244 connect( m, SIGNAL( freeBusyRetrieved( KCal::FreeBusy *,
const QString & ) ),
00245 SLOT( slotInsertFreeBusy( KCal::FreeBusy *,
const QString & ) ) );
00246
00247 connect( &mReloadTimer, SIGNAL( timeout() ), SLOT( reload() ) );
00248 }
00249
00250 KOEditorFreeBusy::~KOEditorFreeBusy()
00251 {
00252 }
00253
00254
void KOEditorFreeBusy::removeAttendee( Attendee *attendee )
00255 {
00256 FreeBusyItem *anItem =
00257 static_cast<FreeBusyItem *>( mGanttView->firstChild() );
00258
while( anItem ) {
00259
if( anItem->attendee() == attendee ) {
00260
delete anItem;
00261 updateStatusSummary();
00262
break;
00263 }
00264 anItem = static_cast<FreeBusyItem *>( anItem->nextSibling() );
00265 }
00266 }
00267
00268
void KOEditorFreeBusy::insertAttendee( Attendee *attendee )
00269 {
00270 (
void)
new FreeBusyItem( attendee, mGanttView );
00271
#if 0
00272
updateFreeBusyData( attendee );
00273
#endif
00274
updateStatusSummary();
00275 }
00276
00277
void KOEditorFreeBusy::updateAttendee( Attendee *attendee )
00278 {
00279 FreeBusyItem *anItem =
00280 static_cast<FreeBusyItem *>( mGanttView->firstChild() );
00281
while( anItem ) {
00282
if( anItem->attendee() == attendee ) {
00283 anItem->updateItem();
00284 updateFreeBusyData( attendee );
00285 updateStatusSummary();
00286
break;
00287 }
00288 anItem = static_cast<FreeBusyItem *>( anItem->nextSibling() );
00289 }
00290 }
00291
00292
void KOEditorFreeBusy::clearAttendees()
00293 {
00294 mGanttView->clear();
00295 }
00296
00297
00298
void KOEditorFreeBusy::setUpdateEnabled(
bool enabled )
00299 {
00300 mGanttView->setUpdateEnabled( enabled );
00301 }
00302
00303
bool KOEditorFreeBusy::updateEnabled()
const
00304
{
00305
return mGanttView->getUpdateEnabled();
00306 }
00307
00308
00309
void KOEditorFreeBusy::readEvent( Event *event )
00310 {
00311 setDateTimes( event->dtStart(), event->dtEnd() );
00312 mIsOrganizer = KOPrefs::instance()->thatIsMe( event->organizer() );
00313 updateStatusSummary();
00314 }
00315
00316
00317
void KOEditorFreeBusy::setDateTimes( QDateTime start, QDateTime end )
00318 {
00319
00320 slotUpdateGanttView( start, end );
00321 }
00322
00323
void KOEditorFreeBusy::slotScaleChanged(
int newScale )
00324 {
00325
00326 KDGanttView::Scale scale = static_cast<KDGanttView::Scale>( newScale+1 );
00327 mGanttView->setScale( scale );
00328 slotCenterOnStart();
00329 }
00330
00331
void KOEditorFreeBusy::slotCenterOnStart()
00332 {
00333 mGanttView->centerTimeline( mDtStart );
00334 }
00335
00336
void KOEditorFreeBusy::slotZoomToTime()
00337 {
00338 mGanttView->zoomToFit();
00339 }
00340
00341
void KOEditorFreeBusy::updateFreeBusyData( KDGanttViewItem *item )
00342 {
00343 FreeBusyItem *g = static_cast<FreeBusyItem *>( item );
00344 updateFreeBusyData( g->attendee() );
00345 }
00346
00347
void KOEditorFreeBusy::updateFreeBusyData( Attendee *attendee )
00348 {
00349
if( KOGroupware::instance() && attendee->name() !=
"(EmptyName)" ) {
00350 FreeBusyManager *m = KOGroupware::instance()->freeBusyManager();
00351 m->retrieveFreeBusy( attendee->email() );
00352 }
00353 }
00354
00355
00356
00357
void KOEditorFreeBusy::slotInsertFreeBusy( KCal::FreeBusy *fb,
00358
const QString &email )
00359 {
00360 kdDebug() <<
"KOEditorFreeBusy::slotInsertFreeBusy() " << email << endl;
00361
00362
if( fb )
00363 fb->sortList();
00364
bool block = mGanttView->getUpdateEnabled();
00365 mGanttView->setUpdateEnabled(
false );
00366
for( KDGanttViewItem *it = mGanttView->firstChild(); it;
00367 it = it->nextSibling() ) {
00368 FreeBusyItem *item = static_cast<FreeBusyItem *>( it );
00369
if( item->email() == email )
00370 item->setFreeBusyPeriods( fb );
00371 }
00372 mGanttView->setUpdateEnabled( block );
00373 }
00374
00375
00380
void KOEditorFreeBusy::slotUpdateGanttView( QDateTime dtFrom, QDateTime dtTo )
00381 {
00382 mDtStart = dtFrom;
00383 mDtEnd = dtTo;
00384
bool block = mGanttView->getUpdateEnabled( );
00385 mGanttView->setUpdateEnabled(
false );
00386 QDateTime horizonStart = QDateTime( dtFrom.addDays( -15 ).date() );
00387 mGanttView->setHorizonStart( horizonStart );
00388 mGanttView->setHorizonEnd( dtTo.addDays( 15 ) );
00389 mGanttView->clearBackgroundColor();
00390 mGanttView->setIntervalBackgroundColor( dtFrom, dtTo, Qt::magenta );
00391 mGanttView->setUpdateEnabled( block );
00392 mGanttView->centerTimelineAfterShow( dtFrom );
00393 }
00394
00395
00399
void KOEditorFreeBusy::slotPickDate()
00400 {
00401 QDateTime start = mDtStart;
00402 QDateTime end = mDtEnd;
00403
bool success = findFreeSlot( start, end );
00404
00405
if( success ) {
00406
if ( start == mDtStart && end == mDtEnd ) {
00407 KMessageBox::information(
this,
00408 i18n(
"The meeting has already suitable start/end times." ));
00409 }
else {
00410 emit dateTimesChanged( start, end );
00411 slotUpdateGanttView( start, end );
00412 KMessageBox::information(
this,
00413 i18n(
"The meeting has been moved to\nStart: %1\nEnd: %2." )
00414 .arg( start.toString() ).arg( end.toString() ) );
00415 }
00416 }
else
00417 KMessageBox::sorry(
this, i18n(
"No suitable date found." ) );
00418 }
00419
00420
00425
bool KOEditorFreeBusy::findFreeSlot( QDateTime &dtFrom, QDateTime &dtTo )
00426 {
00427
if( tryDate( dtFrom, dtTo ) )
00428
00429
return true;
00430
00431 QDateTime tryFrom = dtFrom;
00432 QDateTime tryTo = dtTo;
00433
00434
00435
00436
if( tryFrom < QDateTime::currentDateTime() ) {
00437
00438
int secs = tryFrom.secsTo( tryTo );
00439 tryFrom = QDateTime::currentDateTime();
00440 tryTo = tryFrom.addSecs( secs );
00441 }
00442
00443
bool found =
false;
00444
while( !found ) {
00445 found = tryDate( tryFrom, tryTo );
00446
00447
if( !found && dtFrom.daysTo( tryFrom ) > 365 )
00448
break;
00449 }
00450
00451 dtFrom = tryFrom;
00452 dtTo = tryTo;
00453
00454
return found;
00455 }
00456
00457
00466
bool KOEditorFreeBusy::tryDate( QDateTime& tryFrom, QDateTime& tryTo )
00467 {
00468 FreeBusyItem* currentItem = static_cast<FreeBusyItem*>( mGanttView->firstChild() );
00469
while( currentItem ) {
00470
if( !tryDate( currentItem, tryFrom, tryTo ) ) {
00471
00472
return false;
00473 }
00474
00475 currentItem = static_cast<FreeBusyItem*>( currentItem->nextSibling() );
00476 }
00477
00478
return true;
00479 }
00480
00488
bool KOEditorFreeBusy::tryDate( FreeBusyItem *attendee,
00489 QDateTime &tryFrom, QDateTime &tryTo )
00490 {
00491
00492
00493
00494 KCal::FreeBusy *fb = attendee->freeBusy();
00495
if( !fb )
00496
return true;
00497
00498
QValueList<KCal::Period> busyPeriods = fb->busyPeriods();
00499
for(
QValueList<KCal::Period>::Iterator it = busyPeriods.begin();
00500 it != busyPeriods.end(); ++it ) {
00501
if( (*it).end() <= tryFrom ||
00502 (*it).start() >= tryTo )
00503
continue;
00504
else {
00505
00506
00507
int secsDuration = tryFrom.secsTo( tryTo );
00508 tryFrom = (*it).end();
00509 tryTo = tryFrom.addSecs( secsDuration );
00510
00511 tryDate( attendee, tryFrom, tryTo );
00512
00513
return false;
00514 }
00515 }
00516
00517
return true;
00518 }
00519
00520
void KOEditorFreeBusy::updateStatusSummary()
00521 {
00522 FreeBusyItem *aItem =
00523 static_cast<FreeBusyItem *>( mGanttView->firstChild() );
00524
int total = 0;
00525
int accepted = 0;
00526
int tentative = 0;
00527
int declined = 0;
00528
while( aItem ) {
00529 ++total;
00530
switch( aItem->attendee()->status() ) {
00531
case Attendee::Accepted:
00532 ++accepted;
00533
break;
00534
case Attendee::Tentative:
00535 ++tentative;
00536
break;
00537
case Attendee::Declined:
00538 ++declined;
00539
break;
00540
case Attendee::NeedsAction:
00541
case Attendee::Delegated:
00542
case Attendee::Completed:
00543
case Attendee::InProcess:
00544
00545
break;
00546 }
00547 aItem = static_cast<FreeBusyItem *>( aItem->nextSibling() );
00548 }
00549
if( total > 1 && mIsOrganizer ) {
00550 mStatusSummaryLabel->show();
00551 mStatusSummaryLabel->setText(
00552 i18n(
"Of the %1 participants, %2 have accepted, %3"
00553
" have tentatively accepted, and %4 have declined.")
00554 .arg( total ).arg( accepted ).arg( tentative ).arg( declined ) );
00555 }
else {
00556 mStatusSummaryLabel->hide();
00557 }
00558 mStatusSummaryLabel->adjustSize();
00559 }
00560
00561
void KOEditorFreeBusy::triggerReload()
00562 {
00563 mReloadTimer.start( 1000,
true );
00564 }
00565
00566
void KOEditorFreeBusy::cancelReload()
00567 {
00568 mReloadTimer.stop();
00569 }
00570
00571
void KOEditorFreeBusy::reload()
00572 {
00573 kdDebug() <<
"KOEditorFreeBusy::reload()" << endl;
00574
00575 FreeBusyItem *item = static_cast<FreeBusyItem *>( mGanttView->firstChild() );
00576
while( item ) {
00577 updateFreeBusyData( item->attendee() );
00578 item = static_cast<FreeBusyItem *>( item->nextSibling() );
00579 }
00580 }
00581
00582
void KOEditorFreeBusy::editFreeBusyUrl( KDGanttViewItem *i )
00583 {
00584 FreeBusyItem *item = static_cast<FreeBusyItem *>( i );
00585
if ( !item )
return;
00586
00587 Attendee *attendee = item->attendee();
00588
00589 FreeBusyUrlDialog dialog( attendee,
this );
00590 dialog.exec();
00591 }
00592
00593
#include "koeditorfreebusy.moc"