koagendaview.cpp
00001 /* 00002 This file is part of KOrganizer. 00003 Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> 00004 Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com> 00005 00006 This program is free software; you can redistribute it and/or modify 00007 it under the terms of the GNU General Public License as published by 00008 the Free Software Foundation; either version 2 of the License, or 00009 (at your option) any later version. 00010 00011 This program is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00014 GNU General Public License for more details. 00015 00016 You should have received a copy of the GNU General Public License 00017 along with this program; if not, write to the Free Software 00018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00019 00020 As a special exception, permission is given to link this program 00021 with any edition of TQt, and distribute the resulting executable, 00022 without including the source code for TQt in the source distribution. 00023 */ 00024 00025 #include <tqhbox.h> 00026 #include <tqvbox.h> 00027 #include <tqlabel.h> 00028 #include <tqframe.h> 00029 #include <tqlayout.h> 00030 #ifndef KORG_NOSPLITTER 00031 #include <tqsplitter.h> 00032 #endif 00033 #include <tqfont.h> 00034 #include <tqfontmetrics.h> 00035 #include <tqpopupmenu.h> 00036 #include <tqtooltip.h> 00037 #include <tqpainter.h> 00038 #include <tqpushbutton.h> 00039 #include <tqcursor.h> 00040 #include <tqbitarray.h> 00041 00042 #include <kapplication.h> 00043 #include <kdebug.h> 00044 #include <kstandarddirs.h> 00045 #include <kiconloader.h> 00046 #include <klocale.h> 00047 #include <kconfig.h> 00048 #include <kglobal.h> 00049 #include <kglobalsettings.h> 00050 #include <kholidays.h> 00051 00052 #include <libkcal/calendar.h> 00053 #include <libkcal/icaldrag.h> 00054 #include <libkcal/dndfactory.h> 00055 #include <libkcal/calfilter.h> 00056 00057 #include <kcalendarsystem.h> 00058 00059 #include "koglobals.h" 00060 #ifndef KORG_NOPLUGINS 00061 #include "kocore.h" 00062 #endif 00063 #include "koprefs.h" 00064 #include "koagenda.h" 00065 #include "koagendaitem.h" 00066 #include "timelabels.h" 00067 00068 #include "koincidencetooltip.h" 00069 #include "kogroupware.h" 00070 #include "kodialogmanager.h" 00071 #include "koeventpopupmenu.h" 00072 00073 #include "koagendaview.h" 00074 #include "koagendaview.moc" 00075 00076 using namespace KOrg; 00077 00078 00079 EventIndicator::EventIndicator(Location loc,TQWidget *parent,const char *name) 00080 : TQFrame(parent,name) 00081 { 00082 mColumns = 1; 00083 mEnabled.resize( mColumns ); 00084 mLocation = loc; 00085 00086 if (mLocation == Top) mPixmap = KOGlobals::self()->smallIcon("upindicator"); 00087 else mPixmap = KOGlobals::self()->smallIcon("downindicator"); 00088 00089 setMinimumHeight(mPixmap.height()); 00090 } 00091 00092 EventIndicator::~EventIndicator() 00093 { 00094 } 00095 00096 void EventIndicator::drawContents(TQPainter *p) 00097 { 00098 // kdDebug(5850) << "======== top: " << contentsRect().top() << " bottom " 00099 // << contentsRect().bottom() << " left " << contentsRect().left() 00100 // << " right " << contentsRect().right() << endl; 00101 00102 int i; 00103 for(i=0;i<mColumns;++i) { 00104 if (mEnabled[i]) { 00105 int cellWidth = contentsRect().right()/mColumns; 00106 int xOffset = KOGlobals::self()->reverseLayout() ? 00107 (mColumns - 1 - i)*cellWidth + cellWidth/2 -mPixmap.width()/2 : 00108 i*cellWidth + cellWidth/2 -mPixmap.width()/2; 00109 p->drawPixmap(TQPoint(xOffset,0),mPixmap); 00110 } 00111 } 00112 } 00113 00114 void EventIndicator::changeColumns(int columns) 00115 { 00116 mColumns = columns; 00117 mEnabled.resize(mColumns); 00118 00119 update(); 00120 } 00121 00122 void EventIndicator::enableColumn(int column, bool enable) 00123 { 00124 mEnabled[column] = enable; 00125 } 00126 00127 00128 #include <libkcal/incidence.h> 00129 00133 00134 00135 KOAlternateLabel::KOAlternateLabel(const TQString &shortlabel, const TQString &longlabel, 00136 const TQString &extensivelabel, TQWidget *parent, const char *name ) 00137 : TQLabel(parent, name), mTextTypeFixed(false), mShortText(shortlabel), 00138 mLongText(longlabel), mExtensiveText(extensivelabel) 00139 { 00140 setSizePolicy(TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Fixed )); 00141 if (mExtensiveText.isEmpty()) mExtensiveText = mLongText; 00142 squeezeTextToLabel(); 00143 } 00144 00145 KOAlternateLabel::~KOAlternateLabel() 00146 { 00147 } 00148 00149 void KOAlternateLabel::useShortText() 00150 { 00151 mTextTypeFixed = true; 00152 TQLabel::setText( mShortText ); 00153 TQToolTip::remove( this ); 00154 TQToolTip::add( this, mExtensiveText ); 00155 update(); // for kolab/issue4350 00156 } 00157 00158 void KOAlternateLabel::useLongText() 00159 { 00160 mTextTypeFixed = true; 00161 TQLabel::setText( mLongText ); 00162 TQToolTip::remove( this ); 00163 TQToolTip::add( this, mExtensiveText ); 00164 update(); // for kolab/issue4350 00165 } 00166 00167 void KOAlternateLabel::useExtensiveText() 00168 { 00169 mTextTypeFixed = true; 00170 TQLabel::setText( mExtensiveText ); 00171 TQToolTip::remove( this ); 00172 TQToolTip::add( this, "" ); 00173 update(); // for kolab/issue4350 00174 } 00175 00176 void KOAlternateLabel::useDefaultText() 00177 { 00178 mTextTypeFixed = false; 00179 squeezeTextToLabel(); 00180 } 00181 00182 KOAlternateLabel::TextType KOAlternateLabel::largestFittingTextType() const 00183 { 00184 TQFontMetrics fm( fontMetrics() ); 00185 const int labelWidth = size().width(); 00186 const int longTextWidth = fm.width( mLongText ); 00187 const int extensiveTextWidth = fm.width( mExtensiveText ); 00188 if ( extensiveTextWidth <= labelWidth ) 00189 return Extensive; 00190 else if ( longTextWidth <= labelWidth ) 00191 return Long; 00192 else 00193 return Short; 00194 } 00195 00196 void KOAlternateLabel::setFixedType( TextType type ) 00197 { 00198 switch ( type ) 00199 { 00200 case Extensive: useExtensiveText(); break; 00201 case Long: useLongText(); break; 00202 case Short: useShortText(); break; 00203 } 00204 } 00205 00206 void KOAlternateLabel::squeezeTextToLabel() 00207 { 00208 if ( mTextTypeFixed ) 00209 return; 00210 00211 const TextType type = largestFittingTextType(); 00212 switch ( type ) 00213 { 00214 case Extensive: 00215 TQLabel::setText( mExtensiveText ); 00216 TQToolTip::remove( this ); 00217 TQToolTip::add( this, "" ); 00218 break; 00219 case Long: 00220 TQLabel::setText( mLongText ); 00221 TQToolTip::remove( this ); 00222 TQToolTip::add( this, mExtensiveText ); 00223 break; 00224 case Short: 00225 TQLabel::setText( mShortText ); 00226 TQToolTip::remove( this ); 00227 TQToolTip::add( this, mExtensiveText ); 00228 break; 00229 } 00230 update(); // for kolab/issue4350 00231 } 00232 00233 void KOAlternateLabel::resizeEvent( TQResizeEvent * ) 00234 { 00235 squeezeTextToLabel(); 00236 } 00237 00238 TQSize KOAlternateLabel::minimumSizeHint() const 00239 { 00240 TQSize sh = TQLabel::minimumSizeHint(); 00241 sh.setWidth(-1); 00242 return sh; 00243 } 00244 00248 00249 KOAgendaView::KOAgendaView( Calendar *cal, 00250 CalendarView *calendarView, 00251 TQWidget *parent, 00252 const char *name, 00253 bool isSideBySide ) : 00254 KOrg::AgendaView (cal, parent,name), mExpandButton( 0 ), 00255 mAllowAgendaUpdate( true ), 00256 mUpdateItem( 0 ), 00257 mIsSideBySide( isSideBySide ), 00258 mPendingChanges( true ), 00259 mAreDatesInitialized( false ) 00260 { 00261 mSelectedDates.append(TQDate::currentDate()); 00262 00263 mLayoutDayLabels = 0; 00264 mDayLabelsFrame = 0; 00265 mDayLabels = 0; 00266 00267 bool isRTL = KOGlobals::self()->reverseLayout(); 00268 00269 if ( KOPrefs::instance()->compactDialogs() ) { 00270 if ( KOPrefs::instance()->mVerticalScreen ) { 00271 mExpandedPixmap = KOGlobals::self()->smallIcon( "1downarrow" ); 00272 mNotExpandedPixmap = KOGlobals::self()->smallIcon( "1uparrow" ); 00273 } else { 00274 mExpandedPixmap = KOGlobals::self()->smallIcon( isRTL ? "1leftarrow" : "1rightarrow" ); 00275 mNotExpandedPixmap = KOGlobals::self()->smallIcon( isRTL ? "1rightarrow" : "1leftarrow" ); 00276 } 00277 } 00278 00279 TQBoxLayout *topLayout = new TQVBoxLayout(this); 00280 00281 // Create day name labels for agenda columns 00282 mDayLabelsFrame = new TQHBox(this); 00283 topLayout->addWidget(mDayLabelsFrame); 00284 00285 // Create agenda splitter 00286 #ifndef KORG_NOSPLITTER 00287 mSplitterAgenda = new TQSplitter(Qt::Vertical,this); 00288 topLayout->addWidget(mSplitterAgenda); 00289 00290 #if KDE_IS_VERSION( 3, 1, 93 ) 00291 mSplitterAgenda->setOpaqueResize( KGlobalSettings::opaqueResize() ); 00292 #else 00293 mSplitterAgenda->setOpaqueResize(); 00294 #endif 00295 00296 mAllDayFrame = new TQHBox(mSplitterAgenda); 00297 00298 TQWidget *agendaFrame = new TQWidget(mSplitterAgenda); 00299 #else 00300 TQVBox *mainBox = new TQVBox( this ); 00301 topLayout->addWidget( mainBox ); 00302 00303 mAllDayFrame = new TQHBox(mainBox); 00304 00305 TQWidget *agendaFrame = new TQWidget(mainBox); 00306 #endif 00307 00308 // Create all-day agenda widget 00309 mDummyAllDayLeft = new TQVBox( mAllDayFrame ); 00310 if ( isSideBySide ) 00311 mDummyAllDayLeft->hide(); 00312 00313 if ( KOPrefs::instance()->compactDialogs() ) { 00314 mExpandButton = new TQPushButton(mDummyAllDayLeft); 00315 mExpandButton->setPixmap( mNotExpandedPixmap ); 00316 mExpandButton->setSizePolicy( TQSizePolicy( TQSizePolicy::Fixed, 00317 TQSizePolicy::Fixed ) ); 00318 connect( mExpandButton, TQT_SIGNAL( clicked() ), TQT_SIGNAL( toggleExpand() ) ); 00319 } else { 00320 TQLabel *label = new TQLabel( i18n("All Day"), mDummyAllDayLeft ); 00321 label->setAlignment( TQt::AlignRight | TQt::AlignVCenter | TQt::WordBreak ); 00322 } 00323 00324 mAllDayAgenda = new KOAgenda( 1, calendarView, mAllDayFrame ); 00325 mAllDayAgenda->setCalendar( calendar() ); 00326 TQWidget *dummyAllDayRight = new TQWidget(mAllDayFrame); 00327 00328 // Create agenda frame 00329 TQGridLayout *agendaLayout = new TQGridLayout(agendaFrame,3,3); 00330 // TQHBox *agendaFrame = new TQHBox(splitterAgenda); 00331 00332 // create event indicator bars 00333 mEventIndicatorTop = new EventIndicator(EventIndicator::Top,agendaFrame); 00334 agendaLayout->addWidget(mEventIndicatorTop,0,1); 00335 mEventIndicatorBottom = new EventIndicator(EventIndicator::Bottom, 00336 agendaFrame); 00337 agendaLayout->addWidget(mEventIndicatorBottom,2,1); 00338 TQWidget *dummyAgendaRight = new TQWidget(agendaFrame); 00339 agendaLayout->addWidget(dummyAgendaRight,0,2); 00340 00341 // Create time labels 00342 mTimeLabels = new TimeLabels(24,agendaFrame); 00343 agendaLayout->addWidget(mTimeLabels,1,0); 00344 00345 // Create agenda 00346 mAgenda = new KOAgenda( 1, 96, KOPrefs::instance()->mHourSize, calendarView, agendaFrame ); 00347 mAgenda->setCalendar( calendar() ); 00348 agendaLayout->addMultiCellWidget(mAgenda,1,1,1,2); 00349 agendaLayout->setColStretch(1,1); 00350 00351 // Create event context menu for agenda 00352 mAgendaPopup = eventPopup(); 00353 00354 // Create event context menu for all day agenda 00355 mAllDayAgendaPopup = eventPopup(); 00356 00357 // make connections between dependent widgets 00358 mTimeLabels->setAgenda(mAgenda); 00359 if ( isSideBySide ) 00360 mTimeLabels->hide(); 00361 00362 // Update widgets to reflect user preferences 00363 // updateConfig(); 00364 00365 createDayLabels( true ); 00366 00367 if ( !isSideBySide ) { 00368 // these blank widgets make the All Day Event box line up with the agenda 00369 dummyAllDayRight->setFixedWidth(mAgenda->verticalScrollBar()->width()); 00370 dummyAgendaRight->setFixedWidth(mAgenda->verticalScrollBar()->width()); 00371 } 00372 00373 updateTimeBarWidth(); 00374 00375 // Scrolling 00376 connect(mAgenda->verticalScrollBar(),TQT_SIGNAL(valueChanged(int)), 00377 mTimeLabels, TQT_SLOT(positionChanged())); 00378 00379 connect( mAgenda, 00380 TQT_SIGNAL( zoomView( const int, const TQPoint & ,const Qt::Orientation ) ), 00381 TQT_SLOT( zoomView( const int, const TQPoint &, const Qt::Orientation ) ) ); 00382 00383 connect(mTimeLabels->verticalScrollBar(),TQT_SIGNAL(valueChanged(int)), 00384 TQT_SLOT(setContentsPos(int))); 00385 00386 // Create Events, depends on type of agenda 00387 connect( mAgenda, TQT_SIGNAL(newTimeSpanSignal(const TQPoint &, const TQPoint &)), 00388 TQT_SLOT(newTimeSpanSelected(const TQPoint &, const TQPoint &))); 00389 connect( mAllDayAgenda, TQT_SIGNAL(newTimeSpanSignal(const TQPoint &, const TQPoint &)), 00390 TQT_SLOT(newTimeSpanSelectedAllDay(const TQPoint &, const TQPoint &))); 00391 00392 // event indicator update 00393 connect( mAgenda, TQT_SIGNAL(lowerYChanged(int)), 00394 TQT_SLOT(updateEventIndicatorTop(int))); 00395 connect( mAgenda, TQT_SIGNAL(upperYChanged(int)), 00396 TQT_SLOT(updateEventIndicatorBottom(int))); 00397 00398 if ( !readOnly() ) { 00399 connectAgenda( mAgenda, mAgendaPopup, mAllDayAgenda ); 00400 connectAgenda( mAllDayAgenda, mAllDayAgendaPopup, mAgenda); 00401 } 00402 00403 if ( cal ) { 00404 cal->registerObserver( this ); 00405 } 00406 } 00407 00408 00409 KOAgendaView::~KOAgendaView() 00410 { 00411 if ( calendar() ) 00412 calendar()->unregisterObserver( this ); 00413 delete mAgendaPopup; 00414 delete mAllDayAgendaPopup; 00415 } 00416 00417 void KOAgendaView::connectAgenda( KOAgenda *agenda, TQPopupMenu *popup, 00418 KOAgenda *otherAgenda ) 00419 { 00420 connect( agenda, TQT_SIGNAL(showIncidencePopupSignal(Calendar *,Incidence *,const TQDate &)), 00421 popup, TQT_SLOT(showIncidencePopup(Calendar *,Incidence *,const TQDate &)) ); 00422 00423 connect( agenda, TQT_SIGNAL(showNewEventPopupSignal()), 00424 TQT_SLOT(showNewEventPopup()) ); 00425 00426 00427 // Create/Show/Edit/Delete Event 00428 connect( agenda, TQT_SIGNAL(newEventSignal(ResourceCalendar *,const TQString &)), 00429 TQT_SIGNAL(newEventSignal(ResourceCalendar *,const TQString &)) ); 00430 00431 connect( agenda, TQT_SIGNAL(newStartSelectSignal()), 00432 otherAgenda, TQT_SLOT(clearSelection()) ); 00433 connect( agenda, TQT_SIGNAL(newStartSelectSignal()), 00434 TQT_SIGNAL(timeSpanSelectionChanged()) ); 00435 00436 connect( agenda, TQT_SIGNAL(editIncidenceSignal(Incidence *,const TQDate &)), 00437 TQT_SIGNAL(editIncidenceSignal(Incidence *,const TQDate &)) ); 00438 connect( agenda, TQT_SIGNAL(showIncidenceSignal(Incidence *,const TQDate &)), 00439 TQT_SIGNAL(showIncidenceSignal(Incidence *,const TQDate &)) ); 00440 connect( agenda, TQT_SIGNAL(deleteIncidenceSignal(Incidence *)), 00441 TQT_SIGNAL(deleteIncidenceSignal(Incidence *)) ); 00442 00443 connect( agenda, TQT_SIGNAL(startMultiModify(const TQString &)), 00444 TQT_SIGNAL(startMultiModify(const TQString &)) ); 00445 connect( agenda, TQT_SIGNAL(endMultiModify()), 00446 TQT_SIGNAL(endMultiModify()) ); 00447 00448 connect( agenda, TQT_SIGNAL(itemModified(KOAgendaItem *)), 00449 TQT_SLOT(updateEventDates(KOAgendaItem *)) ); 00450 00451 connect( agenda, TQT_SIGNAL(enableAgendaUpdate(bool)), 00452 TQT_SLOT(enableAgendaUpdate(bool)) ); 00453 00454 // drag signals 00455 connect( agenda, TQT_SIGNAL(startDragSignal(Incidence *)), 00456 TQT_SLOT(startDrag(Incidence *)) ); 00457 00458 // synchronize selections 00459 connect( agenda, TQT_SIGNAL(incidenceSelected(Incidence *,const TQDate &)), 00460 otherAgenda, TQT_SLOT(deselectItem()) ); 00461 connect( agenda, TQT_SIGNAL(incidenceSelected(Incidence *,const TQDate &)), 00462 TQT_SIGNAL(incidenceSelected(Incidence *,const TQDate &)) ); 00463 00464 // rescheduling of todos by d'n'd 00465 connect( agenda, TQT_SIGNAL(droppedToDo(Todo *,const TQPoint &,bool)), 00466 TQT_SLOT(slotTodoDropped(Todo *,const TQPoint &,bool)) ); 00467 00468 } 00469 00470 void KOAgendaView::zoomInVertically( ) 00471 { 00472 if ( !mIsSideBySide ) 00473 KOPrefs::instance()->mHourSize++; 00474 mAgenda->updateConfig(); 00475 mAgenda->checkScrollBoundaries(); 00476 00477 mTimeLabels->updateConfig(); 00478 mTimeLabels->positionChanged(); 00479 mTimeLabels->repaint(); 00480 00481 updateView(); 00482 } 00483 00484 void KOAgendaView::zoomOutVertically( ) 00485 { 00486 00487 if ( KOPrefs::instance()->mHourSize > 4 || mIsSideBySide ) { 00488 00489 if ( !mIsSideBySide ) 00490 KOPrefs::instance()->mHourSize--; 00491 mAgenda->updateConfig(); 00492 mAgenda->checkScrollBoundaries(); 00493 00494 mTimeLabels->updateConfig(); 00495 mTimeLabels->positionChanged(); 00496 mTimeLabels->repaint(); 00497 00498 updateView(); 00499 } 00500 } 00501 00502 void KOAgendaView::zoomInHorizontally( const TQDate &date) 00503 { 00504 TQDate begin; 00505 TQDate newBegin; 00506 TQDate dateToZoom = date; 00507 int ndays,count; 00508 00509 begin = mSelectedDates.first(); 00510 ndays = begin.daysTo( mSelectedDates.last() ); 00511 00512 // zoom with Action and are there a selected Incidence?, Yes, I zoom in to it. 00513 if ( ! dateToZoom.isValid () ) 00514 dateToZoom=mAgenda->selectedIncidenceDate(); 00515 00516 if( !dateToZoom.isValid() ) { 00517 if ( ndays > 1 ) { 00518 newBegin=begin.addDays(1); 00519 count = ndays-1; 00520 emit zoomViewHorizontally ( newBegin , count ); 00521 } 00522 } else { 00523 if ( ndays <= 2 ) { 00524 newBegin = dateToZoom; 00525 count = 1; 00526 } else { 00527 newBegin = dateToZoom.addDays( -ndays/2 +1 ); 00528 count = ndays -1 ; 00529 } 00530 emit zoomViewHorizontally ( newBegin , count ); 00531 } 00532 } 00533 00534 void KOAgendaView::zoomOutHorizontally( const TQDate &date ) 00535 { 00536 TQDate begin; 00537 TQDate newBegin; 00538 TQDate dateToZoom = date; 00539 int ndays,count; 00540 00541 begin = mSelectedDates.first(); 00542 ndays = begin.daysTo( mSelectedDates.last() ); 00543 00544 // zoom with Action and are there a selected Incidence?, Yes, I zoom out to it. 00545 if ( ! dateToZoom.isValid () ) 00546 dateToZoom=mAgenda->selectedIncidenceDate(); 00547 00548 if ( !dateToZoom.isValid() ) { 00549 newBegin = begin.addDays(-1); 00550 count = ndays+3 ; 00551 } else { 00552 newBegin = dateToZoom.addDays( -ndays/2-1 ); 00553 count = ndays+3; 00554 } 00555 00556 if ( abs( count ) >= 31 ) 00557 kdDebug(5850) << "change to the mounth view?"<<endl; 00558 else 00559 //We want to center the date 00560 emit zoomViewHorizontally( newBegin, count ); 00561 } 00562 00563 void KOAgendaView::zoomView( const int delta, const TQPoint &pos, 00564 const Qt::Orientation orient ) 00565 { 00566 static TQDate zoomDate; 00567 static TQTimer *t = new TQTimer( this ); 00568 00569 00570 //Zoom to the selected incidence, on the other way 00571 // zoom to the date on screen after the first mousewheel move. 00572 if ( orient == Qt::Horizontal ) { 00573 TQDate date=mAgenda->selectedIncidenceDate(); 00574 if ( date.isValid() ) 00575 zoomDate=date; 00576 else{ 00577 if ( !t->isActive() ) { 00578 zoomDate= mSelectedDates[pos.x()]; 00579 } 00580 t->start ( 1000,true ); 00581 } 00582 if ( delta > 0 ) 00583 zoomOutHorizontally( zoomDate ); 00584 else 00585 zoomInHorizontally( zoomDate ); 00586 } else { 00587 // Qt::Vertical zoom 00588 TQPoint posConstentsOld = mAgenda->gridToContents(pos); 00589 if ( delta > 0 ) { 00590 zoomOutVertically(); 00591 } else { 00592 zoomInVertically(); 00593 } 00594 TQPoint posConstentsNew = mAgenda->gridToContents(pos); 00595 mAgenda->scrollBy( 0, posConstentsNew.y() - posConstentsOld.y() ); 00596 } 00597 } 00598 00599 void KOAgendaView::createDayLabels( bool force ) 00600 { 00601 // kdDebug(5850) << "KOAgendaView::createDayLabels()" << endl; 00602 00603 // Check if mSelectedDates has changed, if not just return 00604 // Removes some flickering and gains speed (since this is called by each updateView()) 00605 if ( !force && mSaveSelectedDates == mSelectedDates ) { 00606 return; 00607 } 00608 mSaveSelectedDates = mSelectedDates; 00609 00610 delete mDayLabels; 00611 mDateDayLabels.clear(); 00612 00613 mDayLabels = new TQFrame (mDayLabelsFrame); 00614 mLayoutDayLabels = new TQHBoxLayout(mDayLabels); 00615 if ( !mIsSideBySide ) 00616 mLayoutDayLabels->addSpacing(mTimeLabels->width()); 00617 00618 const KCalendarSystem*calsys=KOGlobals::self()->calendarSystem(); 00619 00620 DateList::ConstIterator dit; 00621 for( dit = mSelectedDates.begin(); dit != mSelectedDates.end(); ++dit ) { 00622 TQDate date = *dit; 00623 TQBoxLayout *dayLayout = new TQVBoxLayout(mLayoutDayLabels); 00624 mLayoutDayLabels->setStretchFactor(dayLayout, 1); 00625 // dayLayout->setMinimumWidth(1); 00626 00627 int dW = calsys->dayOfWeek(date); 00628 TQString veryLongStr = KGlobal::locale()->formatDate( date ); 00629 TQString longstr = i18n( "short_weekday date (e.g. Mon 13)","%1 %2" ) 00630 .arg( calsys->weekDayName( dW, true ) ) 00631 .arg( calsys->day(date) ); 00632 TQString shortstr = TQString::number(calsys->day(date)); 00633 00634 KOAlternateLabel *dayLabel = new KOAlternateLabel(shortstr, 00635 longstr, veryLongStr, mDayLabels); 00636 dayLabel->useShortText(); // will be recalculated in updateDayLabelSizes() anyway 00637 dayLabel->setMinimumWidth(1); 00638 dayLabel->setAlignment(TQLabel::AlignHCenter); 00639 if (date == TQDate::currentDate()) { 00640 TQFont font = dayLabel->font(); 00641 font.setBold(true); 00642 dayLabel->setFont(font); 00643 } 00644 dayLayout->addWidget(dayLabel); 00645 mDateDayLabels.append( dayLabel ); 00646 00647 // if a holiday region is selected, show the holiday name 00648 TQStringList texts = KOGlobals::self()->holiday( date ); 00649 TQStringList::ConstIterator textit = texts.begin(); 00650 for ( ; textit != texts.end(); ++textit ) { 00651 // use a KOAlternateLabel so when the text doesn't fit any more a tooltip is used 00652 KOAlternateLabel*label = new KOAlternateLabel( (*textit), (*textit), TQString(), mDayLabels ); 00653 label->setMinimumWidth(1); 00654 label->setAlignment(AlignCenter); 00655 dayLayout->addWidget(label); 00656 } 00657 00658 #ifndef KORG_NOPLUGINS 00659 CalendarDecoration::List cds = KOCore::self()->calendarDecorations(); 00660 CalendarDecoration *it; 00661 for(it = cds.first(); it; it = cds.next()) { 00662 TQString text = it->shortText( date ); 00663 if ( !text.isEmpty() ) { 00664 // use a KOAlternateLabel so when the text doesn't fit any more a tooltip is used 00665 KOAlternateLabel*label = new KOAlternateLabel( text, text, TQString(), mDayLabels ); 00666 label->setMinimumWidth(1); 00667 label->setAlignment(AlignCenter); 00668 dayLayout->addWidget(label); 00669 } 00670 } 00671 00672 for(it = cds.first(); it; it = cds.next()) { 00673 TQWidget *wid = it->smallWidget(mDayLabels,date); 00674 if ( wid ) { 00675 // wid->setHeight(20); 00676 dayLayout->addWidget(wid); 00677 } 00678 } 00679 #endif 00680 } 00681 00682 if ( !mIsSideBySide ) 00683 mLayoutDayLabels->addSpacing(mAgenda->verticalScrollBar()->width()); 00684 mDayLabels->show(); 00685 TQTimer::singleShot( 0, this, TQT_SLOT( updateDayLabelSizes() ) ); 00686 } 00687 00688 void KOAgendaView::enableAgendaUpdate( bool enable ) 00689 { 00690 mAllowAgendaUpdate = enable; 00691 } 00692 00693 int KOAgendaView::maxDatesHint() 00694 { 00695 // Not sure about the max number of events, so return 0 for now. 00696 return 0; 00697 } 00698 00699 int KOAgendaView::currentDateCount() 00700 { 00701 return mSelectedDates.count(); 00702 } 00703 00704 Incidence::List KOAgendaView::selectedIncidences() 00705 { 00706 Incidence::List selected; 00707 Incidence *incidence; 00708 00709 incidence = mAgenda->selectedIncidence(); 00710 if (incidence) selected.append(incidence); 00711 00712 incidence = mAllDayAgenda->selectedIncidence(); 00713 if (incidence) selected.append(incidence); 00714 00715 return selected; 00716 } 00717 00718 DateList KOAgendaView::selectedIncidenceDates() 00719 { 00720 DateList selected; 00721 TQDate qd; 00722 00723 qd = mAgenda->selectedIncidenceDate(); 00724 if (qd.isValid()) selected.append(qd); 00725 00726 qd = mAllDayAgenda->selectedIncidenceDate(); 00727 if (qd.isValid()) selected.append(qd); 00728 00729 return selected; 00730 } 00731 00732 bool KOAgendaView::eventDurationHint( TQDateTime &startDt, TQDateTime &endDt, 00733 bool &allDay ) 00734 { 00735 if ( selectionStart().isValid() ) { 00736 TQDateTime start = selectionStart(); 00737 TQDateTime end = selectionEnd(); 00738 00739 if ( start.secsTo( end ) == 15*60 ) { 00740 // One cell in the agenda view selected, e.g. 00741 // because of a double-click, => Use the default duration 00742 TQTime defaultDuration( KOPrefs::instance()->mDefaultDuration.time() ); 00743 int addSecs = ( defaultDuration.hour()*3600 ) + 00744 ( defaultDuration.minute()*60 ); 00745 end = start.addSecs( addSecs ); 00746 } 00747 00748 startDt = start; 00749 endDt = end; 00750 allDay = selectedIsAllDay(); 00751 return true; 00752 } 00753 return false; 00754 } 00755 00757 bool KOAgendaView::selectedIsSingleCell() 00758 { 00759 if ( !selectionStart().isValid() || !selectionEnd().isValid() ) return false; 00760 00761 if (selectedIsAllDay()) { 00762 int days = selectionStart().daysTo(selectionEnd()); 00763 return ( days < 1 ); 00764 } else { 00765 int secs = selectionStart().secsTo(selectionEnd()); 00766 return ( secs <= 24*60*60/mAgenda->rows() ); 00767 } 00768 } 00769 00770 00771 void KOAgendaView::updateView() 00772 { 00773 // kdDebug(5850) << "KOAgendaView::updateView()" << endl; 00774 fillAgenda(); 00775 } 00776 00777 00778 /* 00779 Update configuration settings for the agenda view. This method is not 00780 complete. 00781 */ 00782 void KOAgendaView::updateConfig() 00783 { 00784 // kdDebug(5850) << "KOAgendaView::updateConfig()" << endl; 00785 00786 // update config for children 00787 mTimeLabels->updateConfig(); 00788 mAgenda->updateConfig(); 00789 mAllDayAgenda->updateConfig(); 00790 00791 // widget synchronization 00792 // FIXME: find a better way, maybe signal/slot 00793 mTimeLabels->positionChanged(); 00794 00795 // for some reason, this needs to be called explicitly 00796 mTimeLabels->repaint(); 00797 00798 updateTimeBarWidth(); 00799 00800 // ToolTips displaying summary of events 00801 KOAgendaItem::toolTipGroup()->setEnabled(KOPrefs::instance() 00802 ->mEnableToolTips); 00803 00804 setHolidayMasks(); 00805 00806 createDayLabels( true ); 00807 00808 updateView(); 00809 } 00810 00811 void KOAgendaView::updateTimeBarWidth() 00812 { 00813 int width; 00814 00815 width = mDummyAllDayLeft->fontMetrics().width( i18n("All Day") ); 00816 width = TQMAX( width, mTimeLabels->width() ); 00817 00818 mDummyAllDayLeft->setFixedWidth( width ); 00819 mTimeLabels->setFixedWidth( width ); 00820 } 00821 00822 void KOAgendaView::updateDayLabelSizes() 00823 { 00824 // First, calculate the maximum text type that fits for all labels 00825 KOAlternateLabel::TextType overallType = KOAlternateLabel::Extensive; 00826 TQPtrList<KOAlternateLabel>::const_iterator it = mDateDayLabels.constBegin(); 00827 for( ; it != mDateDayLabels.constEnd(); it++ ) { 00828 KOAlternateLabel::TextType type = (*it)->largestFittingTextType(); 00829 if ( type < overallType ) 00830 overallType = type; 00831 } 00832 00833 // Then, set that maximum text type to all the labels 00834 it = mDateDayLabels.constBegin(); 00835 for( ; it != mDateDayLabels.constEnd(); it++ ) { 00836 (*it)->setFixedType( overallType ); 00837 } 00838 } 00839 00840 void KOAgendaView::resizeEvent( TQResizeEvent *resizeEvent ) 00841 { 00842 updateDayLabelSizes(); 00843 KOrg::AgendaView::resizeEvent( resizeEvent ); 00844 } 00845 00846 void KOAgendaView::updateEventDates( KOAgendaItem *item ) 00847 { 00848 kdDebug(5850) << "KOAgendaView::updateEventDates(): " << item->text() 00849 << "; item->cellXLeft(): " << item->cellXLeft() 00850 << "; item->cellYTop(): " << item->cellYTop() 00851 << "; item->lastMultiItem(): " << item->lastMultiItem() 00852 << "; item->itemPos(): " << item->itemPos() 00853 << "; item->itemCount(): " << item->itemCount() 00854 << endl; 00855 00856 TQDateTime startDt, endDt; 00857 00858 // Start date of this incidence, calculate the offset from it (so recurring and 00859 // non-recurring items can be treated exactly the same, we never need to check 00860 // for doesRecur(), because we only move the start day by the number of days the 00861 // agenda item was really moved. Smart, isn't it?) 00862 TQDate thisDate; 00863 if ( item->cellXLeft() < 0 ) { 00864 thisDate = ( mSelectedDates.first() ).addDays( item->cellXLeft() ); 00865 } else { 00866 thisDate = mSelectedDates[ item->cellXLeft() ]; 00867 } 00868 TQDate oldThisDate( item->itemDate() ); 00869 const int daysOffset = oldThisDate.daysTo( thisDate ); 00870 int daysLength = 0; 00871 00872 // startDt.setDate( startDate ); 00873 00874 Incidence *incidence = item->incidence(); 00875 if ( !incidence ) { 00876 return; 00877 } 00878 if ( !mChanger || 00879 !mChanger->beginChange( incidence, resourceCalendar(), subResourceCalendar() ) ) { 00880 return; 00881 } 00882 Incidence *oldIncidence = incidence->clone(); 00883 00884 TQTime startTime( 0, 0, 0 ), endTime( 0, 0, 0 ); 00885 if ( incidence->doesFloat() ) { 00886 daysLength = item->cellWidth() - 1; 00887 } else { 00888 startTime = mAgenda->gyToTime( item->cellYTop() ); 00889 if ( item->lastMultiItem() ) { 00890 endTime = mAgenda->gyToTime( item->lastMultiItem()->cellYBottom() + 1 ); 00891 daysLength = item->lastMultiItem()->cellXLeft() - item->cellXLeft(); 00892 kdDebug(5850) << "item->lastMultiItem()->cellXLeft(): " << item->lastMultiItem()->cellXLeft() 00893 << endl; 00894 } else if ( item->itemPos() == item->itemCount() && item->itemCount() > 1 ) { 00895 /* multiitem handling in agenda assumes two things: 00896 - The start (first KOAgendaItem) is always visible. 00897 - The first KOAgendaItem of the incidence has a non-null item->lastMultiItem() 00898 pointing to the last KOagendaItem. 00899 00900 But those aren't always met, for example when in day-view. 00901 kolab/issue4417 00902 */ 00903 00904 // Cornercase 1: - Resizing the end of the event but the start isn't visible 00905 endTime = mAgenda->gyToTime( item->cellYBottom() + 1 ); 00906 daysLength = item->itemCount() - 1; 00907 startTime = incidence->dtStart().time(); 00908 } else if ( item->itemPos() == 1 && item->itemCount() > 1 ) { 00909 // Cornercase 2: - Resizing the start of the event but the end isn't visible 00910 endTime = incidence->dtEnd().time(); 00911 daysLength = item->itemCount() - 1; 00912 } else { 00913 endTime = mAgenda->gyToTime( item->cellYBottom() + 1 ); 00914 } 00915 } 00916 00917 kdDebug(5850) << "daysLength: " << daysLength << "; startTime: " << startTime 00918 << "; endTime: " << endTime << "; thisDate: " << thisDate 00919 << "; incidence->dtStart(): " << incidence->dtStart() << endl; 00920 00921 // FIXME: use a visitor here 00922 if ( incidence->type() == "Event" ) { 00923 startDt = incidence->dtStart(); 00924 startDt = startDt.addDays( daysOffset ); 00925 startDt.setTime( startTime ); 00926 endDt = startDt.addDays( daysLength ); 00927 endDt.setTime( endTime ); 00928 Event* ev = static_cast<Event*>( incidence ); 00929 if ( incidence->dtStart() == startDt && ev->dtEnd() == endDt ) { 00930 // No change 00931 delete oldIncidence; 00932 return; 00933 } 00934 incidence->setDtStart( startDt ); 00935 ev->setDtEnd( endDt ); 00936 } else if ( incidence->type() == "Todo" ) { 00937 Todo *td = static_cast<Todo*>( incidence ); 00938 startDt = td->hasStartDate() ? td->dtStart() : td->dtDue(); 00939 startDt = thisDate.addDays( td->dtDue().daysTo( startDt ) ); 00940 startDt.setTime( startTime ); 00941 endDt.setDate( thisDate ); 00942 endDt.setTime( endTime ); 00943 00944 if( td->dtDue() == endDt ) { 00945 // No change 00946 delete oldIncidence; 00947 return; 00948 } 00949 } 00950 // FIXME: Adjusting the recurrence should really go to CalendarView so this 00951 // functionality will also be available in other views! 00952 // TODO_Recurrence: This does not belong here, and I'm not really sure 00953 // how it's supposed to work anyway. 00954 /* 00955 Recurrence *recur = incidence->recurrence(); 00956 if ( recur->doesRecur() && daysOffset != 0 ) { 00957 switch ( recur->recurrenceType() ) { 00958 case Recurrence::rYearlyPos: { 00959 int freq = recur->frequency(); 00960 int duration = recur->duration(); 00961 TQDate endDt( recur->endDate() ); 00962 bool negative = false; 00963 00964 TQPtrList<Recurrence::rMonthPos> monthPos( recur->yearMonthPositions() ); 00965 if ( monthPos.first() ) { 00966 negative = monthPos.first()->negative; 00967 } 00968 TQBitArray days( 7 ); 00969 int pos = 0; 00970 days.fill( false ); 00971 days.setBit( thisDate.dayOfWeek() - 1 ); 00972 if ( negative ) { 00973 pos = - ( thisDate.daysInMonth() - thisDate.day() - 1 ) / 7 - 1; 00974 } else { 00975 pos = ( thisDate.day()-1 ) / 7 + 1; 00976 } 00977 // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again 00978 recur->unsetRecurs(); 00979 if ( duration != 0 ) { 00980 recur->setYearly( Recurrence::rYearlyPos, freq, duration ); 00981 } else { 00982 recur->setYearly( Recurrence::rYearlyPos, freq, endDt ); 00983 } 00984 recur->addYearlyMonthPos( pos, days ); 00985 recur->addYearlyNum( thisDate.month() ); 00986 00987 break; } 00988 case Recurrence::rYearlyDay: { 00989 int freq = recur->frequency(); 00990 int duration = recur->duration(); 00991 TQDate endDt( recur->endDate() ); 00992 // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again 00993 recur->unsetRecurs(); 00994 if ( duration == 0 ) { // end by date 00995 recur->setYearly( Recurrence::rYearlyDay, freq, endDt ); 00996 } else { 00997 recur->setYearly( Recurrence::rYearlyDay, freq, duration ); 00998 } 00999 recur->addYearlyNum( thisDate.dayOfYear() ); 01000 break; } 01001 case Recurrence::rYearlyMonth: { 01002 int freq = recur->frequency(); 01003 int duration = recur->duration(); 01004 TQDate endDt( recur->endDate() ); 01005 // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again 01006 recur->unsetRecurs(); 01007 if ( duration != 0 ) { 01008 recur->setYearlyByDate( thisDate.day(), recur->feb29YearlyType(), freq, duration ); 01009 } else { 01010 recur->setYearlyByDate( thisDate.day(), recur->feb29YearlyType(), freq, endDt ); 01011 } 01012 recur->addYearlyNum( thisDate.month() ); 01013 break; } 01014 case Recurrence::rMonthlyPos: { 01015 int freq = recur->frequency(); 01016 int duration = recur->duration(); 01017 TQDate endDt( recur->endDate() ); 01018 TQPtrList<Recurrence::rMonthPos> monthPos( recur->monthPositions() ); 01019 if ( !monthPos.isEmpty() ) { 01020 // FIXME: How shall I adapt the day x of week Y if we move the date across month borders??? 01021 // for now, just use the date of the moved item and assume the recurrence only occurs on that day. 01022 // That's fine for korganizer, but might mess up other organizers. 01023 TQBitArray rDays( 7 ); 01024 rDays = monthPos.first()->rDays; 01025 bool negative = monthPos.first()->negative; 01026 int newPos; 01027 rDays.fill( false ); 01028 rDays.setBit( thisDate.dayOfWeek() - 1 ); 01029 if ( negative ) { 01030 newPos = - ( thisDate.daysInMonth() - thisDate.day() - 1 ) / 7 - 1; 01031 } else { 01032 newPos = ( thisDate.day()-1 ) / 7 + 1; 01033 } 01034 01035 // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again 01036 recur->unsetRecurs(); 01037 if ( duration == 0 ) { // end by date 01038 recur->setMonthly( Recurrence::rMonthlyPos, freq, endDt ); 01039 } else { 01040 recur->setMonthly( Recurrence::rMonthlyPos, freq, duration ); 01041 } 01042 recur->addMonthlyPos( newPos, rDays ); 01043 } 01044 break;} 01045 case Recurrence::rMonthlyDay: { 01046 int freq = recur->frequency(); 01047 int duration = recur->duration(); 01048 TQDate endDt( recur->endDate() ); 01049 TQPtrList<int> monthDays( recur->monthDays() ); 01050 // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again 01051 recur->unsetRecurs(); 01052 if ( duration == 0 ) { // end by date 01053 recur->setMonthly( Recurrence::rMonthlyDay, freq, endDt ); 01054 } else { 01055 recur->setMonthly( Recurrence::rMonthlyDay, freq, duration ); 01056 } 01057 // FIXME: How shall I adapt the n-th day if we move the date across month borders??? 01058 // for now, just use the date of the moved item and assume the recurrence only occurs on that day. 01059 // That's fine for korganizer, but might mess up other organizers. 01060 recur->addMonthlyDay( thisDate.day() ); 01061 01062 break;} 01063 case Recurrence::rWeekly: { 01064 TQBitArray days(7), oldDays( recur->days() ); 01065 int offset = daysOffset % 7; 01066 if ( offset<0 ) offset = (offset+7) % 7; 01067 // rotate the days 01068 for (int d=0; d<7; d++ ) { 01069 days.setBit( (d+offset) % 7, oldDays.at(d) ); 01070 } 01071 if ( recur->duration() == 0 ) { // end by date 01072 recur->setWeekly( recur->frequency(), days, recur->endDate(), recur->weekStart() ); 01073 } else { // duration or no end 01074 recur->setWeekly( recur->frequency(), days, recur->duration(), recur->weekStart() ); 01075 } 01076 break;} 01077 // nothing to be done for the following: 01078 case Recurrence::rDaily: 01079 case Recurrence::rHourly: 01080 case Recurrence::rMinutely: 01081 case Recurrence::rNone: 01082 default: 01083 break; 01084 } 01085 if ( recur->duration()==0 ) { // end by date 01086 recur->setEndDate( recur->endDate().addDays( daysOffset ) ); 01087 } 01088 KMessageBox::information( this, i18n("A recurring calendar item was moved " 01089 "to a different day. The recurrence settings " 01090 "have been updated with that move. Please check " 01091 "them in the editor."), 01092 i18n("Recurrence Moved"), 01093 "RecurrenceMoveInAgendaWarning" ); 01094 }*/ 01095 01096 // FIXME: use a visitor here 01097 if ( incidence->type() == "Event" ) { 01098 incidence->setDtStart( startDt ); 01099 static_cast<Event*>( incidence )->setDtEnd( endDt ); 01100 } else if ( incidence->type() == "Todo" ) { 01101 Todo *td = static_cast<Todo*>( incidence ); 01102 if ( td->hasStartDate() ) { 01103 td->setDtStart( startDt ); 01104 } 01105 td->setDtDue( endDt ); 01106 } 01107 01108 item->setItemDate( startDt.date() ); 01109 01110 KOIncidenceToolTip::remove( item ); 01111 KOIncidenceToolTip::add( item, calendar(), incidence, thisDate, KOAgendaItem::toolTipGroup() ); 01112 01113 const bool result = mChanger->changeIncidence( oldIncidence, incidence, 01114 KOGlobals::DATE_MODIFIED, this ); 01115 mChanger->endChange( incidence, resourceCalendar(), subResourceCalendar() ); 01116 delete oldIncidence; 01117 01118 if ( !result ) { 01119 mPendingChanges = true; 01120 TQTimer::singleShot( 0, this, TQT_SLOT(updateView()) ); 01121 return; 01122 } 01123 01124 // don't update the agenda as the item already has the correct coordinates. 01125 // an update would delete the current item and recreate it, but we are still 01126 // using a pointer to that item! => CRASH 01127 enableAgendaUpdate( false ); 01128 // We need to do this in a timer to make sure we are not deleting the item 01129 // we are currently working on, which would lead to crashes 01130 // Only the actually moved agenda item is already at the correct position and mustn't be 01131 // recreated. All others have to!!! 01132 if ( incidence->doesRecur() ) { 01133 mUpdateItem = incidence; 01134 TQTimer::singleShot( 0, this, TQT_SLOT( doUpdateItem() ) ); 01135 } 01136 01137 enableAgendaUpdate( true ); 01138 01139 // kdDebug(5850) << "KOAgendaView::updateEventDates() done " << endl; 01140 } 01141 01142 void KOAgendaView::doUpdateItem() 01143 { 01144 if ( mUpdateItem ) { 01145 changeIncidenceDisplay( mUpdateItem, KOGlobals::INCIDENCEEDITED ); 01146 mUpdateItem = 0; 01147 } 01148 } 01149 01150 01151 01152 void KOAgendaView::showDates( const TQDate &start, const TQDate &end ) 01153 { 01154 // kdDebug(5850) << "KOAgendaView::selectDates" << endl; 01155 if ( !mSelectedDates.isEmpty() && mSelectedDates.first() == start 01156 && mSelectedDates.last() == end && !mPendingChanges ) 01157 return; 01158 01159 mSelectedDates.clear(); 01160 01161 TQDate d = start; 01162 while ( d <= end ) { 01163 mSelectedDates.append( d ); 01164 d = d.addDays( 1 ); 01165 } 01166 01167 mAreDatesInitialized = true; 01168 01169 // and update the view 01170 fillAgenda(); 01171 } 01172 01173 01174 void KOAgendaView::showIncidences( const Incidence::List &, const TQDate & ) 01175 { 01176 kdDebug(5850) << "KOAgendaView::showIncidences( const Incidence::List & ) is not yet implemented" << endl; 01177 } 01178 01179 void KOAgendaView::insertIncidence( Incidence *incidence, const TQDate &curDate ) 01180 { 01181 if ( !filterByResource( incidence ) ) { 01182 return; 01183 } 01184 01185 // FIXME: Use a visitor here, or some other method to get rid of the dynamic_cast's 01186 Event *event = dynamic_cast<Event *>( incidence ); 01187 Todo *todo = dynamic_cast<Todo *>( incidence ); 01188 01189 int curCol = mSelectedDates.first().daysTo( curDate ); 01190 01191 // In case incidence->dtStart() isn't visible (crosses bounderies) 01192 if ( curCol < 0 ) { 01193 curCol = 0; 01194 } 01195 01196 // The date for the event is not displayed, just ignore it 01197 if ( curCol >= int( mSelectedDates.count() ) ) { 01198 return; 01199 } 01200 01201 // Default values, which can never be reached 01202 mMinY[curCol] = mAgenda->timeToY( TQTime( 23, 59 ) ) + 1; 01203 mMaxY[curCol] = mAgenda->timeToY( TQTime( 0, 0 ) ) - 1; 01204 01205 int beginX; 01206 int endX; 01207 TQDate columnDate; 01208 if ( event ) { 01209 TQDate firstVisibleDate = mSelectedDates.first(); 01210 // its crossing bounderies, lets calculate beginX and endX 01211 if ( curDate < firstVisibleDate ) { 01212 beginX = curCol + firstVisibleDate.daysTo( curDate ); 01213 endX = beginX + event->dtStart().daysTo( event->dtEnd() ); 01214 columnDate = firstVisibleDate; 01215 } else { 01216 beginX = curCol; 01217 endX = beginX + event->dtStart().daysTo( event->dtEnd() ); 01218 columnDate = curDate; 01219 } 01220 } else if ( todo ) { 01221 if ( !todo->hasDueDate() ) { 01222 return; // todo shall not be displayed if it has no date 01223 } 01224 columnDate = curDate; 01225 beginX = endX = curCol; 01226 01227 } else { 01228 return; 01229 } 01230 if ( todo && todo->isOverdue() ) { 01231 mAllDayAgenda->insertAllDayItem( incidence, columnDate, curCol, curCol ); 01232 } else if ( incidence->doesFloat() || 01233 ( todo && 01234 !todo->dtDue().isValid() ) ) { 01235 mAllDayAgenda->insertAllDayItem( incidence, columnDate, beginX, endX ); 01236 } else if ( event && event->isMultiDay() ) { 01237 int startY = mAgenda->timeToY( event->dtStart().time() ); 01238 TQTime endtime = event->dtEnd().time(); 01239 if ( endtime == TQTime( 0, 0, 0 ) ) { 01240 endtime = TQTime( 23, 59, 59 ); 01241 } 01242 int endY = mAgenda->timeToY( endtime ) - 1; 01243 if ( ( beginX <= 0 && curCol == 0 ) || beginX == curCol ) { 01244 mAgenda->insertMultiItem( event, columnDate, beginX, endX, startY, endY ); 01245 01246 } 01247 if ( beginX == curCol ) { 01248 mMaxY[curCol] = mAgenda->timeToY( TQTime( 23, 59 ) ); 01249 if ( startY < mMinY[curCol] ) { 01250 mMinY[curCol] = startY; 01251 } 01252 } else if ( endX == curCol ) { 01253 mMinY[curCol] = mAgenda->timeToY( TQTime( 0, 0 ) ); 01254 if ( endY > mMaxY[curCol] ) { 01255 mMaxY[curCol] = endY; 01256 } 01257 } else { 01258 mMinY[curCol] = mAgenda->timeToY( TQTime( 0, 0 ) ); 01259 mMaxY[curCol] = mAgenda->timeToY( TQTime( 23, 59 ) ); 01260 } 01261 } else { 01262 int startY = 0, endY = 0; 01263 if ( event ) { 01264 startY = mAgenda->timeToY( incidence->dtStart().time() ); 01265 TQTime endtime = event->dtEnd().time(); 01266 if ( endtime == TQTime( 0, 0, 0 ) ) { 01267 endtime = TQTime( 23, 59, 59 ); 01268 } 01269 endY = mAgenda->timeToY( endtime ) - 1; 01270 } 01271 if ( todo ) { 01272 TQTime t = todo->dtDue().time(); 01273 01274 if ( t == TQTime( 0, 0 ) ) { 01275 t = TQTime( 23, 59 ); 01276 } 01277 01278 int halfHour = 1800; 01279 if ( t.addSecs( -halfHour ) < t ) { 01280 startY = mAgenda->timeToY( t.addSecs( -halfHour ) ); 01281 endY = mAgenda->timeToY( t ) - 1; 01282 } else { 01283 startY = 0; 01284 endY = mAgenda->timeToY( t.addSecs( halfHour ) ) - 1; 01285 } 01286 } 01287 if ( endY < startY ) { 01288 endY = startY; 01289 } 01290 mAgenda->insertItem( incidence, columnDate, curCol, startY, endY, 1, 1 ); 01291 if ( startY < mMinY[curCol] ) { 01292 mMinY[curCol] = startY; 01293 } 01294 if ( endY > mMaxY[curCol] ) { 01295 mMaxY[curCol] = endY; 01296 } 01297 } 01298 } 01299 01300 void KOAgendaView::changeIncidenceDisplayAdded( Incidence *incidence ) 01301 { 01302 Todo *todo = dynamic_cast<Todo *>(incidence); 01303 CalFilter *filter = calendar()->filter(); 01304 if ( ( filter && !filter->filterIncidence( incidence ) ) || 01305 ( ( todo && !KOPrefs::instance()->showAllDayTodo() ) ) ) { 01306 return; 01307 } 01308 01309 displayIncidence( incidence ); 01310 } 01311 01312 void KOAgendaView::changeIncidenceDisplay( Incidence *incidence, int mode ) 01313 { 01314 switch ( mode ) { 01315 case KOGlobals::INCIDENCEADDED: 01316 { 01317 // Add an event. No need to recreate the whole view! 01318 // recreating everything even causes troubles: dropping to the 01319 // day matrix recreates the agenda items, but the evaluation is 01320 // still in an agendaItems' code, which was deleted in the mean time. 01321 // Thus KOrg crashes... 01322 changeIncidenceDisplayAdded( incidence ); 01323 updateEventIndicators(); 01324 break; 01325 } 01326 case KOGlobals::INCIDENCEEDITED: 01327 { 01328 if ( mAllowAgendaUpdate ) { 01329 removeIncidence( incidence ); 01330 changeIncidenceDisplayAdded( incidence ); 01331 } 01332 updateEventIndicators(); 01333 break; 01334 } 01335 case KOGlobals::INCIDENCEDELETED: 01336 { 01337 removeIncidence( incidence ); 01338 updateEventIndicators(); 01339 break; 01340 } 01341 default: 01342 return; 01343 } 01344 01345 // HACK: Update the view if the all-day agenda has been modified. 01346 // Do this because there are some layout problems in the 01347 // all-day agenda that are not easily solved, but clearing 01348 // and redrawing works ok. 01349 if ( incidence->doesFloat() ) { 01350 updateView(); 01351 } 01352 } 01353 01354 void KOAgendaView::fillAgenda( const TQDate & ) 01355 { 01356 fillAgenda(); 01357 } 01358 01359 void KOAgendaView::fillAgenda() 01360 { 01361 if ( !mAreDatesInitialized ) { 01362 return; 01363 } 01364 01365 mPendingChanges = false; 01366 01367 /* Remember the uids of the selected items. In case one of the 01368 * items was deleted and re-added, we want to reselect it. */ 01369 const TQString &selectedAgendaUid = mAgenda->lastSelectedUid(); 01370 const TQString &selectedAllDayAgendaUid = mAllDayAgenda->lastSelectedUid(); 01371 01372 enableAgendaUpdate( true ); 01373 clearView(); 01374 01375 mAllDayAgenda->changeColumns( mSelectedDates.count() ); 01376 mAgenda->changeColumns( mSelectedDates.count() ); 01377 mEventIndicatorTop->changeColumns( mSelectedDates.count() ); 01378 mEventIndicatorBottom->changeColumns( mSelectedDates.count() ); 01379 01380 createDayLabels( false ); 01381 setHolidayMasks(); 01382 01383 mMinY.resize( mSelectedDates.count() ); 01384 mMaxY.resize( mSelectedDates.count() ); 01385 01386 mAgenda->setDateList( mSelectedDates ); 01387 01388 bool somethingReselected = false; 01389 Incidence::List incidences = calendar()->incidences(); 01390 01391 for ( Incidence::List::ConstIterator it = incidences.begin(); it!=incidences.constEnd(); ++it ) { 01392 Incidence *incidence = (*it); 01393 displayIncidence( incidence ); 01394 01395 if( incidence->uid() == selectedAgendaUid && !selectedAgendaUid.isNull() ) { 01396 mAgenda->selectItemByUID( incidence->uid() ); 01397 somethingReselected = true; 01398 } 01399 01400 if( incidence->uid() == selectedAllDayAgendaUid && !selectedAllDayAgendaUid.isNull() ) { 01401 mAllDayAgenda->selectItemByUID( incidence->uid() ); 01402 somethingReselected = true; 01403 } 01404 01405 } 01406 01407 mAgenda->checkScrollBoundaries(); 01408 updateEventIndicators(); 01409 01410 // mAgenda->viewport()->update(); 01411 // mAllDayAgenda->viewport()->update(); 01412 01413 // make invalid 01414 deleteSelectedDateTime(); 01415 01416 if( !somethingReselected ) { 01417 emit incidenceSelected( 0, TQDate() ); 01418 } 01419 } 01420 01421 void KOAgendaView::displayIncidence( Incidence *incidence ) 01422 { 01423 TQDate today = TQDate::currentDate(); 01424 DateTimeList::iterator t; 01425 01426 // FIXME: use a visitor here 01427 Todo *todo = dynamic_cast<Todo *>( incidence ); 01428 Event *event = dynamic_cast<Event *>( incidence ); 01429 01430 TQDateTime firstVisibleDateTime = mSelectedDates.first(); 01431 TQDateTime lastVisibleDateTime = mSelectedDates.last(); 01432 01433 lastVisibleDateTime.setTime( TQTime( 23, 59, 59, 59 ) ); 01434 firstVisibleDateTime.setTime( TQTime( 0, 0 ) ); 01435 DateTimeList dateTimeList; 01436 01437 TQDateTime incDtStart = incidence->dtStart(); 01438 TQDateTime incDtEnd = incidence->dtEnd(); 01439 01440 if ( todo && 01441 ( !KOPrefs::instance()->showAllDayTodo() || !todo->hasDueDate() ) ) { 01442 return; 01443 } 01444 01445 if ( incidence->doesRecur() ) { 01446 int eventDuration = event ? incDtStart.daysTo( incDtEnd ) : 0; 01447 01448 // if there's a multiday event that starts before firstVisibleDateTime but ends after 01449 // lets include it. timesInInterval() ignores incidences that aren't totaly inside 01450 // the range 01451 TQDateTime startDateTimeWithOffset = firstVisibleDateTime.addDays( -eventDuration ); 01452 dateTimeList = 01453 incidence->recurrence()->timesInInterval( startDateTimeWithOffset, 01454 lastVisibleDateTime ); 01455 } else { 01456 TQDateTime dateToAdd; // date to add to our date list 01457 TQDateTime incidenceStart; 01458 TQDateTime incidenceEnd; 01459 01460 if ( todo && todo->hasDueDate() && !todo->isOverdue() ) { 01461 // If it's not overdue it will be shown at the original date (not today) 01462 dateToAdd = todo->dtDue(); 01463 01464 // To-dos are drawn with the bottom of the rectangle at dtDue 01465 // if dtDue is at 00:00, then it should be displayed in the previous day, at 23:59 01466 if ( !todo->doesFloat() && dateToAdd.time() == TQTime( 0, 0 ) ) { 01467 dateToAdd = dateToAdd.addSecs( -1 ); 01468 } 01469 01470 incidenceEnd = dateToAdd; 01471 } else if ( event ) { 01472 dateToAdd = incDtStart; 01473 incidenceEnd = incDtEnd; 01474 } 01475 01476 if ( incidence->doesFloat() ) { 01477 // so comparisons with < > actually work 01478 dateToAdd.setTime( TQTime( 0, 0 ) ); 01479 incidenceEnd.setTime( TQTime( 23, 59, 59, 59 ) ); 01480 } 01481 01482 if ( dateToAdd <= lastVisibleDateTime && incidenceEnd > firstVisibleDateTime ) { 01483 dateTimeList += dateToAdd; 01484 } 01485 } 01486 01487 // ToDo items shall be displayed today if they are already overdude 01488 TQDateTime dateTimeToday = today; 01489 if ( todo && 01490 todo->isOverdue() && 01491 dateTimeToday >= firstVisibleDateTime && 01492 dateTimeToday <= lastVisibleDateTime ) { 01493 01494 bool doAdd = true; 01495 01496 if ( todo->doesRecur() ) { 01497 /* If there's a recurring instance showing up today don't add "today" again 01498 * we don't want the event to appear duplicated */ 01499 for ( t = dateTimeList.begin(); t != dateTimeList.end(); ++t ) { 01500 if ( (*t).date() == today ) { 01501 doAdd = false; 01502 break; 01503 } 01504 } 01505 } 01506 01507 if ( doAdd ) { 01508 dateTimeList += dateTimeToday; 01509 } 01510 } 01511 01512 for ( t = dateTimeList.begin(); t != dateTimeList.end(); ++t ) { 01513 insertIncidence( incidence, (*t).date() ); 01514 } 01515 } 01516 01517 void KOAgendaView::clearView() 01518 { 01519 // kdDebug(5850) << "ClearView" << endl; 01520 mAllDayAgenda->clear(); 01521 mAgenda->clear(); 01522 } 01523 01524 CalPrinterBase::PrintType KOAgendaView::printType() 01525 { 01526 if ( currentDateCount() == 1 ) return CalPrinterBase::Day; 01527 else return CalPrinterBase::Week; 01528 } 01529 01530 void KOAgendaView::updateEventIndicatorTop( int newY ) 01531 { 01532 uint i; 01533 for( i = 0; i < mMinY.size(); ++i ) { 01534 mEventIndicatorTop->enableColumn( i, newY > mMinY[i] ); 01535 } 01536 mEventIndicatorTop->update(); 01537 } 01538 01539 void KOAgendaView::updateEventIndicatorBottom( int newY ) 01540 { 01541 uint i; 01542 for( i = 0; i < mMaxY.size(); ++i ) { 01543 mEventIndicatorBottom->enableColumn( i, newY <= mMaxY[i] ); 01544 } 01545 mEventIndicatorBottom->update(); 01546 } 01547 01548 void KOAgendaView::slotTodoDropped( Todo *todo, const TQPoint &gpos, bool allDay ) 01549 { 01550 if ( gpos.x()<0 || gpos.y()<0 ) return; 01551 TQDate day = mSelectedDates[gpos.x()]; 01552 TQTime time = mAgenda->gyToTime( gpos.y() ); 01553 TQDateTime newTime( day, time ); 01554 01555 if ( todo ) { 01556 Todo *existingTodo = calendar()->todo( todo->uid() ); 01557 if ( existingTodo ) { 01558 kdDebug(5850) << "Drop existing Todo" << endl; 01559 Todo *oldTodo = existingTodo->clone(); 01560 if ( mChanger && 01561 mChanger->beginChange( existingTodo, resourceCalendar(), subResourceCalendar() ) ) { 01562 existingTodo->setDtDue( newTime ); 01563 existingTodo->setFloats( allDay ); 01564 existingTodo->setHasDueDate( true ); 01565 mChanger->changeIncidence( oldTodo, existingTodo, 01566 KOGlobals::DATE_MODIFIED, this ); 01567 mChanger->endChange( existingTodo, resourceCalendar(), subResourceCalendar() ); 01568 } else { 01569 KMessageBox::sorry( this, i18n("Unable to modify this to-do, " 01570 "because it cannot be locked.") ); 01571 } 01572 delete oldTodo; 01573 } else { 01574 kdDebug(5850) << "Drop new Todo" << endl; 01575 todo->setDtDue( newTime ); 01576 todo->setFloats( allDay ); 01577 todo->setHasDueDate( true ); 01578 if ( !mChanger->addIncidence( todo, 0, TQString(), this ) ) { 01579 KODialogManager::errorSaveIncidence( this, todo ); 01580 } 01581 } 01582 } 01583 } 01584 01585 void KOAgendaView::startDrag( Incidence *incidence ) 01586 { 01587 #ifndef KORG_NODND 01588 DndFactory factory( calendar() ); 01589 ICalDrag *vd = factory.createDrag( incidence, this ); 01590 if ( vd->drag() ) { 01591 kdDebug(5850) << "KOAgendaView::startDrag(): Delete drag source" << endl; 01592 } 01593 #endif 01594 } 01595 01596 void KOAgendaView::readSettings() 01597 { 01598 readSettings(KOGlobals::self()->config()); 01599 } 01600 01601 void KOAgendaView::readSettings(KConfig *config) 01602 { 01603 // kdDebug(5850) << "KOAgendaView::readSettings()" << endl; 01604 01605 config->setGroup("Views"); 01606 01607 #ifndef KORG_NOSPLITTER 01608 TQValueList<int> sizes = config->readIntListEntry("Separator AgendaView"); 01609 if (sizes.count() == 2) { 01610 mSplitterAgenda->setSizes(sizes); 01611 } 01612 #endif 01613 01614 updateConfig(); 01615 } 01616 01617 void KOAgendaView::writeSettings(KConfig *config) 01618 { 01619 // kdDebug(5850) << "KOAgendaView::writeSettings()" << endl; 01620 01621 config->setGroup("Views"); 01622 01623 #ifndef KORG_NOSPLITTER 01624 TQValueList<int> list = mSplitterAgenda->sizes(); 01625 config->writeEntry("Separator AgendaView",list); 01626 #endif 01627 } 01628 01629 void KOAgendaView::setHolidayMasks() 01630 { 01631 if ( mSelectedDates.isEmpty() || !mSelectedDates[0].isValid() ) { 01632 return; 01633 } 01634 01635 mHolidayMask.resize( mSelectedDates.count() + 1 ); 01636 01637 for( uint i = 0; i < mSelectedDates.count(); ++i ) { 01638 mHolidayMask[i] = !KOGlobals::self()->isWorkDay( mSelectedDates[ i ] ); 01639 } 01640 01641 // Store the information about the day before the visible area (needed for 01642 // overnight working hours) in the last bit of the mask: 01643 bool showDay = !KOGlobals::self()->isWorkDay( mSelectedDates[ 0 ].addDays( -1 ) ); 01644 mHolidayMask[ mSelectedDates.count() ] = showDay; 01645 01646 mAgenda->setHolidayMask( &mHolidayMask ); 01647 mAllDayAgenda->setHolidayMask( &mHolidayMask ); 01648 } 01649 01650 void KOAgendaView::setContentsPos( int y ) 01651 { 01652 mAgenda->setContentsPos( 0, y ); 01653 } 01654 01655 void KOAgendaView::setExpandedButton( bool expanded ) 01656 { 01657 if ( !mExpandButton ) return; 01658 01659 if ( expanded ) { 01660 mExpandButton->setPixmap( mExpandedPixmap ); 01661 } else { 01662 mExpandButton->setPixmap( mNotExpandedPixmap ); 01663 } 01664 } 01665 01666 void KOAgendaView::clearSelection() 01667 { 01668 mAgenda->deselectItem(); 01669 mAllDayAgenda->deselectItem(); 01670 } 01671 01672 void KOAgendaView::newTimeSpanSelectedAllDay( const TQPoint &start, const TQPoint &end ) 01673 { 01674 newTimeSpanSelected( start, end ); 01675 mTimeSpanInAllDay = true; 01676 } 01677 01678 void KOAgendaView::newTimeSpanSelected( const TQPoint &start, const TQPoint &end ) 01679 { 01680 if (!mSelectedDates.count()) return; 01681 01682 mTimeSpanInAllDay = false; 01683 01684 TQDate dayStart = mSelectedDates[ kClamp( start.x(), 0, (int)mSelectedDates.size() - 1 ) ]; 01685 TQDate dayEnd = mSelectedDates[ kClamp( end.x(), 0, (int)mSelectedDates.size() - 1 ) ]; 01686 01687 TQTime timeStart = mAgenda->gyToTime(start.y()); 01688 TQTime timeEnd = mAgenda->gyToTime( end.y() + 1 ); 01689 01690 TQDateTime dtStart(dayStart,timeStart); 01691 TQDateTime dtEnd(dayEnd,timeEnd); 01692 01693 mTimeSpanBegin = dtStart; 01694 mTimeSpanEnd = dtEnd; 01695 } 01696 01697 void KOAgendaView::deleteSelectedDateTime() 01698 { 01699 mTimeSpanBegin.setDate(TQDate()); 01700 mTimeSpanEnd.setDate(TQDate()); 01701 mTimeSpanInAllDay = false; 01702 } 01703 01704 void KOAgendaView::setTypeAheadReceiver( TQObject *o ) 01705 { 01706 mAgenda->setTypeAheadReceiver( o ); 01707 mAllDayAgenda->setTypeAheadReceiver( o ); 01708 } 01709 01710 void KOAgendaView::finishTypeAhead() 01711 { 01712 mAgenda->finishTypeAhead(); 01713 mAllDayAgenda->finishTypeAhead(); 01714 } 01715 01716 void KOAgendaView::removeIncidence( Incidence *incidence ) 01717 { 01718 mAgenda->removeIncidence( incidence ); 01719 mAllDayAgenda->removeIncidence( incidence ); 01720 } 01721 01722 void KOAgendaView::updateEventIndicators() 01723 { 01724 mMinY = mAgenda->minContentsY(); 01725 mMaxY = mAgenda->maxContentsY(); 01726 01727 mAgenda->checkScrollBoundaries(); 01728 updateEventIndicatorTop( mAgenda->visibleContentsYMin() ); 01729 updateEventIndicatorBottom( mAgenda->visibleContentsYMax() ); 01730 } 01731 01732 void KOAgendaView::setIncidenceChanger( IncidenceChangerBase *changer ) 01733 { 01734 mChanger = changer; 01735 mAgenda->setIncidenceChanger( changer ); 01736 mAllDayAgenda->setIncidenceChanger( changer ); 01737 } 01738 01739 void KOAgendaView::clearTimeSpanSelection() 01740 { 01741 mAgenda->clearSelection(); 01742 mAllDayAgenda->clearSelection(); 01743 deleteSelectedDateTime(); 01744 } 01745 01746 bool KOAgendaView::filterByResource( Incidence *incidence ) 01747 { 01748 // Special handling for groupware to-dos that are in Task folders. 01749 // Put them in the top-level "Calendar" folder for lack of a better 01750 // place since we never show Task type folders even in the 01751 // multiagenda view. 01752 if ( resourceCalendar() && incidence->type() == "Todo" ) { 01753 TQString subRes = resourceCalendar()->subresourceIdentifier( incidence ); 01754 if ( resourceCalendar()->subresourceType( subRes ) == "todo" ) { 01755 TQString calmatch = "/.INBOX.directory/Calendar"; 01756 TQString i18nmatch = "/.INBOX.directory/" + i18n( "Calendar" ); 01757 if ( subResourceCalendar().contains( calmatch ) || 01758 subResourceCalendar().contains( i18nmatch ) ) { 01759 return true; 01760 } 01761 } 01762 } 01763 01764 // Normal handling 01765 if ( !resourceCalendar() ) 01766 return true; 01767 CalendarResources *calRes = dynamic_cast<CalendarResources*>( calendar() ); 01768 if ( !calRes ) 01769 return true; 01770 if ( calRes->resource( incidence ) != resourceCalendar() ) 01771 return false; 01772 if ( !subResourceCalendar().isEmpty() ) { 01773 if ( resourceCalendar()->subresourceIdentifier( incidence ) != subResourceCalendar() ) 01774 return false; 01775 } 01776 return true; 01777 } 01778 01779 void KOAgendaView::resourcesChanged() 01780 { 01781 mPendingChanges = true; 01782 } 01783 01784 void KOAgendaView::calendarIncidenceAdded(Incidence * incidence) 01785 { 01786 Q_UNUSED( incidence ); 01787 mPendingChanges = true; 01788 } 01789 01790 void KOAgendaView::calendarIncidenceChanged(Incidence * incidence) 01791 { 01792 Q_UNUSED( incidence ); 01793 mPendingChanges = true; 01794 } 01795 01796 void KOAgendaView::calendarIncidenceDeleted(Incidence * incidence) 01797 { 01798 Q_UNUSED( incidence ); 01799 mPendingChanges = true; 01800 }