00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024 #include <qtooltip.h>
00025 #include <qlayout.h>
00026 #include <qlabel.h>
00027 #include <qcombobox.h>
00028 #include <qpushbutton.h>
00029
00030 #include <kdebug.h>
00031 #include <klocale.h>
00032 #include <kiconloader.h>
00033 #include <kmessagebox.h>
00034
00035 #include <libkcal/event.h>
00036 #include <libkcal/freebusy.h>
00037
00038 #include <kdgantt/KDGanttView.h>
00039 #include <kdgantt/KDGanttViewTaskItem.h>
00040
00041 #include "koprefs.h"
00042 #include "koglobals.h"
00043 #include "kogroupware.h"
00044
00045 #include "koeditorgantt.h"
00046
00047
00048
00049
00050 class GanttItem : public KDGanttViewTaskItem
00051 {
00052 public:
00053 GanttItem( Attendee* data, KDGanttView *parent ) :
00054 KDGanttViewTaskItem( parent ), mData( data )
00055 {
00056 Q_ASSERT( data );
00057 updateItem();
00058 setFreeBusyPeriods( 0 );
00059 }
00060 ~GanttItem();
00061
00062 void updateItem();
00063
00064 Attendee* data() const { return mData; }
00065 void setFreeBusy( KCal::FreeBusy* fb ) { mFreeBusy = fb; }
00066 KCal::FreeBusy* freeBusy() const { return mFreeBusy; }
00067
00068 void setFreeBusyPeriods( FreeBusy* fb );
00069
00070 QString key(int column, bool) const
00071 {
00072 QMap<int,QString>::ConstIterator it = mKeyMap.find(column);
00073 if (it == mKeyMap.end()) return listViewText(column);
00074 else return *it;
00075 }
00076
00077 void setSortKey(int column,const QString &key)
00078 {
00079 mKeyMap.insert(column,key);
00080 }
00081
00082 QString email() const { return mData->email(); }
00083
00084 private:
00085 Attendee* mData;
00086 KCal::FreeBusy* mFreeBusy;
00087
00088 QMap<int,QString> mKeyMap;
00089 };
00090
00091
00092
00093 GanttItem::~GanttItem()
00094 {
00095 }
00096
00097 void GanttItem::updateItem()
00098 {
00099 setListViewText(0,mData->name());
00100 setListViewText(1,mData->email());
00101 setListViewText(2,mData->roleStr());
00102 setListViewText(3,mData->statusStr());
00103 if (mData->RSVP() && !mData->email().isEmpty())
00104 setPixmap(4,KOGlobals::self()->smallIcon("mailappt"));
00105 else
00106 setPixmap(4,KOGlobals::self()->smallIcon("nomailappt"));
00107 }
00108
00109
00110
00111 void GanttItem::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 setFreeBusy( 0 );
00132 setShowNoInformation( true );
00133 }
00134 }
00135
00136
00137 KOEditorGantt::KOEditorGantt( int spacing, QWidget* parent, const char* name )
00138 : QWidget( parent, name )
00139 {
00140 QVBoxLayout* topLayout = new QVBoxLayout( this );
00141 topLayout->setSpacing( spacing );
00142
00143 QString organizer = KOPrefs::instance()->email();
00144 mOrganizerLabel = new QLabel( i18n("Organizer: %1").arg( organizer ), this );
00145 mIsOrganizer = true;
00146 topLayout->addWidget( mOrganizerLabel );
00147
00148
00149
00150 mStatusSummaryLabel = new QLabel( this );
00151 mStatusSummaryLabel->setPalette( QToolTip::palette() );
00152 mStatusSummaryLabel->setFrameStyle( QFrame::Plain | QFrame::Box );
00153 mStatusSummaryLabel->setLineWidth( 1 );
00154 topLayout->addWidget( mStatusSummaryLabel );
00155
00156
00157 QHBox* scaleHB = new QHBox( this );
00158 topLayout->addWidget( scaleHB );
00159 QLabel* scaleLA = new QLabel( i18n( "Scale: " ), scaleHB );
00160 scaleLA->setAlignment( AlignRight | AlignVCenter );
00161 scaleCombo = new QComboBox( scaleHB );
00162 scaleCombo->insertItem( i18n( "Hour" ) );
00163 scaleCombo->insertItem( i18n( "Day" ) );
00164 scaleCombo->insertItem( i18n( "Week" ) );
00165 scaleCombo->insertItem( i18n( "Month" ) );
00166 scaleCombo->insertItem( i18n( "Automatic" ) );
00167 scaleCombo->setCurrentItem( 0 );
00168 QLabel* dummy = new QLabel( scaleHB );
00169 scaleHB->setStretchFactor( dummy, 2 );
00170 QLabel* hFormatLA = new QLabel( i18n( "Hour format:" ), scaleHB );
00171 hFormatLA->setAlignment( AlignRight | AlignVCenter );
00172 dummy = new QLabel( scaleHB );
00173 scaleHB->setStretchFactor( dummy, 2 );
00174 QPushButton* centerPB = new QPushButton( i18n( "Center on Start" ), scaleHB );
00175 connect( centerPB, SIGNAL( clicked() ), this, SLOT( slotCenterOnStart() ) );
00176 dummy = new QLabel( scaleHB );
00177 scaleHB->setStretchFactor( dummy, 2 );
00178 QPushButton* zoomPB = new QPushButton( i18n( "Zoom to Fit" ), scaleHB );
00179 connect( zoomPB, SIGNAL( clicked() ), this, SLOT( slotZoomToTime() ) );
00180 dummy = new QLabel( scaleHB );
00181 scaleHB->setStretchFactor( dummy, 2 );
00182 QPushButton* pickPB = new QPushButton( i18n( "Pick Date" ), scaleHB );
00183 connect( pickPB, SIGNAL( clicked() ), this, SLOT( slotPickDate() ) );
00184 connect( scaleCombo, SIGNAL( activated( int ) ),
00185 this, SLOT( slotScaleChanged( int ) ) );
00186
00187 mGanttView = new KDGanttView(this,"mGanttView");
00188 topLayout->addWidget( mGanttView );
00189
00190 mGanttView->removeColumn( 0 );
00191 mGanttView->addColumn(i18n("Name"),180);
00192 mGanttView->addColumn(i18n("Email"),180);
00193 mGanttView->addColumn(i18n("Role"),60);
00194 mGanttView->addColumn(i18n("Status"),100);
00195 mGanttView->addColumn(i18n("RSVP"),35);
00196 if ( KOPrefs::instance()->mCompactDialogs ) {
00197 mGanttView->setFixedHeight(78);
00198 }
00199 mGanttView->setHeaderVisible( true );
00200 mGanttView->setScale( KDGanttView::Hour );
00201 #if 0
00202
00203 if ( !strcmp(QTextCodec::codecForLocale()->locale(),"de_DE@euro") ) {
00204 mGanttView->setHourFormat( KDGanttView::Hour_24 );
00205 hFormatCombo->setCurrentItem( 0 );
00206 }
00207 #endif
00208
00209
00210 QDateTime horizonStart = QDateTime( QDateTime::currentDateTime().addDays( -15 ).date() );
00211 QDateTime horizonEnd = QDateTime::currentDateTime().addDays( 15 );
00212 mGanttView->setHorizonStart( horizonStart );
00213 mGanttView->setHorizonEnd( horizonEnd );
00214 mGanttView->setCalendarMode( true );
00215 mGanttView->setDisplaySubitemsAsGroup( true );
00216 mGanttView->setShowLegendButton( false );
00217
00218 mGanttView->centerTimelineAfterShow( QDateTime::currentDateTime() );
00219 if ( KGlobal::locale()->use12Clock() )
00220 mGanttView->setHourFormat( KDGanttView::Hour_12 );
00221 else
00222 mGanttView->setHourFormat( KDGanttView::Hour_24 );
00223
00224 connect( mGanttView, SIGNAL( lvItemDoubleClicked( KDGanttViewItem* ) ),
00225 SLOT( updateFreeBusyData() ) );
00226 }
00227
00228 KOEditorGantt::~KOEditorGantt()
00229 {
00230 }
00231
00232 void KOEditorGantt::removeAttendee( Attendee* attendee )
00233 {
00234 GanttItem *anItem =
00235 static_cast<GanttItem*>( mGanttView->firstChild() );
00236 while( anItem ) {
00237 if( anItem->data() == attendee ) {
00238 delete anItem;
00239 updateStatusSummary();
00240 break;
00241 }
00242 anItem = static_cast<GanttItem*>( anItem->nextSibling() );
00243 }
00244 }
00245
00246 void KOEditorGantt::insertAttendee( Attendee* attendee )
00247 {
00248 (void)new GanttItem( attendee, mGanttView );
00249 updateFreeBusyData( attendee );
00250 updateStatusSummary();
00251 }
00252
00253 void KOEditorGantt::updateAttendee( Attendee* attendee )
00254 {
00255 GanttItem *anItem =
00256 static_cast<GanttItem*>( mGanttView->firstChild() );
00257 while( anItem ) {
00258 if( anItem->data() == attendee ) {
00259 anItem->updateItem();
00260 updateFreeBusyData( attendee );
00261 updateStatusSummary();
00262 break;
00263 }
00264 anItem = static_cast<GanttItem*>( anItem->nextSibling() );
00265 }
00266 }
00267
00268 void KOEditorGantt::clearAttendees()
00269 {
00270 mGanttView->clear();
00271 }
00272
00273
00274 void KOEditorGantt::setUpdateEnabled( bool enabled )
00275 {
00276 mGanttView->setUpdateEnabled( enabled );
00277 }
00278
00279 bool KOEditorGantt::updateEnabled() const
00280 {
00281 return mGanttView->getUpdateEnabled();
00282 }
00283
00284
00285 void KOEditorGantt::readEvent( Event* event )
00286 {
00287 setDateTimes( event->dtStart(), event->dtEnd() );
00288 }
00289
00290
00291 void KOEditorGantt::setDateTimes( QDateTime start, QDateTime end )
00292 {
00293 mDtStart = start;
00294 mDtEnd = end;
00295
00296 mGanttView->centerTimelineAfterShow( start );
00297 mGanttView->clearBackgroundColor();
00298 mGanttView->setIntervalBackgroundColor( start, end, Qt::magenta );
00299 }
00300
00301 void KOEditorGantt::slotScaleChanged( int newScale )
00302 {
00303
00304 KDGanttView::Scale scale = static_cast<KDGanttView::Scale>( newScale+1 );
00305 mGanttView->setScale( scale );
00306 slotCenterOnStart();
00307 }
00308
00309 void KOEditorGantt::slotCenterOnStart()
00310 {
00311 mGanttView->centerTimeline( mDtStart );
00312 }
00313
00314 void KOEditorGantt::slotZoomToTime()
00315 {
00316 bool block = mGanttView->getUpdateEnabled();
00317 mGanttView->setUpdateEnabled( false );
00318 if ( scaleCombo->currentItem() != 4 ) {
00319 scaleCombo->setCurrentItem( 4 );
00320 slotScaleChanged( 4 );
00321 }
00322 mGanttView->setUpdateEnabled( block );
00323 mGanttView->zoomToSelection( mDtStart, mDtEnd );
00324 }
00325
00326
00332 void KOEditorGantt::updateFreeBusyData( Attendee* attendee )
00333 {
00334 if( KOGroupware::instance() && attendee->name() != "(EmptyName)" ) {
00335 if( attendee->email() == KOPrefs::instance()->email() ) {
00336
00337 QCString fbText = KOGroupware::instance()->getFreeBusyString().utf8();
00338 slotInsertFreeBusy( attendee->email(),
00339 KOGroupware::instance()->parseFreeBusy( fbText ) );
00340 } else
00341 KOGroupware::instance()->downloadFreeBusyData( attendee->email(), this,
00342 SLOT( slotInsertFreeBusy( const QString&, FreeBusy* ) ) );
00343 }
00344 }
00345
00346
00347
00348 void KOEditorGantt::slotInsertFreeBusy( const QString& email, FreeBusy* fb )
00349 {
00350 if( fb )
00351 fb->sortList();
00352 bool block = mGanttView->getUpdateEnabled();
00353 mGanttView->setUpdateEnabled(false);
00354 for( KDGanttViewItem* it = mGanttView->firstChild(); it;
00355 it = it->nextSibling() ) {
00356 GanttItem* item = static_cast<GanttItem*>( it );
00357 if( item->email() == email )
00358 item->setFreeBusyPeriods( fb );
00359 }
00360 mGanttView->setUpdateEnabled(block);
00361 }
00362
00363
00368 void KOEditorGantt::slotUpdateGanttView( QDateTime dtFrom, QDateTime dtTo )
00369 {
00370 bool block = mGanttView->getUpdateEnabled( );
00371 mGanttView->setUpdateEnabled( false );
00372 QDateTime horizonStart = QDateTime( dtFrom.addDays( -15 ).date() );
00373 mGanttView->setHorizonStart( horizonStart );
00374 mGanttView->setHorizonEnd( dtTo.addDays( 15 ) );
00375 mGanttView->clearBackgroundColor();
00376 mGanttView->setIntervalBackgroundColor( dtFrom, dtTo, Qt::magenta );
00377 mGanttView->setUpdateEnabled( block );
00378 mGanttView->centerTimelineAfterShow( dtFrom );
00379 }
00380
00381
00385 void KOEditorGantt::slotPickDate()
00386 {
00387 QDateTime start = mDtStart;
00388 QDateTime end = mDtEnd;
00389 bool success = findFreeSlot( start, end );
00390
00391 if( success ) {
00392 if ( start == mDtStart && end == mDtEnd ) {
00393 KMessageBox::information( this, i18n( "The meeting has already suitable start/end times." ));
00394 } else {
00395 emit dateTimesChanged( start, end );
00396 slotUpdateGanttView( start, end );
00397 KMessageBox::information( this, i18n( "The meeting has been moved to\nStart: %1\nEnd: %2." ).arg( start.toString() ).arg( end.toString() ) );
00398 }
00399 } else
00400 KMessageBox::sorry( this, i18n( "No suitable date found." ) );
00401 }
00402
00403
00408 bool KOEditorGantt::findFreeSlot( QDateTime& dtFrom, QDateTime& dtTo )
00409 {
00410 if( tryDate( dtFrom, dtTo ) )
00411
00412 return true;
00413
00414 QDateTime tryFrom = dtFrom;
00415 QDateTime tryTo = dtTo;
00416
00417
00418
00419 if( tryFrom < QDateTime::currentDateTime() ) {
00420
00421 int secs = tryFrom.secsTo( tryTo );
00422 tryFrom = QDateTime::currentDateTime();
00423 tryTo = tryFrom.addSecs( secs );
00424 }
00425
00426 bool found = false;
00427 while( !found ) {
00428 found = tryDate( tryFrom, tryTo );
00429
00430 if( !found && dtFrom.daysTo( tryFrom ) > 365 )
00431 break;
00432 }
00433
00434 dtFrom = tryFrom;
00435 dtTo = tryTo;
00436
00437 return found;
00438 }
00439
00440
00449 bool KOEditorGantt::tryDate( QDateTime& tryFrom, QDateTime& tryTo )
00450 {
00451 GanttItem* currentItem = static_cast<GanttItem*>( mGanttView->firstChild() );
00452 while( currentItem ) {
00453 if( !tryDate( currentItem, tryFrom, tryTo ) ) {
00454
00455 return false;
00456 }
00457
00458 currentItem = static_cast<GanttItem*>( currentItem->nextSibling() );
00459 }
00460
00461 return true;
00462 }
00463
00471 bool KOEditorGantt::tryDate( GanttItem* attendee,
00472 QDateTime& tryFrom, QDateTime& tryTo )
00473 {
00474
00475
00476
00477 KCal::FreeBusy* fb = attendee->freeBusy();
00478 if( !fb )
00479 return true;
00480
00481 QValueList<KCal::Period> busyPeriods = fb->busyPeriods();
00482 for( QValueList<KCal::Period>::Iterator it = busyPeriods.begin();
00483 it != busyPeriods.end(); ++it ) {
00484 if( (*it).end() <= tryFrom ||
00485 (*it).start() >= tryTo )
00486 continue;
00487 else {
00488
00489
00490 int secsDuration = tryFrom.secsTo( tryTo );
00491 tryFrom = (*it).end();
00492 tryTo = tryFrom.addSecs( secsDuration );
00493
00494 tryDate( attendee, tryFrom, tryTo );
00495
00496 return false;
00497 }
00498 }
00499
00500 return true;
00501 }
00502
00503 void KOEditorGantt::updateStatusSummary()
00504 {
00505 GanttItem *aItem =
00506 static_cast<GanttItem*>(mGanttView->firstChild());
00507 int total = 0;
00508 int accepted = 0;
00509 int tentative = 0;
00510 int declined = 0;
00511 while( aItem ) {
00512 ++total;
00513 switch( aItem->data()->status() ) {
00514 case Attendee::Accepted:
00515 ++accepted;
00516 break;
00517 case Attendee::Tentative:
00518 ++tentative;
00519 break;
00520 case Attendee::Declined:
00521 ++declined;
00522 break;
00523 case Attendee::NeedsAction:
00524 case Attendee::Delegated:
00525 case Attendee::Completed:
00526 case Attendee::InProcess:
00527
00528 break;
00529 }
00530 aItem = static_cast<GanttItem*>(aItem->nextSibling());
00531 }
00532 if( total > 1 && mIsOrganizer ) {
00533 mStatusSummaryLabel->show();
00534 mStatusSummaryLabel->setText( i18n( "Of the %1 participants, %2 have accepted, %3"
00535 " have tentatively accepted, and %4 have declined.")
00536 .arg(total).arg(accepted).arg(tentative).arg(declined));
00537 } else {
00538 mStatusSummaryLabel->hide();
00539 mStatusSummaryLabel->setText("");
00540 }
00541 mStatusSummaryLabel->adjustSize();
00542 }
00543
00544 #include "koeditorgantt.moc"