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