korganizer

komonthview.cpp

00001 /*
00002     This file is part of KOrganizer.
00003 
00004     Copyright (c) 2000,2001 Cornelius Schumacher <schumacher@kde.org>
00005     Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
00006 
00007     This program is free software; you can redistribute it and/or modify
00008     it under the terms of the GNU General Public License as published by
00009     the Free Software Foundation; either version 2 of the License, or
00010     (at your option) any later version.
00011 
00012     This program is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00015     GNU General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program; if not, write to the Free Software
00019     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020 
00021     As a special exception, permission is given to link this program
00022     with any edition of Qt, and distribute the resulting executable,
00023     without including the source code for Qt in the source distribution.
00024 */
00025 
00026 #include <qpopupmenu.h>
00027 #include <qfont.h>
00028 #include <qfontmetrics.h>
00029 #include <qkeycode.h>
00030 #include <qhbox.h>
00031 #include <qvbox.h>
00032 #include <qpushbutton.h>
00033 #include <qtooltip.h>
00034 #include <qpainter.h>
00035 #include <qcursor.h>
00036 #include <qlistbox.h>
00037 #include <qlayout.h>
00038 #include <qlabel.h>
00039 
00040 #include <kdebug.h>
00041 #include <klocale.h>
00042 #include <kglobal.h>
00043 #include <kconfig.h>
00044 #include <kiconloader.h>
00045 #include <kwordwrap.h>
00046 
00047 #include <kcalendarsystem.h>
00048 #include <libkcal/calfilter.h>
00049 #include <libkcal/calendar.h>
00050 #include <libkcal/incidenceformatter.h>
00051 #include <libkcal/calendarresources.h>
00052 
00053 #include "koprefs.h"
00054 #include "koglobals.h"
00055 #include "koincidencetooltip.h"
00056 #include "koeventpopupmenu.h"
00057 #include "kohelper.h"
00058 
00059 #include "komonthview.h"
00060 #include "komonthview.moc"
00061 
00062 //--------------------------------------------------------------------------
00063 
00064 KOMonthCellToolTip::KOMonthCellToolTip( QWidget *parent,
00065                                         Calendar *calendar,
00066                                         const QDate &date,
00067                                         KNoScrollListBox *lv )
00068   : QToolTip( parent ), mCalendar( calendar ), mDate( date )
00069 {
00070   eventlist = lv;
00071 }
00072 
00073 void KOMonthCellToolTip::maybeTip( const QPoint &pos )
00074 {
00075   QRect r;
00076   QListBoxItem *it = eventlist->itemAt( pos );
00077   MonthViewItem *i = static_cast<MonthViewItem*>( it );
00078 
00079   if( i && KOPrefs::instance()->mEnableToolTips ) {
00080     /* Calculate the rectangle. */
00081     r=eventlist->itemRect( it );
00082     /* Show the tip */
00083     QString tipText( IncidenceFormatter::toolTipStr( mCalendar, i->incidence(), mDate ) );
00084     if ( !tipText.isEmpty() ) {
00085       tip( r, tipText );
00086     }
00087   }
00088 }
00089 
00090 KNoScrollListBox::KNoScrollListBox( QWidget *parent, const char *name )
00091   : QListBox( parent, name ),
00092     mSqueezing( false )
00093 {
00094   QPalette pal = palette();
00095   pal.setColor( QColorGroup::Foreground, KOPrefs::instance()->agendaBgColor().dark( 150 ) );
00096   pal.setColor( QColorGroup::Base, KOPrefs::instance()->agendaBgColor() );
00097   setPalette( pal );
00098 }
00099 
00100 void KNoScrollListBox::setBackground( bool primary, bool workDay )
00101 {
00102   QColor color;
00103   if ( workDay ) {
00104     color = KOPrefs::instance()->workingHoursColor();
00105   } else {
00106     color = KOPrefs::instance()->agendaBgColor();
00107   }
00108 
00109   QPalette pal = palette();
00110   if ( primary ) {
00111     pal.setColor( QColorGroup::Base, color );
00112   } else {
00113     pal.setColor( QColorGroup::Base, color.dark( 115 ) );
00114   }
00115   setPalette( pal );
00116 }
00117 
00118 void KNoScrollListBox::keyPressEvent( QKeyEvent *e )
00119 {
00120   switch( e->key() ) {
00121     case Key_Right:
00122       scrollBy( 4, 0 );
00123       break;
00124     case Key_Left:
00125       scrollBy( -4, 0 );
00126       break;
00127     case Key_Up:
00128       if ( !count() ) break;
00129       setCurrentItem( ( currentItem() + count() - 1 ) % count() );
00130       if ( !itemVisible( currentItem() ) ) {
00131         if ( (unsigned int)currentItem() == ( count() - 1 ) ) {
00132           setTopItem( currentItem() - numItemsVisible() + 1 );
00133         } else {
00134           setTopItem( topItem() - 1 );
00135         }
00136       }
00137       break;
00138     case Key_Down:
00139       if ( !count() ) break;
00140       setCurrentItem( ( currentItem() + 1 ) % count() );
00141       if( !itemVisible( currentItem() ) ) {
00142         if( currentItem() == 0 ) {
00143           setTopItem( 0 );
00144         } else {
00145           setTopItem( topItem() + 1 );
00146         }
00147       }
00148     case Key_Shift:
00149       emit shiftDown();
00150       break;
00151     default:
00152       break;
00153   }
00154 }
00155 
00156 void KNoScrollListBox::keyReleaseEvent( QKeyEvent *e )
00157 {
00158   switch( e->key() ) {
00159     case Key_Shift:
00160       emit shiftUp();
00161       break;
00162     default:
00163       break;
00164   }
00165 }
00166 
00167 void KNoScrollListBox::mousePressEvent( QMouseEvent *e )
00168 {
00169   QListBox::mousePressEvent( e );
00170 
00171   if ( e->button() == RightButton ) {
00172     emit rightClick();
00173   }
00174 }
00175 
00176 void KNoScrollListBox::contentsMouseDoubleClickEvent ( QMouseEvent * e )
00177 {
00178   QListBox::contentsMouseDoubleClickEvent( e );
00179   QListBoxItem *item = itemAt( e->pos() );
00180   if ( !item ) {
00181     emit doubleClicked( item );
00182   }
00183 }
00184 
00185 void KNoScrollListBox::resizeEvent( QResizeEvent *e )
00186 {
00187   bool s = count() && ( maxItemWidth() > e->size().width() );
00188   if ( mSqueezing || s )
00189     triggerUpdate( false );
00190 
00191   mSqueezing = s;
00192   QListBox::resizeEvent( e );
00193 }
00194 
00195 MonthViewItem::MonthViewItem( Incidence *incidence, const QDateTime &qd,
00196                               const QString & s ) : QListBoxItem()
00197 {
00198   setText( s );
00199 
00200   mIncidence = incidence;
00201   mDateTime = qd;
00202 
00203   mEventPixmap     = KOGlobals::self()->smallIcon( "appointment" );
00204   mBirthdayPixmap  = KOGlobals::self()->smallIcon( "calendarbirthday" );
00205   mAnniversaryPixmap= KOGlobals::self()->smallIcon( "calendaranniversary" );
00206   mTodoPixmap      = KOGlobals::self()->smallIcon( "todo" );
00207   mTodoDonePixmap  = KOGlobals::self()->smallIcon( "checkedbox" );
00208   mAlarmPixmap     = KOGlobals::self()->smallIcon( "bell" );
00209   mRecurPixmap     = KOGlobals::self()->smallIcon( "recur" );
00210   mReplyPixmap     = KOGlobals::self()->smallIcon( "mail_reply" );
00211 
00212   mEvent     = false;
00213   mTodo      = false;
00214   mTodoDone  = false;
00215   mRecur     = false;
00216   mAlarm     = false;
00217   mReply     = false;
00218 }
00219 
00220 QColor MonthViewItem::catColor() const
00221 {
00222   QColor retColor;
00223   if ( !mIncidence ) {
00224     return retColor;
00225   }
00226 
00227   QStringList categories = mIncidence->categories();
00228   QString cat;
00229   if ( !categories.isEmpty() ) {
00230     cat = categories.first();
00231   }
00232   if ( cat.isEmpty() ) {
00233     retColor = KOPrefs::instance()->unsetCategoryColor();
00234   } else {
00235     retColor = *( KOPrefs::instance()->categoryColor( cat ) );
00236   }
00237   return retColor;
00238 }
00239 
00240 void MonthViewItem::paint( QPainter *p )
00241 {
00242 #if QT_VERSION >= 0x030000
00243   bool sel = isSelected();
00244 #else
00245   bool sel = selected();
00246 #endif
00247 
00248   QColor bgColor = QColor(); // Default invalid color;
00249   if ( mIncidence && mTodo ) {
00250     if ( static_cast<Todo*>( mIncidence )->isOverdue() ) {
00251       bgColor = KOPrefs::instance()->todoOverdueColor();
00252     } else if ( static_cast<Todo*>( mIncidence )->dtDue().date() == QDate::currentDate() ) {
00253       bgColor = KOPrefs::instance()->todoDueTodayColor();
00254     }
00255   }
00256 
00257   if ( !bgColor.isValid() ) {
00258     if ( KOPrefs::instance()->monthItemColors() == KOPrefs::MonthItemResourceOnly ||
00259          KOPrefs::instance()->monthItemColors() == KOPrefs::MonthItemResourceInsideCategoryOutside ) {
00260       bgColor = resourceColor();
00261     } else {
00262       bgColor = catColor();
00263     }
00264 
00265     if ( !bgColor.isValid() ) {
00266       bgColor = palette().color( QPalette::Normal,
00267                                  sel ? QColorGroup::Highlight :
00268                                        QColorGroup::Background );
00269     }
00270   }
00271 
00272   QColor frameColor;
00273   if ( KOPrefs::instance()->monthItemColors() == KOPrefs::MonthItemResourceOnly ||
00274        KOPrefs::instance()->monthItemColors() == KOPrefs::MonthItemCategoryInsideResourceOutside ) {
00275     frameColor = resourceColor();
00276   } else {
00277     frameColor = catColor();
00278   }
00279 
00280   if ( mIncidence ) {
00281     if ( mIncidence->categories().isEmpty() &&
00282          KOPrefs::instance()->monthItemColors() == KOPrefs::MonthItemResourceInsideCategoryOutside ) {
00283       frameColor = bgColor;
00284     }
00285 
00286     if ( mIncidence->categories().isEmpty() &&
00287          KOPrefs::instance()->monthItemColors() == KOPrefs::MonthItemCategoryInsideResourceOutside ) {
00288       bgColor = frameColor;
00289     }
00290   }
00291 
00292   if ( !frameColor.isValid() ) {
00293     frameColor = palette().color( QPalette::Normal,
00294                                   sel ? QColorGroup::Highlight :
00295                                         QColorGroup::Foreground );
00296   } else {
00297     frameColor = frameColor.dark( 115 );
00298   }
00299 
00300   // draw the box for the item
00301   p->setBackgroundColor( frameColor );
00302   p->eraseRect( 0, 0, listBox()->maxItemWidth(), height( listBox() ) );
00303   int offset = 2;
00304   p->setBackgroundColor( bgColor );
00305   p->eraseRect( offset, offset, listBox()->maxItemWidth()-2*offset, height( listBox() )-2*offset );
00306 
00307   int x = 3;
00308 
00309   bool specialEvent = false;
00310   if ( mEvent ) {
00311     if ( mIncidence->customProperty( "KABC", "BIRTHDAY" ) == "YES" ) {
00312       specialEvent = true;
00313       if ( mIncidence->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
00314         p->drawPixmap( x, 0, mAnniversaryPixmap );
00315         x += mAnniversaryPixmap.width() + 2;
00316       } else {
00317         p->drawPixmap( x, 0, mBirthdayPixmap );
00318         x += mBirthdayPixmap.width() + 2;
00319       }
00320     // Do NOT put on the event pixmap because it takes up too much space
00321     //} else {
00322     //  p->drawPixmap( x, 0, mEventPixmap );
00323     //  x += mEventPixmap.width() + 2;
00324     //
00325     }
00326   }
00327 
00328   if ( mTodo ) {
00329     p->drawPixmap( x, 0, mTodoPixmap );
00330     x += mTodoPixmap.width() + 2;
00331   }
00332   if ( mTodoDone ) {
00333     p->drawPixmap( x, 0, mTodoDonePixmap );
00334     x += mTodoPixmap.width() + 2;
00335   }
00336   if ( mRecur && !specialEvent ) {
00337     p->drawPixmap( x, 0, mRecurPixmap );
00338     x += mRecurPixmap.width() + 2;
00339   }
00340   if ( mAlarm && !specialEvent ) {
00341     p->drawPixmap( x, 0, mAlarmPixmap );
00342     x += mAlarmPixmap.width() + 2;
00343   }
00344   if ( mReply ) {
00345     p->drawPixmap(x, 0, mReplyPixmap );
00346     x += mReplyPixmap.width() + 2;
00347   }
00348   QFontMetrics fm = p->fontMetrics();
00349   int yPos;
00350   int pmheight = QMAX( mRecurPixmap.height(),
00351                        QMAX( mAlarmPixmap.height(), mReplyPixmap.height() ) );
00352   if( pmheight < fm.height() )
00353     yPos = fm.ascent() + fm.leading()/2;
00354   else
00355     yPos = pmheight/2 - fm.height()/2  + fm.ascent();
00356   QColor textColor = getTextColor( bgColor );
00357   p->setPen( textColor );
00358 
00359   KWordWrap::drawFadeoutText( p, x, yPos, listBox()->width() - x, text() );
00360 }
00361 
00362 int MonthViewItem::height( const QListBox *lb ) const
00363 {
00364   return QMAX( QMAX( mRecurPixmap.height(), mReplyPixmap.height() ),
00365                QMAX( mAlarmPixmap.height(), lb->fontMetrics().lineSpacing()+1) );
00366 }
00367 
00368 int MonthViewItem::width( const QListBox *lb ) const
00369 {
00370   int x = 3;
00371   if( mRecur ) {
00372     x += mRecurPixmap.width()+2;
00373   }
00374   if( mAlarm ) {
00375     x += mAlarmPixmap.width()+2;
00376   }
00377   if( mReply ) {
00378     x += mReplyPixmap.width()+2;
00379   }
00380 
00381   return( x + lb->fontMetrics().boundingRect( text() ).width() + 1 );
00382 }
00383 
00384 
00385 MonthViewCell::MonthViewCell( KOMonthView *parent)
00386   : QWidget( parent ),
00387     mMonthView( parent ), mPrimary( false ), mHoliday( false )
00388 {
00389   QVBoxLayout *topLayout = new QVBoxLayout( this );
00390 
00391   mLabel = new QLabel( this );
00392   mLabel->setFrameStyle( QFrame::Panel | QFrame::Plain );
00393   mLabel->setLineWidth( 1 );
00394   mLabel->setAlignment( AlignCenter );
00395 
00396   mItemList = new KNoScrollListBox( this );
00397   mItemList->setMinimumSize( 10, 10 );
00398   mItemList->setFrameStyle( QFrame::Panel | QFrame::Plain );
00399   mItemList->setLineWidth( 1 );
00400 
00401   new KOMonthCellToolTip( mItemList->viewport(),
00402                           monthView()->calendar(),
00403                           mDate,
00404                           static_cast<KNoScrollListBox *>( mItemList ) );
00405 
00406   topLayout->addWidget( mItemList );
00407 
00408   mLabel->raise();
00409 
00410   mStandardPalette = palette();
00411 
00412   enableScrollBars( false );
00413 
00414   updateConfig();
00415 
00416   connect( mItemList, SIGNAL( doubleClicked( QListBoxItem *) ),
00417            SLOT( defaultAction( QListBoxItem * ) ) );
00418   connect( mItemList, SIGNAL( rightButtonPressed( QListBoxItem *,
00419                                                   const QPoint &) ),
00420            SLOT( contextMenu( QListBoxItem * ) ) );
00421   connect( mItemList, SIGNAL( clicked( QListBoxItem * ) ),
00422            SLOT( select() ) );
00423 }
00424 
00425 void MonthViewCell::setDate( const QDate &date )
00426 {
00427 //  kdDebug(5850) << "MonthViewCell::setDate(): " << date.toString() << endl;
00428 
00429   mDate = date;
00430 
00431   setFrameWidth();
00432 
00433   QString text;
00434   if ( KOGlobals::self()->calendarSystem()->day( date ) == 1 ) {
00435     text = i18n("'Month day' for month view cells", "%1 %2")
00436         .arg( KOGlobals::self()->calendarSystem()->monthName( date, true ) )
00437         .arg( KOGlobals::self()->calendarSystem()->day(mDate) );
00438     QFontMetrics fm( mLabel->font() );
00439     mLabel->resize( mLabelSize + QSize( fm.width( text ), 0 ) );
00440   } else {
00441     mLabel->resize( mLabelSize );
00442     text = QString::number( KOGlobals::self()->calendarSystem()->day(mDate) );
00443   }
00444   mLabel->setText( text );
00445 
00446   resizeEvent( 0 );
00447 }
00448 
00449 QDate MonthViewCell::date() const
00450 {
00451   return mDate;
00452 }
00453 
00454 void MonthViewCell::setFrameWidth()
00455 {
00456   // show current day with a thicker frame
00457   if ( mDate == QDate::currentDate() )
00458     mItemList->setLineWidth( 3 );
00459   else
00460     mItemList->setLineWidth( 1 );
00461 }
00462 
00463 void MonthViewCell::setPrimary( bool primary )
00464 {
00465   mPrimary = primary;
00466 
00467   if ( mPrimary ) {
00468     mLabel->setBackgroundMode( PaletteBase );
00469   } else {
00470     mLabel->setBackgroundMode( PaletteBackground );
00471   }
00472 
00473   mItemList->setBackground( mPrimary, KOGlobals::self()->isWorkDay( mDate ) );
00474 }
00475 
00476 bool MonthViewCell::isPrimary() const
00477 {
00478   return mPrimary;
00479 }
00480 
00481 void MonthViewCell::setHoliday( bool holiday )
00482 {
00483   mHoliday = holiday;
00484 
00485   if ( holiday ) {
00486     setPalette( mHolidayPalette );
00487   } else {
00488     setPalette( mStandardPalette );
00489   }
00490 }
00491 
00492 void MonthViewCell::setHolidayString( const QString &holiday )
00493 {
00494   mHolidayString = holiday;
00495 }
00496 
00497 void MonthViewCell::updateCell()
00498 {
00499   setFrameWidth();
00500 
00501   if ( mDate == QDate::currentDate() ) {
00502     setPalette( mTodayPalette );
00503 
00504     QPalette pal = mItemList->palette();
00505     pal.setColor( QColorGroup::Foreground, KOPrefs::instance()->highlightColor() );
00506     mItemList->setPalette( pal );
00507   }
00508   else {
00509     if ( mHoliday )
00510       setPalette( mHolidayPalette );
00511     else
00512       setPalette( mStandardPalette );
00513 
00514     QPalette pal = mItemList->palette();
00515     pal.setColor( QColorGroup::Foreground, KOPrefs::instance()->agendaBgColor().dark( 150 ) );
00516     mItemList->setPalette( pal );
00517   }
00518 
00519   mItemList->clear();
00520 
00521   if ( !mHolidayString.isEmpty() ) {
00522     MonthViewItem *item = new MonthViewItem( 0, QDateTime( mDate ), mHolidayString );
00523     item->setPalette( mHolidayPalette );
00524     mItemList->insertItem( item );
00525   }
00526 }
00527 
00528 class MonthViewCell::CreateItemVisitor :
00529       public IncidenceBase::Visitor
00530 {
00531   public:
00532     CreateItemVisitor() : mItem(0) { emails = KOPrefs::instance()->allEmails(); }
00533 
00534     bool act( IncidenceBase *incidence, QDate date, QPalette stdPal, int multiDay )
00535     {
00536       mItem = 0;
00537       mDate = date;
00538       mStandardPalette = stdPal;
00539       mMultiDay = multiDay;
00540       return incidence->accept( *this );
00541     }
00542     MonthViewItem *item() const { return mItem; }
00543     QStringList emails;
00544 
00545   protected:
00546     bool visit( Event *event ) {
00547       QString text;
00548       QDateTime dt( mDate );
00549       // take the time 0:00 into account, which is non-inclusive
00550       QDate dtEnd = event->dtEnd().addSecs( event->doesFloat() ? 0 : -1).date();
00551       int length = event->dtStart().daysTo( dtEnd );
00552       if ( event->isMultiDay() ) {
00553         if (  mDate == event->dtStart().date()
00554            || ( mMultiDay == 0 && event->recursOn( mDate ) ) ) {
00555           text = "(-- " + event->summary();
00556           dt = event->dtStart();
00557         } else if ( !event->doesRecur() && mDate == dtEnd
00558                  // last day of a recurring multi-day event?
00559                  || ( mMultiDay == length && event->recursOn( mDate.addDays( -length ) ) ) ) {
00560           text = event->summary() + " --)";
00561         } else if (!(event->dtStart().date().daysTo(mDate) % 7) && length > 7 ) {
00562           text = "-- " + event->summary() + " --";
00563         } else {
00564           text = "----------------";
00565           dt = QDateTime( mDate );
00566         }
00567       } else {
00568         if (event->doesFloat())
00569           text = event->summary();
00570         else {
00571           text = KGlobal::locale()->formatTime(event->dtStart().time());
00572           dt.setTime( event->dtStart().time() );
00573           text += ' ' + event->summary();
00574         }
00575       }
00576 
00577       mItem = new MonthViewItem( event, dt, text );
00578       mItem->setEvent( true );
00579       if ( KOPrefs::instance()->monthItemColors() == KOPrefs::MonthItemCategoryOnly ||
00580            KOPrefs::instance()->monthItemColors() == KOPrefs::MonthItemCategoryInsideResourceOutside ) {
00581         QStringList categories = event->categories();
00582         QString cat = categories.first();
00583         if (cat.isEmpty()) {
00584           mItem->setPalette(QPalette(KOPrefs::instance()->unsetCategoryColor(),
00585                                      KOPrefs::instance()->unsetCategoryColor()) );
00586         } else {
00587           mItem->setPalette(QPalette(*(KOPrefs::instance()->categoryColor(cat)),
00588                                      *(KOPrefs::instance()->categoryColor(cat))));
00589         }
00590       } else {
00591         mItem->setPalette( mStandardPalette );
00592       }
00593 
00594       Attendee *me = event->attendeeByMails( emails );
00595       if ( me != 0 ) {
00596         mItem->setReply( me->status() == Attendee::NeedsAction && me->RSVP() );
00597       } else
00598         mItem->setReply(false);
00599       return true;
00600     }
00601     bool visit( Todo *todo ) {
00602       QString text;
00603       if ( !KOPrefs::instance()->showAllDayTodo() )
00604         return false;
00605       QDateTime dt( mDate );
00606       if ( todo->hasDueDate() && !todo->doesFloat() ) {
00607         text += KGlobal::locale()->formatTime( todo->dtDue().time() );
00608         text += ' ';
00609         dt.setTime( todo->dtDue().time() );
00610       }
00611       text += todo->summary();
00612 
00613       mItem = new MonthViewItem( todo, dt, text );
00614       if ( todo->doesRecur() ) {
00615         mDate < todo->dtDue().date() ?
00616         mItem->setTodoDone( true ) : mItem->setTodo( true );
00617       }
00618       else
00619         todo->isCompleted() ? mItem->setTodoDone( true ) : mItem->setTodo( true );
00620       mItem->setPalette( mStandardPalette );
00621       return true;
00622     }
00623   protected:
00624     MonthViewItem *mItem;
00625     QDate mDate;
00626     QPalette mStandardPalette;
00627     int mMultiDay;
00628 };
00629 
00630 
00631 void MonthViewCell::addIncidence( Incidence *incidence, CreateItemVisitor& v, int multiDay )
00632 {
00633   if ( v.act( incidence, mDate, mStandardPalette, multiDay ) ) {
00634     MonthViewItem *item = v.item();
00635     if ( item ) {
00636       item->setAlarm( incidence->isAlarmEnabled() );
00637       item->setRecur( incidence->recurrenceType() );
00638 
00639       QColor resourceColor = KOHelper::resourceColor( monthView()->calendar(), incidence );
00640       if ( !resourceColor.isValid() )
00641         resourceColor = KOPrefs::instance()->unsetCategoryColor();
00642       item->setResourceColor( resourceColor );
00643 
00644       // FIXME: Find the correct position (time-wise) to insert the item.
00645       //        Currently, the items are displayed in "random" order instead of
00646       //        chronologically sorted.
00647       uint i = 0;
00648       int pos = -1;
00649       QDateTime dt( item->incidenceDateTime() );
00650 
00651       while ( i < mItemList->count() && pos<0 ) {
00652         QListBoxItem *item = mItemList->item( i );
00653         MonthViewItem *mvitem = dynamic_cast<MonthViewItem*>( item );
00654         if ( mvitem && mvitem->incidenceDateTime()>dt ) {
00655           pos = i;
00656         }
00657         ++i;
00658       }
00659       mItemList->insertItem( item, pos );
00660     }
00661   }
00662 }
00663 
00664 void MonthViewCell::removeIncidence( Incidence *incidence )
00665 {
00666   for ( uint i = 0; i < mItemList->count(); ++i ) {
00667     MonthViewItem *item = static_cast<MonthViewItem *>(mItemList->item( i ) );
00668     if ( item && item->incidence() &&
00669          item->incidence()->uid() == incidence->uid() ) {
00670       mItemList->removeItem( i );
00671       --i;
00672     }
00673   }
00674 }
00675 
00676 void MonthViewCell::updateConfig()
00677 {
00678   setFont( KOPrefs::instance()->mMonthViewFont );
00679 
00680   QFontMetrics fm( font() );
00681   mLabelSize = fm.size( 0, "30" ) +
00682                QSize( mLabel->frameWidth() * 2, mLabel->frameWidth() * 2 ) +
00683                QSize( 2, 2 );
00684 //  mStandardPalette = mOriginalPalette;
00685   QColor bg = mStandardPalette.color( QPalette::Active, QColorGroup::Background );
00686   int h,s,v;
00687   bg.getHsv( &h, &s, &v );
00688   if ( date().month() %2 == 0 ) {
00689     if ( v < 128 ) {
00690       bg = bg.light( 125 );
00691     } else {
00692       bg = bg.dark( 125 );
00693     }
00694   }
00695   setPaletteBackgroundColor( bg );
00696 //  mStandardPalette.setColor( QColorGroup::Background, bg);*/
00697 
00698   mHolidayPalette = mStandardPalette;
00699   mHolidayPalette.setColor( QColorGroup::Foreground,
00700                             KOPrefs::instance()->holidayColor() );
00701   mHolidayPalette.setColor( QColorGroup::Text,
00702                             KOPrefs::instance()->holidayColor() );
00703   mTodayPalette = mStandardPalette;
00704   mTodayPalette.setColor( QColorGroup::Foreground,
00705                           KOPrefs::instance()->highlightColor() );
00706   mTodayPalette.setColor( QColorGroup::Text,
00707                           KOPrefs::instance()->highlightColor() );
00708   updateCell();
00709 
00710   mItemList->setBackground( mPrimary, KOGlobals::self()->isWorkDay( mDate ) );
00711 }
00712 
00713 void MonthViewCell::enableScrollBars( bool enabled )
00714 {
00715   if ( enabled ) {
00716     mItemList->setVScrollBarMode( QScrollView::Auto );
00717     mItemList->setHScrollBarMode( QScrollView::Auto );
00718   } else {
00719     mItemList->setVScrollBarMode( QScrollView::AlwaysOff );
00720     mItemList->setHScrollBarMode( QScrollView::AlwaysOff );
00721   }
00722 }
00723 
00724 Incidence *MonthViewCell::selectedIncidence()
00725 {
00726   int index = mItemList->currentItem();
00727   if ( index < 0 ) return 0;
00728 
00729   MonthViewItem *item =
00730       static_cast<MonthViewItem *>( mItemList->item( index ) );
00731 
00732   if ( !item ) return 0;
00733 
00734   return item->incidence();
00735 }
00736 
00737 QDate MonthViewCell::selectedIncidenceDate()
00738 {
00739   QDate qd;
00740   int index = mItemList->currentItem();
00741   if ( index < 0 ) return qd;
00742 
00743   MonthViewItem *item =
00744       static_cast<MonthViewItem *>( mItemList->item( index ) );
00745 
00746   if ( !item ) return qd;
00747 
00748   return item->incidenceDateTime().date();
00749 }
00750 
00751 void MonthViewCell::select()
00752 {
00753   // setSelectedCell will deselect currently selected cells
00754   mMonthView->setSelectedCell( this );
00755 
00756   if( KOPrefs::instance()->enableMonthScroll() )
00757     enableScrollBars( true );
00758 
00759   // don't mess up the cell when it represents today
00760   if( mDate != QDate::currentDate() ) {
00761     mItemList->setFrameStyle( QFrame::Sunken | QFrame::Panel );
00762     mItemList->setLineWidth( 3 );
00763   }
00764 }
00765 
00766 void MonthViewCell::deselect()
00767 {
00768   mItemList->clearSelection();
00769   mItemList->setFrameStyle( QFrame::Plain | QFrame::Panel );
00770   setFrameWidth();
00771 
00772   enableScrollBars( false );
00773 }
00774 
00775 void MonthViewCell::resizeEvent ( QResizeEvent * )
00776 {
00777   mLabel->move( width() - mLabel->width(), height() - mLabel->height() );
00778 }
00779 
00780 void MonthViewCell::defaultAction( QListBoxItem *item )
00781 {
00782   select();
00783 
00784   if ( !item ) {
00785     emit newEventSignal( date() );
00786   } else {
00787     MonthViewItem *eventItem = static_cast<MonthViewItem *>( item );
00788     Incidence *incidence = eventItem->incidence();
00789     if ( incidence ) mMonthView->defaultAction( incidence );
00790   }
00791 }
00792 
00793 void MonthViewCell::contextMenu( QListBoxItem *item )
00794 {
00795   select();
00796 
00797   if ( item ) {
00798     MonthViewItem *eventItem = static_cast<MonthViewItem *>( item );
00799     Incidence *incidence = eventItem->incidence();
00800     if ( incidence ) mMonthView->showEventContextMenu( monthView()->calendar(), incidence, date() );
00801   }
00802   else {
00803     mMonthView->showGeneralContextMenu();
00804   }
00805 }
00806 
00807 
00808 KOMonthView::KOMonthView( Calendar *calendar, QWidget *parent, const char *name )
00809     : KOEventView( calendar, parent, name ),
00810       mDaysPerWeek( 7 ), mNumWeeks( 6 ), mNumCells( mDaysPerWeek * mNumWeeks ),
00811       mShortDayLabels( false ), mWidthLongDayLabel( 0 ), mSelectedCell( 0 )
00812 {
00813   mCells.setAutoDelete( true );
00814 
00815   QGridLayout *dayLayout = new QGridLayout( this );
00816 
00817   QFont bfont = font();
00818   bfont.setBold( true );
00819 
00820   QFont mfont = bfont;
00821   mfont.setPointSize( 20 );
00822 
00823   // month name on top
00824   mLabel = new QLabel( this );
00825   mLabel->setFont( mfont );
00826   mLabel->setAlignment( AlignCenter );
00827   mLabel->setLineWidth( 0 );
00828   mLabel->setFrameStyle( QFrame::Plain );
00829 
00830   dayLayout->addMultiCellWidget( mLabel, 0, 0, 0, mDaysPerWeek );
00831 
00832   // create the day of the week labels (Sun, Mon, etc) and add them to
00833   // the layout.
00834   mDayLabels.resize( mDaysPerWeek );
00835   int i;
00836   for( i = 0; i < mDaysPerWeek; i++ ) {
00837     QLabel *label = new QLabel( this );
00838     label->setFont( bfont );
00839     label->setFrameStyle( QFrame::Panel | QFrame::Raised );
00840     label->setLineWidth( 1 );
00841     label->setAlignment( AlignCenter );
00842 
00843     mDayLabels.insert( i, label );
00844 
00845     dayLayout->addWidget( label, 1, i );
00846     dayLayout->addColSpacing( i, 10 );
00847     dayLayout->setColStretch( i, 1 );
00848   }
00849 
00850   int row, col;
00851 
00852   mCells.resize( mNumCells );
00853   for( row = 0; row < mNumWeeks; ++row ) {
00854     for( col = 0; col < mDaysPerWeek; ++col ) {
00855       MonthViewCell *cell = new MonthViewCell( this );
00856       mCells.insert( row * mDaysPerWeek + col, cell );
00857       dayLayout->addWidget( cell, row + 2, col );
00858 
00859       connect( cell, SIGNAL( defaultAction( Incidence * ) ),
00860                SLOT( defaultAction( Incidence * ) ) );
00861       connect( cell, SIGNAL( newEventSignal( const QDate & ) ),
00862                SIGNAL( newEventSignal( const QDate & ) ) );
00863     }
00864     dayLayout->setRowStretch( row + 2, 1 );
00865   }
00866 
00867   mEventContextMenu = eventPopup();
00868 
00869   updateConfig();
00870 
00871   emit incidenceSelected( 0, QDate() );
00872 }
00873 
00874 KOMonthView::~KOMonthView()
00875 {
00876   delete mEventContextMenu;
00877 }
00878 
00879 int KOMonthView::maxDatesHint()
00880 {
00881   return mNumCells;
00882 }
00883 
00884 int KOMonthView::currentDateCount()
00885 {
00886   return mNumCells;
00887 }
00888 
00889 Incidence::List KOMonthView::selectedIncidences()
00890 {
00891   Incidence::List selected;
00892 
00893   if ( mSelectedCell ) {
00894     Incidence *incidence = mSelectedCell->selectedIncidence();
00895     if ( incidence ) selected.append( incidence );
00896   }
00897 
00898   return selected;
00899 }
00900 
00901 DateList KOMonthView::selectedDates()
00902 {
00903   DateList selected;
00904 
00905   if ( mSelectedCell ) {
00906     QDate qd = mSelectedCell->selectedIncidenceDate();
00907     if ( qd.isValid() ) selected.append( qd );
00908   }
00909 
00910   return selected;
00911 }
00912 
00913 bool KOMonthView::eventDurationHint( QDateTime &startDt, QDateTime &endDt, bool &allDay )
00914 {
00915   if ( mSelectedCell ) {
00916     startDt.setDate( mSelectedCell->date() );
00917     endDt.setDate( mSelectedCell->date() );
00918     allDay = true;
00919     return true;
00920   }
00921   return false;
00922 }
00923 
00924 void KOMonthView::updateConfig()
00925 {
00926   mWeekStartDay = KGlobal::locale()->weekStartDay();
00927 
00928   QFontMetrics fontmetric( mDayLabels[0]->font() );
00929   mWidthLongDayLabel = 0;
00930 
00931   for ( int i = 0; i < 7; ++i ) {
00932     int width =
00933         fontmetric.width( KOGlobals::self()->calendarSystem()->weekDayName( i + 1 ) );
00934     if ( width > mWidthLongDayLabel ) mWidthLongDayLabel = width;
00935   }
00936 
00937   updateDayLabels();
00938 
00939   for ( uint i = 0; i < mCells.count(); ++i ) {
00940     mCells[i]->updateConfig();
00941   }
00942 }
00943 
00944 void KOMonthView::updateDayLabels()
00945 {
00946   kdDebug(5850) << "KOMonthView::updateDayLabels()" << endl;
00947 
00948   const KCalendarSystem*calsys=KOGlobals::self()->calendarSystem();
00949   int currDay;
00950   for ( int i = 0; i < 7; i++ ) {
00951     currDay = i+mWeekStartDay;
00952     if ( currDay > 7 ) currDay -= 7;
00953     mDayLabels[i]->setText( calsys->weekDayName( currDay, mShortDayLabels ) );
00954   }
00955 }
00956 
00957 void KOMonthView::showDates( const QDate &start, const QDate & )
00958 {
00959 //  kdDebug(5850) << "KOMonthView::showDates(): " << start.toString() << endl;
00960 
00961   const KCalendarSystem *calSys = KOGlobals::self()->calendarSystem();
00962 
00963   mDateToCell.clear();
00964 
00965   // show first day of month on top for readability issues
00966   mStartDate = start.addDays( -start.day() + 1 );
00967   // correct begin of week
00968   int weekdayCol=( mStartDate.dayOfWeek() + 7 - mWeekStartDay ) % 7;
00969   mStartDate = mStartDate.addDays( -weekdayCol );
00970 
00971   mLabel->setText( i18n( "monthname year", "%1 %2" )
00972                    .arg( calSys->monthName( start ) )
00973                    .arg( calSys->year( start ) ) );
00974   if ( !KOPrefs::instance()->fullViewMonth() ) {
00975     mLabel->show();
00976   } else {
00977     mLabel->hide();
00978   }
00979 
00980   bool primary = false;
00981   uint i;
00982   for( i = 0; i < mCells.size(); ++i ) {
00983     QDate date = mStartDate.addDays( i );
00984     if ( calSys->day( date ) == 1 ) {
00985       primary = !primary;
00986     }
00987 
00988     mCells[i]->setDate( date );
00989     mDateToCell[ date ] = mCells[ i ];
00990     if( date == start )
00991       mCells[i]->select();
00992 
00993     mCells[i]->setPrimary( primary );
00994 
00995     bool isHoliday = calSys->dayOfWeek( date ) == calSys->weekDayOfPray()
00996                   || !KOGlobals::self()->isWorkDay( date );
00997     mCells[i]->setHoliday( isHoliday );
00998 
00999     // add holiday, if present
01000     QStringList holidays( KOGlobals::self()->holiday( date ) );
01001     mCells[i]->setHolidayString( holidays.join( i18n("delimiter for joining holiday names", ", " ) ) );
01002   }
01003 
01004   updateView();
01005 }
01006 
01007 void KOMonthView::showIncidences( const Incidence::List &, const QDate & )
01008 {
01009   kdDebug(5850) << "KOMonthView::showIncidences( const Incidence::List & ) is not implemented yet." << endl;
01010 }
01011 
01012 class KOMonthView::GetDateVisitor : public IncidenceBase::Visitor
01013 {
01014   public:
01015     GetDateVisitor() {}
01016 
01017     bool act( IncidenceBase *incidence )
01018     {
01019       return incidence->accept( *this );
01020     }
01021     QDateTime startDate() const { return mStartDate; }
01022     QDateTime endDate() const { return mEndDate; }
01023 
01024   protected:
01025     bool visit( Event *event ) {
01026       mStartDate = event->dtStart();
01027       mEndDate = event->dtEnd();
01028       return true;
01029     }
01030     bool visit( Todo *todo ) {
01031       if ( todo->hasDueDate() ) {
01032         mStartDate = todo->dtDue();
01033         mEndDate = todo->dtDue();
01034       }// else
01035 //         return false;
01036       return true;
01037     }
01038     bool visit( Journal *journal ) {
01039       mStartDate = journal->dtStart();
01040       mEndDate = journal->dtStart();
01041       return true;
01042     }
01043   protected:
01044     QDateTime mStartDate;
01045     QDateTime mEndDate;
01046 };
01047 
01048 void KOMonthView::changeIncidenceDisplayAdded( Incidence *incidence, MonthViewCell::CreateItemVisitor& v)
01049 {
01050   GetDateVisitor gdv;
01051 
01052   if ( !gdv.act( incidence ) ) {
01053     kdDebug(5850) << "Visiting GetDateVisitor failed." << endl;
01054     return;
01055   }
01056 
01057   bool floats = incidence->doesFloat();
01058 
01059   if ( incidence->doesRecur() ) {
01060     for ( uint i = 0; i < mCells.count(); ++i ) {
01061       if ( incidence->recursOn( mCells[i]->date() ) ) {
01062 
01063         // handle multiday events
01064         int length = gdv.startDate().daysTo( gdv.endDate().addSecs( floats ? 0 : -1 ).date() );
01065         for ( int j = 0; j <= length && i+j < mCells.count(); ++j ) {
01066           mCells[i+j]->addIncidence( incidence, v, j );
01067         }
01068       }
01069     }
01070   } else {
01071     // addSecs(-1) is added to handle 0:00 cases (because it's non-inclusive according to rfc)
01072     if ( gdv.endDate().isValid() ) {
01073       QDate endDate = gdv.endDate().addSecs( floats ? 0 : -1).date();
01074       for ( QDate date = gdv.startDate().date();
01075             date <= endDate; date = date.addDays( 1 ) ) {
01076         MonthViewCell *mvc = mDateToCell[ date ];
01077         if ( mvc ) mvc->addIncidence( incidence, v );
01078       }
01079     }
01080   }
01081 }
01082 
01083 void KOMonthView::changeIncidenceDisplay( Incidence *incidence, int action )
01084 {
01085   MonthViewCell::CreateItemVisitor v;
01086   switch ( action ) {
01087     case KOGlobals::INCIDENCEADDED:
01088       changeIncidenceDisplayAdded( incidence, v );
01089       break;
01090     case KOGlobals::INCIDENCEEDITED:
01091       for( uint i = 0; i < mCells.count(); i++ )
01092         mCells[i]->removeIncidence( incidence );
01093       changeIncidenceDisplayAdded( incidence, v );
01094       break;
01095     case KOGlobals::INCIDENCEDELETED:
01096       for( uint i = 0; i < mCells.count(); i++ )
01097         mCells[i]->removeIncidence( incidence );
01098       break;
01099     default:
01100       return;
01101   }
01102 }
01103 
01104 void KOMonthView::updateView()
01105 {
01106   for( uint i = 0; i < mCells.count(); ++i ) {
01107     mCells[i]->updateCell();
01108   }
01109 
01110   Incidence::List incidences = calendar()->incidences();
01111   Incidence::List::ConstIterator it;
01112 
01113   MonthViewCell::CreateItemVisitor v;
01114   for ( it = incidences.begin(); it != incidences.end(); ++it )
01115     changeIncidenceDisplayAdded( *it, v );
01116 
01117   processSelectionChange();
01118 }
01119 
01120 void KOMonthView::resizeEvent( QResizeEvent * )
01121 {
01122   // select the appropriate heading string size. E.g. "Wednesday" or "Wed".
01123   // note this only changes the text if the requested size crosses the
01124   // threshold between big enough to support the full name and not big
01125   // enough.
01126   if( mDayLabels[0]->width() < mWidthLongDayLabel ) {
01127     if ( !mShortDayLabels ) {
01128       mShortDayLabels = true;
01129       updateDayLabels();
01130     }
01131   } else {
01132     if ( mShortDayLabels ) {
01133       mShortDayLabels = false;
01134       updateDayLabels();
01135     }
01136   }
01137 }
01138 
01139 void KOMonthView::showEventContextMenu( Calendar *cal, Incidence *incidence, const QDate &qd )
01140 {
01141   mEventContextMenu->showIncidencePopup( cal, incidence, qd );
01142 }
01143 
01144 void KOMonthView::showGeneralContextMenu()
01145 {
01146   showNewEventPopup();
01147 }
01148 
01149 void KOMonthView::setSelectedCell( MonthViewCell *cell )
01150 {
01151   if ( mSelectedCell && cell != mSelectedCell )
01152     mSelectedCell->deselect();
01153 
01154   mSelectedCell = cell;
01155 
01156   if ( !mSelectedCell )
01157     emit incidenceSelected( 0, QDate() );
01158   else
01159     if ( selectedDates().isEmpty() ) {
01160       emit incidenceSelected( mSelectedCell->selectedIncidence(), QDate() );
01161     } else {
01162       emit incidenceSelected( mSelectedCell->selectedIncidence(), selectedDates().first() );
01163     }
01164 }
01165 
01166 void KOMonthView::processSelectionChange()
01167 {
01168   Incidence::List incidences = selectedIncidences();
01169   if (incidences.count() > 0) {
01170     if ( selectedDates().isEmpty() ) {
01171       emit incidenceSelected( incidences.first(), QDate() );
01172     } else {
01173       emit incidenceSelected( incidences.first(), selectedDates().first() );
01174     }
01175   } else {
01176     emit incidenceSelected( 0, QDate() );
01177   }
01178 }
01179 
01180 void KOMonthView::clearSelection()
01181 {
01182   if ( mSelectedCell ) {
01183     mSelectedCell->deselect();
01184     mSelectedCell = 0;
01185   }
01186 }