/* This file is part of KOrganizer. Copyright (c) 2001 Cornelius Schumacher <schumacher@kde.org> Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com> This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. As a special exception, permission is given to link this program with any edition of Qt, and distribute the resulting executable, without including the source code for Qt in the source distribution. */ #include <tqhbox.h> #include <tqvbox.h> #include <tqlabel.h> #include <tqframe.h> #include <tqlayout.h> #ifndef KORG_NOSPLITTER #include <tqsplitter.h> #endif #include <tqfont.h> #include <tqfontmetrics.h> #include <tqpopupmenu.h> #include <tqtooltip.h> #include <tqpainter.h> #include <tqpushbutton.h> #include <tqcursor.h> #include <tqbitarray.h> #include <kapplication.h> #include <kdebug.h> #include <kstandarddirs.h> #include <kiconloader.h> #include <klocale.h> #include <kconfig.h> #include <kglobal.h> #include <kglobalsettings.h> #include <kholidays.h> #include <libkcal/calendar.h> #include <libkcal/icaldrag.h> #include <libkcal/dndfactory.h> #include <libkcal/calfilter.h> #include <kcalendarsystem.h> #include "koglobals.h" #ifndef KORG_NOPLUGINS #include "kocore.h" #endif #include "koprefs.h" #include "koagenda.h" #include "koagendaitem.h" #include "timelabels.h" #include "koincidencetooltip.h" #include "kogroupware.h" #include "kodialogmanager.h" #include "koeventpopupmenu.h" #include "koagendaview.h" #include "koagendaview.moc" using namespace KOrg; EventIndicator::EventIndicator(Location loc,TQWidget *parent,const char *name) : TQFrame(parent,name) { mColumns = 1; mEnabled.resize( mColumns ); mLocation = loc; if (mLocation == Top) mPixmap = KOGlobals::self()->smallIcon("upindicator"); else mPixmap = KOGlobals::self()->smallIcon("downindicator"); setMinimumHeight(mPixmap.height()); } EventIndicator::~EventIndicator() { } void EventIndicator::drawContents(TQPainter *p) { // kdDebug(5850) << "======== top: " << contentsRect().top() << " bottom " // << contentsRect().bottom() << " left " << contentsRect().left() // << " right " << contentsRect().right() << endl; int i; for(i=0;i<mColumns;++i) { if (mEnabled[i]) { int cellWidth = contentsRect().right()/mColumns; int xOffset = KOGlobals::self()->reverseLayout() ? (mColumns - 1 - i)*cellWidth + cellWidth/2 -mPixmap.width()/2 : i*cellWidth + cellWidth/2 -mPixmap.width()/2; p->drawPixmap(TQPoint(xOffset,0),mPixmap); } } } void EventIndicator::changeColumns(int columns) { mColumns = columns; mEnabled.resize(mColumns); update(); } void EventIndicator::enableColumn(int column, bool enable) { mEnabled[column] = enable; } #include <libkcal/incidence.h> //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// KOAlternateLabel::KOAlternateLabel(const TQString &shortlabel, const TQString &longlabel, const TQString &extensivelabel, TQWidget *parent, const char *name ) : TQLabel(parent, name), mTextTypeFixed(false), mShortText(shortlabel), mLongText(longlabel), mExtensiveText(extensivelabel) { tqsetSizePolicy(TQSizePolicy( TQSizePolicy::Expanding, TQSizePolicy::Fixed )); if (mExtensiveText.isEmpty()) mExtensiveText = mLongText; squeezeTextToLabel(); } KOAlternateLabel::~KOAlternateLabel() { } void KOAlternateLabel::useShortText() { mTextTypeFixed = true; TQLabel::setText( mShortText ); TQToolTip::remove( this ); TQToolTip::add( this, mExtensiveText ); update(); // for kolab/issue4350 } void KOAlternateLabel::useLongText() { mTextTypeFixed = true; TQLabel::setText( mLongText ); TQToolTip::remove( this ); TQToolTip::add( this, mExtensiveText ); update(); // for kolab/issue4350 } void KOAlternateLabel::useExtensiveText() { mTextTypeFixed = true; TQLabel::setText( mExtensiveText ); TQToolTip::remove( this ); TQToolTip::add( this, "" ); update(); // for kolab/issue4350 } void KOAlternateLabel::useDefaultText() { mTextTypeFixed = false; squeezeTextToLabel(); } KOAlternateLabel::TextType KOAlternateLabel::largestFittingTextType() const { TQFontMetrics fm( fontMetrics() ); const int labelWidth = size().width(); const int longTextWidth = fm.width( mLongText ); const int extensiveTextWidth = fm.width( mExtensiveText ); if ( extensiveTextWidth <= labelWidth ) return Extensive; else if ( longTextWidth <= labelWidth ) return Long; else return Short; } void KOAlternateLabel::setFixedType( TextType type ) { switch ( type ) { case Extensive: useExtensiveText(); break; case Long: useLongText(); break; case Short: useShortText(); break; } } void KOAlternateLabel::squeezeTextToLabel() { if ( mTextTypeFixed ) return; const TextType type = largestFittingTextType(); switch ( type ) { case Extensive: TQLabel::setText( mExtensiveText ); TQToolTip::remove( this ); TQToolTip::add( this, "" ); break; case Long: TQLabel::setText( mLongText ); TQToolTip::remove( this ); TQToolTip::add( this, mExtensiveText ); break; case Short: TQLabel::setText( mShortText ); TQToolTip::remove( this ); TQToolTip::add( this, mExtensiveText ); break; } update(); // for kolab/issue4350 } void KOAlternateLabel::resizeEvent( TQResizeEvent * ) { squeezeTextToLabel(); } TQSize KOAlternateLabel::tqminimumSizeHint() const { TQSize sh = TQLabel::tqminimumSizeHint(); sh.setWidth(-1); return sh; } //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// KOAgendaView::KOAgendaView( Calendar *cal, CalendarView *calendarView, TQWidget *parent, const char *name, bool isSideBySide ) : KOrg::AgendaView (cal, parent,name), mExpandButton( 0 ), mAllowAgendaUpdate( true ), mUpdateItem( 0 ), mIsSideBySide( isSideBySide ), mPendingChanges( true ), mAreDatesInitialized( false ) { mSelectedDates.append(TQDate::tqcurrentDate()); mLayoutDayLabels = 0; mDayLabelsFrame = 0; mDayLabels = 0; bool isRTL = KOGlobals::self()->reverseLayout(); if ( KOPrefs::instance()->compactDialogs() ) { if ( KOPrefs::instance()->mVerticalScreen ) { mExpandedPixmap = KOGlobals::self()->smallIcon( "1downarrow" ); mNotExpandedPixmap = KOGlobals::self()->smallIcon( "1uparrow" ); } else { mExpandedPixmap = KOGlobals::self()->smallIcon( isRTL ? "1leftarrow" : "1rightarrow" ); mNotExpandedPixmap = KOGlobals::self()->smallIcon( isRTL ? "1rightarrow" : "1leftarrow" ); } } TQBoxLayout *topLayout = new TQVBoxLayout(this); // Create day name labels for agenda columns mDayLabelsFrame = new TQHBox(this); topLayout->addWidget(mDayLabelsFrame); // Create agenda splitter #ifndef KORG_NOSPLITTER mSplitterAgenda = new TQSplitter(Vertical,this); topLayout->addWidget(mSplitterAgenda); #if KDE_IS_VERSION( 3, 1, 93 ) mSplitterAgenda->setOpaqueResize( KGlobalSettings::opaqueResize() ); #else mSplitterAgenda->setOpaqueResize(); #endif mAllDayFrame = new TQHBox(mSplitterAgenda); TQWidget *agendaFrame = new TQWidget(mSplitterAgenda); #else TQVBox *mainBox = new TQVBox( this ); topLayout->addWidget( mainBox ); mAllDayFrame = new TQHBox(mainBox); TQWidget *agendaFrame = new TQWidget(mainBox); #endif // Create all-day agenda widget mDummyAllDayLeft = new TQVBox( mAllDayFrame ); if ( isSideBySide ) mDummyAllDayLeft->hide(); if ( KOPrefs::instance()->compactDialogs() ) { mExpandButton = new TQPushButton(mDummyAllDayLeft); mExpandButton->setPixmap( mNotExpandedPixmap ); mExpandButton->tqsetSizePolicy( TQSizePolicy( TQSizePolicy::Fixed, TQSizePolicy::Fixed ) ); connect( mExpandButton, TQT_SIGNAL( clicked() ), TQT_SIGNAL( toggleExpand() ) ); } else { TQLabel *label = new TQLabel( i18n("All Day"), mDummyAllDayLeft ); label->tqsetAlignment( Qt::AlignRight | Qt::AlignVCenter | Qt::WordBreak ); } mAllDayAgenda = new KOAgenda( 1, calendarView, mAllDayFrame ); mAllDayAgenda->setCalendar( calendar() ); TQWidget *dummyAllDayRight = new TQWidget(mAllDayFrame); // Create agenda frame TQGridLayout *agendaLayout = new TQGridLayout(agendaFrame,3,3); // TQHBox *agendaFrame = new TQHBox(splitterAgenda); // create event indicator bars mEventIndicatorTop = new EventIndicator(EventIndicator::Top,agendaFrame); agendaLayout->addWidget(mEventIndicatorTop,0,1); mEventIndicatorBottom = new EventIndicator(EventIndicator::Bottom, agendaFrame); agendaLayout->addWidget(mEventIndicatorBottom,2,1); TQWidget *dummyAgendaRight = new TQWidget(agendaFrame); agendaLayout->addWidget(dummyAgendaRight,0,2); // Create time labels mTimeLabels = new TimeLabels(24,agendaFrame); agendaLayout->addWidget(mTimeLabels,1,0); // Create agenda mAgenda = new KOAgenda( 1, 96, KOPrefs::instance()->mHourSize, calendarView, agendaFrame ); mAgenda->setCalendar( calendar() ); agendaLayout->addMultiCellWidget(mAgenda,1,1,1,2); agendaLayout->setColStretch(1,1); // Create event context menu for agenda mAgendaPopup = eventPopup(); // Create event context menu for all day agenda mAllDayAgendaPopup = eventPopup(); // make connections between dependent widgets mTimeLabels->setAgenda(mAgenda); if ( isSideBySide ) mTimeLabels->hide(); // Update widgets to reflect user preferences // updateConfig(); createDayLabels( true ); if ( !isSideBySide ) { // these blank widgets make the All Day Event box line up with the agenda dummyAllDayRight->setFixedWidth(mAgenda->verticalScrollBar()->width()); dummyAgendaRight->setFixedWidth(mAgenda->verticalScrollBar()->width()); } updateTimeBarWidth(); // Scrolling connect(mAgenda->verticalScrollBar(),TQT_SIGNAL(valueChanged(int)), mTimeLabels, TQT_SLOT(positionChanged())); connect( mAgenda, TQT_SIGNAL( zoomView( const int, const TQPoint & ,const Qt::Orientation ) ), TQT_SLOT( zoomView( const int, const TQPoint &, const Qt::Orientation ) ) ); connect(mTimeLabels->verticalScrollBar(),TQT_SIGNAL(valueChanged(int)), TQT_SLOT(setContentsPos(int))); // Create Events, depends on type of agenda connect( mAgenda, TQT_SIGNAL(newTimeSpanSignal(const TQPoint &, const TQPoint &)), TQT_SLOT(newTimeSpanSelected(const TQPoint &, const TQPoint &))); connect( mAllDayAgenda, TQT_SIGNAL(newTimeSpanSignal(const TQPoint &, const TQPoint &)), TQT_SLOT(newTimeSpanSelectedAllDay(const TQPoint &, const TQPoint &))); // event indicator update connect( mAgenda, TQT_SIGNAL(lowerYChanged(int)), TQT_SLOT(updateEventIndicatorTop(int))); connect( mAgenda, TQT_SIGNAL(upperYChanged(int)), TQT_SLOT(updateEventIndicatorBottom(int))); if ( !readOnly() ) { connectAgenda( mAgenda, mAgendaPopup, mAllDayAgenda ); connectAgenda( mAllDayAgenda, mAllDayAgendaPopup, mAgenda); } if ( cal ) { cal->registerObserver( this ); } } KOAgendaView::~KOAgendaView() { if ( calendar() ) calendar()->unregisterObserver( this ); delete mAgendaPopup; delete mAllDayAgendaPopup; } void KOAgendaView::connectAgenda( KOAgenda *agenda, TQPopupMenu *popup, KOAgenda *otherAgenda ) { connect( agenda, TQT_SIGNAL(showIncidencePopupSignal(Calendar *,Incidence *,const TQDate &)), popup, TQT_SLOT(showIncidencePopup(Calendar *,Incidence *,const TQDate &)) ); connect( agenda, TQT_SIGNAL(showNewEventPopupSignal()), TQT_SLOT(showNewEventPopup()) ); // Create/Show/Edit/Delete Event connect( agenda, TQT_SIGNAL(newEventSignal(ResourceCalendar *,const TQString &)), TQT_SIGNAL(newEventSignal(ResourceCalendar *,const TQString &)) ); connect( agenda, TQT_SIGNAL(newStartSelectSignal()), otherAgenda, TQT_SLOT(clearSelection()) ); connect( agenda, TQT_SIGNAL(newStartSelectSignal()), TQT_SIGNAL(timeSpanSelectionChanged()) ); connect( agenda, TQT_SIGNAL(editIncidenceSignal(Incidence *,const TQDate &)), TQT_SIGNAL(editIncidenceSignal(Incidence *,const TQDate &)) ); connect( agenda, TQT_SIGNAL(showIncidenceSignal(Incidence *,const TQDate &)), TQT_SIGNAL(showIncidenceSignal(Incidence *,const TQDate &)) ); connect( agenda, TQT_SIGNAL(deleteIncidenceSignal(Incidence *)), TQT_SIGNAL(deleteIncidenceSignal(Incidence *)) ); connect( agenda, TQT_SIGNAL(startMultiModify(const TQString &)), TQT_SIGNAL(startMultiModify(const TQString &)) ); connect( agenda, TQT_SIGNAL(endMultiModify()), TQT_SIGNAL(endMultiModify()) ); connect( agenda, TQT_SIGNAL(itemModified(KOAgendaItem *)), TQT_SLOT(updateEventDates(KOAgendaItem *)) ); connect( agenda, TQT_SIGNAL(enableAgendaUpdate(bool)), TQT_SLOT(enableAgendaUpdate(bool)) ); // drag signals connect( agenda, TQT_SIGNAL(startDragSignal(Incidence *)), TQT_SLOT(startDrag(Incidence *)) ); // synchronize selections connect( agenda, TQT_SIGNAL(incidenceSelected(Incidence *,const TQDate &)), otherAgenda, TQT_SLOT(deselectItem()) ); connect( agenda, TQT_SIGNAL(incidenceSelected(Incidence *,const TQDate &)), TQT_SIGNAL(incidenceSelected(Incidence *,const TQDate &)) ); // rescheduling of todos by d'n'd connect( agenda, TQT_SIGNAL(droppedToDo(Todo *,const TQPoint &,bool)), TQT_SLOT(slotTodoDropped(Todo *,const TQPoint &,bool)) ); } void KOAgendaView::zoomInVertically( ) { if ( !mIsSideBySide ) KOPrefs::instance()->mHourSize++; mAgenda->updateConfig(); mAgenda->checkScrollBoundaries(); mTimeLabels->updateConfig(); mTimeLabels->positionChanged(); mTimeLabels->tqrepaint(); updateView(); } void KOAgendaView::zoomOutVertically( ) { if ( KOPrefs::instance()->mHourSize > 4 || mIsSideBySide ) { if ( !mIsSideBySide ) KOPrefs::instance()->mHourSize--; mAgenda->updateConfig(); mAgenda->checkScrollBoundaries(); mTimeLabels->updateConfig(); mTimeLabels->positionChanged(); mTimeLabels->tqrepaint(); updateView(); } } void KOAgendaView::zoomInHorizontally( const TQDate &date) { TQDate begin; TQDate newBegin; TQDate dateToZoom = date; int ndays,count; begin = mSelectedDates.first(); ndays = begin.daysTo( mSelectedDates.last() ); // zoom with Action and are there a selected Incidence?, Yes, I zoom in to it. if ( ! dateToZoom.isValid () ) dateToZoom=mAgenda->selectedIncidenceDate(); if( !dateToZoom.isValid() ) { if ( ndays > 1 ) { newBegin=begin.addDays(1); count = ndays-1; emit zoomViewHorizontally ( newBegin , count ); } } else { if ( ndays <= 2 ) { newBegin = dateToZoom; count = 1; } else { newBegin = dateToZoom.addDays( -ndays/2 +1 ); count = ndays -1 ; } emit zoomViewHorizontally ( newBegin , count ); } } void KOAgendaView::zoomOutHorizontally( const TQDate &date ) { TQDate begin; TQDate newBegin; TQDate dateToZoom = date; int ndays,count; begin = mSelectedDates.first(); ndays = begin.daysTo( mSelectedDates.last() ); // zoom with Action and are there a selected Incidence?, Yes, I zoom out to it. if ( ! dateToZoom.isValid () ) dateToZoom=mAgenda->selectedIncidenceDate(); if ( !dateToZoom.isValid() ) { newBegin = begin.addDays(-1); count = ndays+3 ; } else { newBegin = dateToZoom.addDays( -ndays/2-1 ); count = ndays+3; } if ( abs( count ) >= 31 ) kdDebug(5850) << "change to the mounth view?"<<endl; else //We want to center the date emit zoomViewHorizontally( newBegin, count ); } void KOAgendaView::zoomView( const int delta, const TQPoint &pos, const Qt::Orientation orient ) { static TQDate zoomDate; static TQTimer *t = new TQTimer( this ); //Zoom to the selected incidence, on the other way // zoom to the date on screen after the first mousewheel move. if ( orient == Qt::Horizontal ) { TQDate date=mAgenda->selectedIncidenceDate(); if ( date.isValid() ) zoomDate=date; else{ if ( !t->isActive() ) { zoomDate= mSelectedDates[pos.x()]; } t->start ( 1000,true ); } if ( delta > 0 ) zoomOutHorizontally( zoomDate ); else zoomInHorizontally( zoomDate ); } else { // Vertical zoom TQPoint posConstentsOld = mAgenda->gridToContents(pos); if ( delta > 0 ) { zoomOutVertically(); } else { zoomInVertically(); } TQPoint posConstentsNew = mAgenda->gridToContents(pos); mAgenda->scrollBy( 0, posConstentsNew.y() - posConstentsOld.y() ); } } void KOAgendaView::createDayLabels( bool force ) { // kdDebug(5850) << "KOAgendaView::createDayLabels()" << endl; // Check if mSelectedDates has changed, if not just return // Removes some flickering and gains speed (since this is called by each updateView()) if ( !force && mSaveSelectedDates == mSelectedDates ) { return; } mSaveSelectedDates = mSelectedDates; delete mDayLabels; mDateDayLabels.clear(); mDayLabels = new TQFrame (mDayLabelsFrame); mLayoutDayLabels = new TQHBoxLayout(mDayLabels); if ( !mIsSideBySide ) mLayoutDayLabels->addSpacing(mTimeLabels->width()); const KCalendarSystem*calsys=KOGlobals::self()->calendarSystem(); DateList::ConstIterator dit; for( dit = mSelectedDates.begin(); dit != mSelectedDates.end(); ++dit ) { TQDate date = *dit; TQBoxLayout *dayLayout = new TQVBoxLayout(mLayoutDayLabels); mLayoutDayLabels->setStretchFactor(dayLayout, 1); // dayLayout->setMinimumWidth(1); int dW = calsys->dayOfWeek(date); TQString veryLongStr = KGlobal::locale()->formatDate( date ); TQString longstr = i18n( "short_weekday date (e.g. Mon 13)","%1 %2" ) .arg( calsys->weekDayName( dW, true ) ) .arg( calsys->day(date) ); TQString shortstr = TQString::number(calsys->day(date)); KOAlternateLabel *dayLabel = new KOAlternateLabel(shortstr, longstr, veryLongStr, mDayLabels); dayLabel->useShortText(); // will be recalculated in updateDayLabelSizes() anyway dayLabel->setMinimumWidth(1); dayLabel->tqsetAlignment(TQLabel::AlignHCenter); if (date == TQDate::tqcurrentDate()) { TQFont font = dayLabel->font(); font.setBold(true); dayLabel->setFont(font); } dayLayout->addWidget(dayLabel); mDateDayLabels.append( dayLabel ); // if a holiday region is selected, show the holiday name TQStringList texts = KOGlobals::self()->holiday( date ); TQStringList::ConstIterator textit = texts.begin(); for ( ; textit != texts.end(); ++textit ) { // use a KOAlternateLabel so when the text doesn't fit any more a tooltip is used KOAlternateLabel*label = new KOAlternateLabel( (*textit), (*textit), TQString::null, mDayLabels ); label->setMinimumWidth(1); label->tqsetAlignment(AlignCenter); dayLayout->addWidget(label); } #ifndef KORG_NOPLUGINS CalendarDecoration::List cds = KOCore::self()->calendarDecorations(); CalendarDecoration *it; for(it = cds.first(); it; it = cds.next()) { TQString text = it->shortText( date ); if ( !text.isEmpty() ) { // use a KOAlternateLabel so when the text doesn't fit any more a tooltip is used KOAlternateLabel*label = new KOAlternateLabel( text, text, TQString::null, mDayLabels ); label->setMinimumWidth(1); label->tqsetAlignment(AlignCenter); dayLayout->addWidget(label); } } for(it = cds.first(); it; it = cds.next()) { TQWidget *wid = it->smallWidget(mDayLabels,date); if ( wid ) { // wid->setHeight(20); dayLayout->addWidget(wid); } } #endif } if ( !mIsSideBySide ) mLayoutDayLabels->addSpacing(mAgenda->verticalScrollBar()->width()); mDayLabels->show(); TQTimer::singleShot( 0, this, TQT_SLOT( updateDayLabelSizes() ) ); } void KOAgendaView::enableAgendaUpdate( bool enable ) { mAllowAgendaUpdate = enable; } int KOAgendaView::maxDatesHint() { // Not sure about the max number of events, so return 0 for now. return 0; } int KOAgendaView::tqcurrentDateCount() { return mSelectedDates.count(); } Incidence::List KOAgendaView::selectedIncidences() { Incidence::List selected; Incidence *incidence; incidence = mAgenda->selectedIncidence(); if (incidence) selected.append(incidence); incidence = mAllDayAgenda->selectedIncidence(); if (incidence) selected.append(incidence); return selected; } DateList KOAgendaView::selectedIncidenceDates() { DateList selected; TQDate qd; qd = mAgenda->selectedIncidenceDate(); if (qd.isValid()) selected.append(qd); qd = mAllDayAgenda->selectedIncidenceDate(); if (qd.isValid()) selected.append(qd); return selected; } bool KOAgendaView::eventDurationHint( TQDateTime &startDt, TQDateTime &endDt, bool &allDay ) { if ( selectionStart().isValid() ) { TQDateTime start = selectionStart(); TQDateTime end = selectionEnd(); if ( start.secsTo( end ) == 15*60 ) { // One cell in the agenda view selected, e.g. // because of a double-click, => Use the default duration TQTime defaultDuration( KOPrefs::instance()->mDefaultDuration.time() ); int addSecs = ( defaultDuration.hour()*3600 ) + ( defaultDuration.minute()*60 ); end = start.addSecs( addSecs ); } startDt = start; endDt = end; allDay = selectedIsAllDay(); return true; } return false; } /** returns if only a single cell is selected, or a range of cells */ bool KOAgendaView::selectedIsSingleCell() { if ( !selectionStart().isValid() || !selectionEnd().isValid() ) return false; if (selectedIsAllDay()) { int days = selectionStart().daysTo(selectionEnd()); return ( days < 1 ); } else { int secs = selectionStart().secsTo(selectionEnd()); return ( secs <= 24*60*60/mAgenda->rows() ); } } void KOAgendaView::updateView() { // kdDebug(5850) << "KOAgendaView::updateView()" << endl; fillAgenda(); } /* Update configuration settings for the agenda view. This method is not complete. */ void KOAgendaView::updateConfig() { // kdDebug(5850) << "KOAgendaView::updateConfig()" << endl; // update config for children mTimeLabels->updateConfig(); mAgenda->updateConfig(); mAllDayAgenda->updateConfig(); // widget synchronization // FIXME: find a better way, maybe signal/slot mTimeLabels->positionChanged(); // for some reason, this needs to be called explicitly mTimeLabels->tqrepaint(); updateTimeBarWidth(); // ToolTips displaying summary of events KOAgendaItem::toolTipGroup()->setEnabled(KOPrefs::instance() ->mEnableToolTips); setHolidayMasks(); createDayLabels( true ); updateView(); } void KOAgendaView::updateTimeBarWidth() { int width; width = mDummyAllDayLeft->fontMetrics().width( i18n("All Day") ); width = QMAX( width, mTimeLabels->width() ); mDummyAllDayLeft->setFixedWidth( width ); mTimeLabels->setFixedWidth( width ); } void KOAgendaView::updateDayLabelSizes() { // First, calculate the maximum text type that fits for all labels KOAlternateLabel::TextType overallType = KOAlternateLabel::Extensive; TQPtrList<KOAlternateLabel>::const_iterator it = mDateDayLabels.constBegin(); for( ; it != mDateDayLabels.constEnd(); it++ ) { KOAlternateLabel::TextType type = (*it)->largestFittingTextType(); if ( type < overallType ) overallType = type; } // Then, set that maximum text type to all the labels it = mDateDayLabels.constBegin(); for( ; it != mDateDayLabels.constEnd(); it++ ) { (*it)->setFixedType( overallType ); } } void KOAgendaView::resizeEvent( TQResizeEvent *resizeEvent ) { updateDayLabelSizes(); KOrg::AgendaView::resizeEvent( resizeEvent ); } void KOAgendaView::updateEventDates( KOAgendaItem *item ) { kdDebug(5850) << "KOAgendaView::updateEventDates(): " << item->text() << "; item->cellXLeft(): " << item->cellXLeft() << "; item->cellYTop(): " << item->cellYTop() << "; item->lastMultiItem(): " << item->lastMultiItem() << "; item->itemPos(): " << item->itemPos() << "; item->itemCount(): " << item->itemCount() << endl; TQDateTime startDt, endDt; // Start date of this incidence, calculate the offset from it (so recurring and // non-recurring items can be treated exactly the same, we never need to check // for doesRecur(), because we only move the start day by the number of days the // agenda item was really moved. Smart, isn't it?) TQDate thisDate; if ( item->cellXLeft() < 0 ) { thisDate = ( mSelectedDates.first() ).addDays( item->cellXLeft() ); } else { thisDate = mSelectedDates[ item->cellXLeft() ]; } TQDate oldThisDate( item->itemDate() ); const int daysOffset = oldThisDate.daysTo( thisDate ); int daysLength = 0; // startDt.setDate( startDate ); Incidence *incidence = item->incidence(); if ( !incidence ) { return; } if ( !mChanger || !mChanger->beginChange( incidence, resourceCalendar(), subResourceCalendar() ) ) { return; } Incidence *oldIncidence = incidence->clone(); TQTime startTime( 0, 0, 0 ), endTime( 0, 0, 0 ); if ( incidence->doesFloat() ) { daysLength = item->cellWidth() - 1; } else { startTime = mAgenda->gyToTime( item->cellYTop() ); if ( item->lastMultiItem() ) { endTime = mAgenda->gyToTime( item->lastMultiItem()->cellYBottom() + 1 ); daysLength = item->lastMultiItem()->cellXLeft() - item->cellXLeft(); kdDebug(5850) << "item->lastMultiItem()->cellXLeft(): " << item->lastMultiItem()->cellXLeft() << endl; } else if ( item->itemPos() == item->itemCount() && item->itemCount() > 1 ) { /* multiitem handling in agenda assumes two things: - The start (first KOAgendaItem) is always visible. - The first KOAgendaItem of the incidence has a non-null item->lastMultiItem() pointing to the last KOagendaItem. But those aren't always met, for example when in day-view. kolab/issue4417 */ // Cornercase 1: - Resizing the end of the event but the start isn't visible endTime = mAgenda->gyToTime( item->cellYBottom() + 1 ); daysLength = item->itemCount() - 1; startTime = incidence->dtStart().time(); } else if ( item->itemPos() == 1 && item->itemCount() > 1 ) { // Cornercase 2: - Resizing the start of the event but the end isn't visible endTime = incidence->dtEnd().time(); daysLength = item->itemCount() - 1; } else { endTime = mAgenda->gyToTime( item->cellYBottom() + 1 ); } } kdDebug(5850) << "daysLength: " << daysLength << "; startTime: " << startTime << "; endTime: " << endTime << "; thisDate: " << thisDate << "; incidence->dtStart(): " << incidence->dtStart() << endl; // FIXME: use a visitor here if ( incidence->type() == "Event" ) { startDt = incidence->dtStart(); startDt = startDt.addDays( daysOffset ); startDt.setTime( startTime ); endDt = startDt.addDays( daysLength ); endDt.setTime( endTime ); Event* ev = static_cast<Event*>( incidence ); if ( incidence->dtStart() == startDt && ev->dtEnd() == endDt ) { // No change delete oldIncidence; return; } incidence->setDtStart( startDt ); ev->setDtEnd( endDt ); } else if ( incidence->type() == "Todo" ) { Todo *td = static_cast<Todo*>( incidence ); startDt = td->hasStartDate() ? td->dtStart() : td->dtDue(); startDt = thisDate.addDays( td->dtDue().daysTo( startDt ) ); startDt.setTime( startTime ); endDt.setDate( thisDate ); endDt.setTime( endTime ); if( td->dtDue() == endDt ) { // No change delete oldIncidence; return; } } // FIXME: Adjusting the recurrence should really go to CalendarView so this // functionality will also be available in other views! // TODO_Recurrence: This does not belong here, and I'm not really sure // how it's supposed to work anyway. /* Recurrence *recur = incidence->recurrence(); if ( recur->doesRecur() && daysOffset != 0 ) { switch ( recur->recurrenceType() ) { case Recurrence::rYearlyPos: { int freq = recur->frequency(); int duration = recur->duration(); TQDate endDt( recur->endDate() ); bool negative = false; TQPtrList<Recurrence::rMonthPos> monthPos( recur->yearMonthPositions() ); if ( monthPos.first() ) { negative = monthPos.first()->negative; } TQBitArray days( 7 ); int pos = 0; days.fill( false ); days.setBit( thisDate.dayOfWeek() - 1 ); if ( negative ) { pos = - ( thisDate.daysInMonth() - thisDate.day() - 1 ) / 7 - 1; } else { pos = ( thisDate.day()-1 ) / 7 + 1; } // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again recur->unsetRecurs(); if ( duration != 0 ) { recur->setYearly( Recurrence::rYearlyPos, freq, duration ); } else { recur->setYearly( Recurrence::rYearlyPos, freq, endDt ); } recur->addYearlyMonthPos( pos, days ); recur->addYearlyNum( thisDate.month() ); break; } case Recurrence::rYearlyDay: { int freq = recur->frequency(); int duration = recur->duration(); TQDate endDt( recur->endDate() ); // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again recur->unsetRecurs(); if ( duration == 0 ) { // end by date recur->setYearly( Recurrence::rYearlyDay, freq, endDt ); } else { recur->setYearly( Recurrence::rYearlyDay, freq, duration ); } recur->addYearlyNum( thisDate.dayOfYear() ); break; } case Recurrence::rYearlyMonth: { int freq = recur->frequency(); int duration = recur->duration(); TQDate endDt( recur->endDate() ); // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again recur->unsetRecurs(); if ( duration != 0 ) { recur->setYearlyByDate( thisDate.day(), recur->feb29YearlyType(), freq, duration ); } else { recur->setYearlyByDate( thisDate.day(), recur->feb29YearlyType(), freq, endDt ); } recur->addYearlyNum( thisDate.month() ); break; } case Recurrence::rMonthlyPos: { int freq = recur->frequency(); int duration = recur->duration(); TQDate endDt( recur->endDate() ); TQPtrList<Recurrence::rMonthPos> monthPos( recur->monthPositions() ); if ( !monthPos.isEmpty() ) { // FIXME: How shall I adapt the day x of week Y if we move the date across month borders??? // for now, just use the date of the moved item and assume the recurrence only occurs on that day. // That's fine for korganizer, but might mess up other organizers. TQBitArray rDays( 7 ); rDays = monthPos.first()->rDays; bool negative = monthPos.first()->negative; int newPos; rDays.fill( false ); rDays.setBit( thisDate.dayOfWeek() - 1 ); if ( negative ) { newPos = - ( thisDate.daysInMonth() - thisDate.day() - 1 ) / 7 - 1; } else { newPos = ( thisDate.day()-1 ) / 7 + 1; } // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again recur->unsetRecurs(); if ( duration == 0 ) { // end by date recur->setMonthly( Recurrence::rMonthlyPos, freq, endDt ); } else { recur->setMonthly( Recurrence::rMonthlyPos, freq, duration ); } recur->addMonthlyPos( newPos, rDays ); } break;} case Recurrence::rMonthlyDay: { int freq = recur->frequency(); int duration = recur->duration(); TQDate endDt( recur->endDate() ); TQPtrList<int> monthDays( recur->monthDays() ); // Terrible hack: to change the month days, I have to unset the recurrence, and set all days manually again recur->unsetRecurs(); if ( duration == 0 ) { // end by date recur->setMonthly( Recurrence::rMonthlyDay, freq, endDt ); } else { recur->setMonthly( Recurrence::rMonthlyDay, freq, duration ); } // FIXME: How shall I adapt the n-th day if we move the date across month borders??? // for now, just use the date of the moved item and assume the recurrence only occurs on that day. // That's fine for korganizer, but might mess up other organizers. recur->addMonthlyDay( thisDate.day() ); break;} case Recurrence::rWeekly: { TQBitArray days(7), oldDays( recur->days() ); int offset = daysOffset % 7; if ( offset<0 ) offset = (offset+7) % 7; // rotate the days for (int d=0; d<7; d++ ) { days.setBit( (d+offset) % 7, oldDays.at(d) ); } if ( recur->duration() == 0 ) { // end by date recur->setWeekly( recur->frequency(), days, recur->endDate(), recur->weekStart() ); } else { // duration or no end recur->setWeekly( recur->frequency(), days, recur->duration(), recur->weekStart() ); } break;} // nothing to be done for the following: case Recurrence::rDaily: case Recurrence::rHourly: case Recurrence::rMinutely: case Recurrence::rNone: default: break; } if ( recur->duration()==0 ) { // end by date recur->setEndDate( recur->endDate().addDays( daysOffset ) ); } KMessageBox::information( this, i18n("A recurring calendar item was moved " "to a different day. The recurrence settings " "have been updated with that move. Please check " "them in the editor."), i18n("Recurrence Moved"), "RecurrenceMoveInAgendaWarning" ); }*/ // FIXME: use a visitor here if ( incidence->type() == "Event" ) { incidence->setDtStart( startDt ); static_cast<Event*>( incidence )->setDtEnd( endDt ); } else if ( incidence->type() == "Todo" ) { Todo *td = static_cast<Todo*>( incidence ); if ( td->hasStartDate() ) { td->setDtStart( startDt ); } td->setDtDue( endDt ); } item->setItemDate( startDt.date() ); KOIncidenceToolTip::remove( item ); KOIncidenceToolTip::add( item, calendar(), incidence, thisDate, KOAgendaItem::toolTipGroup() ); const bool result = mChanger->changeIncidence( oldIncidence, incidence, KOGlobals::DATE_MODIFIED, this ); mChanger->endChange( incidence, resourceCalendar(), subResourceCalendar() ); delete oldIncidence; if ( !result ) { mPendingChanges = true; TQTimer::singleShot( 0, this, TQT_SLOT(updateView()) ); return; } // don't update the agenda as the item already has the correct coordinates. // an update would delete the current item and recreate it, but we are still // using a pointer to that item! => CRASH enableAgendaUpdate( false ); // We need to do this in a timer to make sure we are not deleting the item // we are currently working on, which would lead to crashes // Only the actually moved agenda item is already at the correct position and mustn't be // recreated. All others have to!!! if ( incidence->doesRecur() ) { mUpdateItem = incidence; TQTimer::singleShot( 0, this, TQT_SLOT( doUpdateItem() ) ); } enableAgendaUpdate( true ); // kdDebug(5850) << "KOAgendaView::updateEventDates() done " << endl; } void KOAgendaView::doUpdateItem() { if ( mUpdateItem ) { changeIncidenceDisplay( mUpdateItem, KOGlobals::INCIDENCEEDITED ); mUpdateItem = 0; } } void KOAgendaView::showDates( const TQDate &start, const TQDate &end ) { // kdDebug(5850) << "KOAgendaView::selectDates" << endl; if ( !mSelectedDates.isEmpty() && mSelectedDates.first() == start && mSelectedDates.last() == end && !mPendingChanges ) return; mSelectedDates.clear(); TQDate d = start; while ( d <= end ) { mSelectedDates.append( d ); d = d.addDays( 1 ); } mAreDatesInitialized = true; // and update the view fillAgenda(); } void KOAgendaView::showIncidences( const Incidence::List &, const TQDate & ) { kdDebug(5850) << "KOAgendaView::showIncidences( const Incidence::List & ) is not yet implemented" << endl; } void KOAgendaView::insertIncidence( Incidence *incidence, const TQDate &curDate ) { if ( !filterByResource( incidence ) ) { return; } // FIXME: Use a visitor here, or some other method to get rid of the dynamic_cast's Event *event = dynamic_cast<Event *>( incidence ); Todo *todo = dynamic_cast<Todo *>( incidence ); int curCol = mSelectedDates.first().daysTo( curDate ); // In case incidence->dtStart() isn't visible (crosses bounderies) if ( curCol < 0 ) { curCol = 0; } // The date for the event is not displayed, just ignore it if ( curCol >= int( mSelectedDates.count() ) ) { return; } // Default values, which can never be reached mMinY[curCol] = mAgenda->timeToY( TQTime( 23, 59 ) ) + 1; mMaxY[curCol] = mAgenda->timeToY( TQTime( 0, 0 ) ) - 1; int beginX; int endX; TQDate columnDate; if ( event ) { TQDate firstVisibleDate = mSelectedDates.first(); // its crossing bounderies, lets calculate beginX and endX if ( curDate < firstVisibleDate ) { beginX = curCol + firstVisibleDate.daysTo( curDate ); endX = beginX + event->dtStart().daysTo( event->dtEnd() ); columnDate = firstVisibleDate; } else { beginX = curCol; endX = beginX + event->dtStart().daysTo( event->dtEnd() ); columnDate = curDate; } } else if ( todo ) { if ( !todo->hasDueDate() ) { return; // todo shall not be displayed if it has no date } columnDate = curDate; beginX = endX = curCol; } else { return; } if ( todo && todo->isOverdue() ) { mAllDayAgenda->insertAllDayItem( incidence, columnDate, curCol, curCol ); } else if ( incidence->doesFloat() || ( todo && !todo->dtDue().isValid() ) ) { mAllDayAgenda->insertAllDayItem( incidence, columnDate, beginX, endX ); } else if ( event && event->isMultiDay() ) { int startY = mAgenda->timeToY( event->dtStart().time() ); TQTime endtime = event->dtEnd().time(); if ( endtime == TQTime( 0, 0, 0 ) ) { endtime = TQTime( 23, 59, 59 ); } int endY = mAgenda->timeToY( endtime ) - 1; if ( ( beginX <= 0 && curCol == 0 ) || beginX == curCol ) { mAgenda->insertMultiItem( event, columnDate, beginX, endX, startY, endY ); } if ( beginX == curCol ) { mMaxY[curCol] = mAgenda->timeToY( TQTime( 23, 59 ) ); if ( startY < mMinY[curCol] ) { mMinY[curCol] = startY; } } else if ( endX == curCol ) { mMinY[curCol] = mAgenda->timeToY( TQTime( 0, 0 ) ); if ( endY > mMaxY[curCol] ) { mMaxY[curCol] = endY; } } else { mMinY[curCol] = mAgenda->timeToY( TQTime( 0, 0 ) ); mMaxY[curCol] = mAgenda->timeToY( TQTime( 23, 59 ) ); } } else { int startY = 0, endY = 0; if ( event ) { startY = mAgenda->timeToY( incidence->dtStart().time() ); TQTime endtime = event->dtEnd().time(); if ( endtime == TQTime( 0, 0, 0 ) ) { endtime = TQTime( 23, 59, 59 ); } endY = mAgenda->timeToY( endtime ) - 1; } if ( todo ) { TQTime t = todo->dtDue().time(); if ( t == TQTime( 0, 0 ) ) { t = TQTime( 23, 59 ); } int halfHour = 1800; if ( t.addSecs( -halfHour ) < t ) { startY = mAgenda->timeToY( t.addSecs( -halfHour ) ); endY = mAgenda->timeToY( t ) - 1; } else { startY = 0; endY = mAgenda->timeToY( t.addSecs( halfHour ) ) - 1; } } if ( endY < startY ) { endY = startY; } mAgenda->insertItem( incidence, columnDate, curCol, startY, endY, 1, 1 ); if ( startY < mMinY[curCol] ) { mMinY[curCol] = startY; } if ( endY > mMaxY[curCol] ) { mMaxY[curCol] = endY; } } } void KOAgendaView::changeIncidenceDisplayAdded( Incidence *incidence ) { Todo *todo = dynamic_cast<Todo *>(incidence); CalFilter *filter = calendar()->filter(); if ( ( filter && !filter->filterIncidence( incidence ) ) || ( ( todo && !KOPrefs::instance()->showAllDayTodo() ) ) ) { return; } displayIncidence( incidence ); } void KOAgendaView::changeIncidenceDisplay( Incidence *incidence, int mode ) { switch ( mode ) { case KOGlobals::INCIDENCEADDED: { // Add an event. No need to recreate the whole view! // recreating everything even causes troubles: dropping to the // day matrix recreates the agenda items, but the evaluation is // still in an agendaItems' code, which was deleted in the mean time. // Thus KOrg crashes... changeIncidenceDisplayAdded( incidence ); updateEventIndicators(); break; } case KOGlobals::INCIDENCEEDITED: { if ( mAllowAgendaUpdate ) { removeIncidence( incidence ); changeIncidenceDisplayAdded( incidence ); } updateEventIndicators(); break; } case KOGlobals::INCIDENCEDELETED: { removeIncidence( incidence ); updateEventIndicators(); break; } default: return; } // HACK: Update the view if the all-day agenda has been modified. // Do this because there are some tqlayout problems in the // all-day agenda that are not easily solved, but clearing // and redrawing works ok. if ( incidence->doesFloat() ) { updateView(); } } void KOAgendaView::fillAgenda( const TQDate & ) { fillAgenda(); } void KOAgendaView::fillAgenda() { if ( !mAreDatesInitialized ) { return; } mPendingChanges = false; /* Remember the uids of the selected items. In case one of the * items was deleted and re-added, we want to reselect it. */ const TQString &selectedAgendaUid = mAgenda->lastSelectedUid(); const TQString &selectedAllDayAgendaUid = mAllDayAgenda->lastSelectedUid(); enableAgendaUpdate( true ); clearView(); mAllDayAgenda->changeColumns( mSelectedDates.count() ); mAgenda->changeColumns( mSelectedDates.count() ); mEventIndicatorTop->changeColumns( mSelectedDates.count() ); mEventIndicatorBottom->changeColumns( mSelectedDates.count() ); createDayLabels( false ); setHolidayMasks(); mMinY.resize( mSelectedDates.count() ); mMaxY.resize( mSelectedDates.count() ); mAgenda->setDateList( mSelectedDates ); bool somethingReselected = false; Incidence::List incidences = calendar()->incidences(); for ( Incidence::List::ConstIterator it = incidences.begin(); it!=incidences.constEnd(); ++it ) { Incidence *incidence = (*it); displayIncidence( incidence ); if( incidence->uid() == selectedAgendaUid && !selectedAgendaUid.isNull() ) { mAgenda->selectItemByUID( incidence->uid() ); somethingReselected = true; } if( incidence->uid() == selectedAllDayAgendaUid && !selectedAllDayAgendaUid.isNull() ) { mAllDayAgenda->selectItemByUID( incidence->uid() ); somethingReselected = true; } } mAgenda->checkScrollBoundaries(); updateEventIndicators(); // mAgenda->viewport()->update(); // mAllDayAgenda->viewport()->update(); // make invalid deleteSelectedDateTime(); if( !somethingReselected ) { emit incidenceSelected( 0, TQDate() ); } } void KOAgendaView::displayIncidence( Incidence *incidence ) { TQDate today = TQDate::tqcurrentDate(); DateTimeList::iterator t; // FIXME: use a visitor here Todo *todo = dynamic_cast<Todo *>( incidence ); Event *event = dynamic_cast<Event *>( incidence ); TQDateTime firstVisibleDateTime = mSelectedDates.first(); TQDateTime lastVisibleDateTime = mSelectedDates.last(); lastVisibleDateTime.setTime( TQTime( 23, 59, 59, 59 ) ); firstVisibleDateTime.setTime( TQTime( 0, 0 ) ); DateTimeList dateTimeList; TQDateTime incDtStart = incidence->dtStart(); TQDateTime incDtEnd = incidence->dtEnd(); if ( todo && ( !KOPrefs::instance()->showAllDayTodo() || !todo->hasDueDate() ) ) { return; } if ( incidence->doesRecur() ) { int eventDuration = event ? incDtStart.daysTo( incDtEnd ) : 0; // if there's a multiday event that starts before firstVisibleDateTime but ends after // lets include it. timesInInterval() ignores incidences that aren't totaly inside // the range TQDateTime startDateTimeWithOffset = firstVisibleDateTime.addDays( -eventDuration ); dateTimeList = incidence->recurrence()->timesInInterval( startDateTimeWithOffset, lastVisibleDateTime ); } else { TQDateTime dateToAdd; // date to add to our date list TQDateTime incidenceStart; TQDateTime incidenceEnd; if ( todo && todo->hasDueDate() && !todo->isOverdue() ) { // If it's not overdue it will be shown at the original date (not today) dateToAdd = todo->dtDue(); // To-dos are drawn with the bottom of the rectangle at dtDue // if dtDue is at 00:00, then it should be displayed in the previous day, at 23:59 if ( !todo->doesFloat() && dateToAdd.time() == TQTime( 0, 0 ) ) { dateToAdd = dateToAdd.addSecs( -1 ); } incidenceEnd = dateToAdd; } else if ( event ) { dateToAdd = incDtStart; incidenceEnd = incDtEnd; } if ( incidence->doesFloat() ) { // so comparisons with < > actually work dateToAdd.setTime( TQTime( 0, 0 ) ); incidenceEnd.setTime( TQTime( 23, 59, 59, 59 ) ); } if ( dateToAdd <= lastVisibleDateTime && incidenceEnd > firstVisibleDateTime ) { dateTimeList += dateToAdd; } } // ToDo items shall be displayed today if they are already overdude TQDateTime dateTimeToday = today; if ( todo && todo->isOverdue() && dateTimeToday >= firstVisibleDateTime && dateTimeToday <= lastVisibleDateTime ) { bool doAdd = true; if ( todo->doesRecur() ) { /* If there's a recurring instance showing up today don't add "today" again * we don't want the event to appear duplicated */ for ( t = dateTimeList.begin(); t != dateTimeList.end(); ++t ) { if ( (*t).date() == today ) { doAdd = false; break; } } } if ( doAdd ) { dateTimeList += dateTimeToday; } } for ( t = dateTimeList.begin(); t != dateTimeList.end(); ++t ) { insertIncidence( incidence, (*t).date() ); } } void KOAgendaView::clearView() { // kdDebug(5850) << "ClearView" << endl; mAllDayAgenda->clear(); mAgenda->clear(); } CalPrinterBase::PrintType KOAgendaView::printType() { if ( tqcurrentDateCount() == 1 ) return CalPrinterBase::Day; else return CalPrinterBase::Week; } void KOAgendaView::updateEventIndicatorTop( int newY ) { uint i; for( i = 0; i < mMinY.size(); ++i ) { mEventIndicatorTop->enableColumn( i, newY > mMinY[i] ); } mEventIndicatorTop->update(); } void KOAgendaView::updateEventIndicatorBottom( int newY ) { uint i; for( i = 0; i < mMaxY.size(); ++i ) { mEventIndicatorBottom->enableColumn( i, newY <= mMaxY[i] ); } mEventIndicatorBottom->update(); } void KOAgendaView::slotTodoDropped( Todo *todo, const TQPoint &gpos, bool allDay ) { if ( gpos.x()<0 || gpos.y()<0 ) return; TQDate day = mSelectedDates[gpos.x()]; TQTime time = mAgenda->gyToTime( gpos.y() ); TQDateTime newTime( day, time ); if ( todo ) { Todo *existingTodo = calendar()->todo( todo->uid() ); if ( existingTodo ) { kdDebug(5850) << "Drop existing Todo" << endl; Todo *oldTodo = existingTodo->clone(); if ( mChanger && mChanger->beginChange( existingTodo, resourceCalendar(), subResourceCalendar() ) ) { existingTodo->setDtDue( newTime ); existingTodo->setFloats( allDay ); existingTodo->setHasDueDate( true ); mChanger->changeIncidence( oldTodo, existingTodo, KOGlobals::DATE_MODIFIED, this ); mChanger->endChange( existingTodo, resourceCalendar(), subResourceCalendar() ); } else { KMessageBox::sorry( this, i18n("Unable to modify this to-do, " "because it cannot be locked.") ); } delete oldTodo; } else { kdDebug(5850) << "Drop new Todo" << endl; todo->setDtDue( newTime ); todo->setFloats( allDay ); todo->setHasDueDate( true ); if ( !mChanger->addIncidence( todo, 0, TQString(), this ) ) { KODialogManager::errorSaveIncidence( this, todo ); } } } } void KOAgendaView::startDrag( Incidence *incidence ) { #ifndef KORG_NODND DndFactory factory( calendar() ); ICalDrag *vd = factory.createDrag( incidence, this ); if ( vd->drag() ) { kdDebug(5850) << "KOAgendaView::startDrag(): Delete drag source" << endl; } #endif } void KOAgendaView::readSettings() { readSettings(KOGlobals::self()->config()); } void KOAgendaView::readSettings(KConfig *config) { // kdDebug(5850) << "KOAgendaView::readSettings()" << endl; config->setGroup("Views"); #ifndef KORG_NOSPLITTER TQValueList<int> sizes = config->readIntListEntry("Separator AgendaView"); if (sizes.count() == 2) { mSplitterAgenda->setSizes(sizes); } #endif updateConfig(); } void KOAgendaView::writeSettings(KConfig *config) { // kdDebug(5850) << "KOAgendaView::writeSettings()" << endl; config->setGroup("Views"); #ifndef KORG_NOSPLITTER TQValueList<int> list = mSplitterAgenda->sizes(); config->writeEntry("Separator AgendaView",list); #endif } void KOAgendaView::setHolidayMasks() { if ( mSelectedDates.isEmpty() || !mSelectedDates[0].isValid() ) { return; } mHolidayMask.resize( mSelectedDates.count() + 1 ); for( uint i = 0; i < mSelectedDates.count(); ++i ) { mHolidayMask[i] = !KOGlobals::self()->isWorkDay( mSelectedDates[ i ] ); } // Store the information about the day before the visible area (needed for // overnight working hours) in the last bit of the tqmask: bool showDay = !KOGlobals::self()->isWorkDay( mSelectedDates[ 0 ].addDays( -1 ) ); mHolidayMask[ mSelectedDates.count() ] = showDay; mAgenda->setHolidayMask( &mHolidayMask ); mAllDayAgenda->setHolidayMask( &mHolidayMask ); } void KOAgendaView::setContentsPos( int y ) { mAgenda->setContentsPos( 0, y ); } void KOAgendaView::setExpandedButton( bool expanded ) { if ( !mExpandButton ) return; if ( expanded ) { mExpandButton->setPixmap( mExpandedPixmap ); } else { mExpandButton->setPixmap( mNotExpandedPixmap ); } } void KOAgendaView::clearSelection() { mAgenda->deselectItem(); mAllDayAgenda->deselectItem(); } void KOAgendaView::newTimeSpanSelectedAllDay( const TQPoint &start, const TQPoint &end ) { newTimeSpanSelected( start, end ); mTimeSpanInAllDay = true; } void KOAgendaView::newTimeSpanSelected( const TQPoint &start, const TQPoint &end ) { if (!mSelectedDates.count()) return; mTimeSpanInAllDay = false; TQDate dayStart = mSelectedDates[ kClamp( start.x(), 0, (int)mSelectedDates.size() - 1 ) ]; TQDate dayEnd = mSelectedDates[ kClamp( end.x(), 0, (int)mSelectedDates.size() - 1 ) ]; TQTime timeStart = mAgenda->gyToTime(start.y()); TQTime timeEnd = mAgenda->gyToTime( end.y() + 1 ); TQDateTime dtStart(dayStart,timeStart); TQDateTime dtEnd(dayEnd,timeEnd); mTimeSpanBegin = dtStart; mTimeSpanEnd = dtEnd; } void KOAgendaView::deleteSelectedDateTime() { mTimeSpanBegin.setDate(TQDate()); mTimeSpanEnd.setDate(TQDate()); mTimeSpanInAllDay = false; } void KOAgendaView::setTypeAheadReceiver( TQObject *o ) { mAgenda->setTypeAheadReceiver( o ); mAllDayAgenda->setTypeAheadReceiver( o ); } void KOAgendaView::finishTypeAhead() { mAgenda->finishTypeAhead(); mAllDayAgenda->finishTypeAhead(); } void KOAgendaView::removeIncidence( Incidence *incidence ) { mAgenda->removeIncidence( incidence ); mAllDayAgenda->removeIncidence( incidence ); } void KOAgendaView::updateEventIndicators() { mMinY = mAgenda->minContentsY(); mMaxY = mAgenda->maxContentsY(); mAgenda->checkScrollBoundaries(); updateEventIndicatorTop( mAgenda->visibleContentsYMin() ); updateEventIndicatorBottom( mAgenda->visibleContentsYMax() ); } void KOAgendaView::setIncidenceChanger( IncidenceChangerBase *changer ) { mChanger = changer; mAgenda->setIncidenceChanger( changer ); mAllDayAgenda->setIncidenceChanger( changer ); } void KOAgendaView::clearTimeSpanSelection() { mAgenda->clearSelection(); mAllDayAgenda->clearSelection(); deleteSelectedDateTime(); } bool KOAgendaView::filterByResource( Incidence *incidence ) { // Special handling for groupware to-dos that are in Task folders. // Put them in the top-level "Calendar" folder for lack of a better // place since we never show Task type folders even in the // multiagenda view. if ( resourceCalendar() && incidence->type() == "Todo" ) { TQString subRes = resourceCalendar()->subresourceIdentifier( incidence ); if ( resourceCalendar()->subresourceType( subRes ) == "todo" ) { TQString calmatch = "/.INBOX.directory/Calendar"; TQString i18nmatch = "/.INBOX.directory/" + i18n( "Calendar" ); if ( subResourceCalendar().tqcontains( calmatch ) || subResourceCalendar().tqcontains( i18nmatch ) ) { return true; } } } // Normal handling if ( !resourceCalendar() ) return true; CalendarResources *calRes = dynamic_cast<CalendarResources*>( calendar() ); if ( !calRes ) return true; if ( calRes->resource( incidence ) != resourceCalendar() ) return false; if ( !subResourceCalendar().isEmpty() ) { if ( resourceCalendar()->subresourceIdentifier( incidence ) != subResourceCalendar() ) return false; } return true; } void KOAgendaView::resourcesChanged() { mPendingChanges = true; } void KOAgendaView::calendarIncidenceAdded(Incidence * incidence) { Q_UNUSED( incidence ); mPendingChanges = true; } void KOAgendaView::calendarIncidenceChanged(Incidence * incidence) { Q_UNUSED( incidence ); mPendingChanges = true; } void KOAgendaView::calendarIncidenceDeleted(Incidence * incidence) { Q_UNUSED( incidence ); mPendingChanges = true; }