/*
    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 TQt, and distribute the resulting executable,
    without including the source code for TQt 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 <tdeapplication.h>
#include <kdebug.h>
#include <kstandarddirs.h>
#include <kiconloader.h>
#include <tdelocale.h>
#include <tdeconfig.h>
#include <tdeglobal.h>
#include <tdeglobalsettings.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)
{
  setSizePolicy(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::minimumSizeHint() const
{
  TQSize sh = TQLabel::minimumSizeHint();
  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::currentDate());

  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(Qt::Vertical,this);
  topLayout->addWidget(mSplitterAgenda);

#if KDE_IS_VERSION( 3, 1, 93 )
  mSplitterAgenda->setOpaqueResize( TDEGlobalSettings::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->setSizePolicy( TQSizePolicy( TQSizePolicy::Fixed,
                                  TQSizePolicy::Fixed ) );
    connect( mExpandButton, TQT_SIGNAL( clicked() ), TQT_SIGNAL( toggleExpand() ) );
  } else {
    TQLabel *label = new TQLabel( i18n("All Day"), mDummyAllDayLeft );
    label->setAlignment( TQt::AlignRight | TQt::AlignVCenter | TQt::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->repaint();

  updateView();
}

void KOAgendaView::zoomOutVertically( )
{

  if ( KOPrefs::instance()->mHourSize > 4 || mIsSideBySide ) {

    if ( !mIsSideBySide )
      KOPrefs::instance()->mHourSize--;
    mAgenda->updateConfig();
    mAgenda->checkScrollBoundaries();

    mTimeLabels->updateConfig();
    mTimeLabels->positionChanged();
    mTimeLabels->repaint();

    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 {
    // Qt::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 = TDEGlobal::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->setAlignment(TQLabel::AlignHCenter);
    if (date == TQDate::currentDate()) {
      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(), mDayLabels );
      label->setMinimumWidth(1);
      label->setAlignment(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(), mDayLabels );
        label->setMinimumWidth(1);
        label->setAlignment(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::currentDateCount()
{
  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->repaint();

  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 = TQMAX( 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 layout 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::currentDate();
  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 ( currentDateCount() == 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(TDEConfig *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(TDEConfig *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 mask:
  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().contains( calmatch ) ||
           subResourceCalendar().contains( 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;
}
