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     isSelected( false )
00389 {
00390   QVBoxLayout *topLayout = new QVBoxLayout( this );
00391 
00392   mLabel = new QLabel( this );
00393   mLabel->setFrameStyle( QFrame::Panel | QFrame::Plain );
00394   mLabel->setLineWidth( 1 );
00395   mLabel->setAlignment( AlignCenter );
00396 
00397   mItemList = new KNoScrollListBox( this );
00398   mItemList->setMinimumSize( 10, 10 );
00399   mItemList->setFrameStyle( QFrame::Panel | QFrame::Plain );
00400   mItemList->setLineWidth( 1 );
00401 
00402   topLayout->addWidget( mItemList );
00403 
00404   mLabel->raise();
00405 
00406   mStandardPalette = palette();
00407 
00408   enableScrollBars( false );
00409 
00410   updateConfig();
00411 
00412   connect( mItemList, SIGNAL( doubleClicked( QListBoxItem *) ),
00413            SLOT( defaultAction( QListBoxItem * ) ) );
00414   connect( mItemList, SIGNAL( rightButtonPressed( QListBoxItem *,
00415                                                   const QPoint &) ),
00416            SLOT( contextMenu( QListBoxItem * ) ) );
00417   connect( mItemList, SIGNAL( clicked( QListBoxItem * ) ),
00418            SLOT( select() ) );
00419 }
00420 
00421 void MonthViewCell::setDate( const QDate &date )
00422 {
00423 //  kdDebug(5850) << "MonthViewCell::setDate(): " << date.toString() << endl;
00424 
00425   mDate = date;
00426 
00427   setFrameWidth();
00428 
00429   QString text;
00430   if ( KOGlobals::self()->calendarSystem()->day( date ) == 1 ) {
00431     text = i18n("'Month day' for month view cells", "%1 %2")
00432         .arg( KOGlobals::self()->calendarSystem()->monthName( date, true ) )
00433         .arg( KOGlobals::self()->calendarSystem()->day(mDate) );
00434     QFontMetrics fm( mLabel->font() );
00435     mLabel->resize( mLabelSize + QSize( fm.width( text ), 0 ) );
00436   } else {
00437     mLabel->resize( mLabelSize );
00438     text = QString::number( KOGlobals::self()->calendarSystem()->day(mDate) );
00439   }
00440   mLabel->setText( text );
00441 
00442   new KOMonthCellToolTip( mItemList->viewport(),
00443                           monthView()->calendar(),
00444                           mDate,
00445                           static_cast<KNoScrollListBox *>( mItemList ) );
00446 
00447   resizeEvent( 0 );
00448 }
00449 
00450 QDate MonthViewCell::date() const
00451 {
00452   return mDate;
00453 }
00454 
00455 void MonthViewCell::setFrameWidth()
00456 {
00457   // show current day with a thicker frame
00458   if ( mDate == QDate::currentDate() ) {
00459     mItemList->setLineWidth( 3 );
00460   } else if ( !isSelected ) {
00461     mItemList->setLineWidth( 1 );
00462   }
00463 }
00464 
00465 void MonthViewCell::setPrimary( bool primary )
00466 {
00467   mPrimary = primary;
00468 
00469   if ( mPrimary ) {
00470     mLabel->setBackgroundMode( PaletteBase );
00471   } else {
00472     mLabel->setBackgroundMode( PaletteBackground );
00473   }
00474 
00475   mItemList->setBackground( mPrimary, KOGlobals::self()->isWorkDay( mDate ) );
00476 }
00477 
00478 bool MonthViewCell::isPrimary() const
00479 {
00480   return mPrimary;
00481 }
00482 
00483 void MonthViewCell::setHoliday( bool holiday )
00484 {
00485   mHoliday = holiday;
00486 
00487   if ( holiday ) {
00488     setPalette( mHolidayPalette );
00489   } else {
00490     setPalette( mStandardPalette );
00491   }
00492 }
00493 
00494 void MonthViewCell::setHolidayString( const QString &holiday )
00495 {
00496   mHolidayString = holiday;
00497 }
00498 
00499 void MonthViewCell::updateCell()
00500 {
00501   setFrameWidth();
00502 
00503   if ( mDate == QDate::currentDate() ) {
00504     setPalette( mTodayPalette );
00505 
00506     QPalette pal = mItemList->palette();
00507     pal.setColor( QColorGroup::Foreground, KOPrefs::instance()->highlightColor() );
00508     mItemList->setPalette( pal );
00509   }
00510   else {
00511     if ( mHoliday )
00512       setPalette( mHolidayPalette );
00513     else
00514       setPalette( mStandardPalette );
00515 
00516     QPalette pal = mItemList->palette();
00517     pal.setColor( QColorGroup::Foreground, KOPrefs::instance()->agendaBgColor().dark( 150 ) );
00518     mItemList->setPalette( pal );
00519   }
00520 
00521   mItemList->clear();
00522 
00523   if ( !mHolidayString.isEmpty() ) {
00524     MonthViewItem *item = new MonthViewItem( 0, QDateTime( mDate ), mHolidayString );
00525     item->setPalette( mHolidayPalette );
00526     mItemList->insertItem( item );
00527   }
00528 }
00529 
00530 class MonthViewCell::CreateItemVisitor :
00531       public IncidenceBase::Visitor
00532 {
00533   public:
00534     CreateItemVisitor() : mItem(0) { emails = KOPrefs::instance()->allEmails(); }
00535 
00536     bool act( IncidenceBase *incidence, QDate date, QPalette stdPal, int multiDay )
00537     {
00538       mItem = 0;
00539       mDate = date;
00540       mStandardPalette = stdPal;
00541       mMultiDay = multiDay;
00542       return incidence->accept( *this );
00543     }
00544     MonthViewItem *item() const { return mItem; }
00545     QStringList emails;
00546 
00547   protected:
00548     bool visit( Event *event ) {
00549       QString text;
00550       QDateTime dt( mDate );
00551       // take the time 0:00 into account, which is non-inclusive
00552       QDate dtEnd = event->dtEnd().addSecs( event->doesFloat() ? 0 : -1).date();
00553       int length = event->dtStart().daysTo( dtEnd );
00554       if ( event->isMultiDay() ) {
00555         if (  mDate == event->dtStart().date()
00556            || ( mMultiDay == 0 && event->recursOn( mDate ) ) ) {
00557           text = "(-- " + event->summary();
00558           dt = event->dtStart();
00559         } else if ( !event->doesRecur() && mDate == dtEnd
00560                  // last day of a recurring multi-day event?
00561                  || ( mMultiDay == length && event->recursOn( mDate.addDays( -length ) ) ) ) {
00562           text = event->summary() + " --)";
00563         } else if (!(event->dtStart().date().daysTo(mDate) % 7) && length > 7 ) {
00564           text = "-- " + event->summary() + " --";
00565         } else {
00566           text = "----------------";
00567           dt = QDateTime( mDate );
00568         }
00569       } else {
00570         if (event->doesFloat())
00571           text = event->summary();
00572         else {
00573           text = KGlobal::locale()->formatTime(event->dtStart().time());
00574           dt.setTime( event->dtStart().time() );
00575           text += ' ' + event->summary();
00576         }
00577       }
00578 
00579       mItem = new MonthViewItem( event, dt, text );
00580       mItem->setEvent( true );
00581       if ( KOPrefs::instance()->monthItemColors() == KOPrefs::MonthItemCategoryOnly ||
00582            KOPrefs::instance()->monthItemColors() == KOPrefs::MonthItemCategoryInsideResourceOutside ) {
00583         QStringList categories = event->categories();
00584         QString cat = categories.first();
00585         if (cat.isEmpty()) {
00586           mItem->setPalette(QPalette(KOPrefs::instance()->unsetCategoryColor(),
00587                                      KOPrefs::instance()->unsetCategoryColor()) );
00588         } else {
00589           mItem->setPalette(QPalette(*(KOPrefs::instance()->categoryColor(cat)),
00590                                      *(KOPrefs::instance()->categoryColor(cat))));
00591         }
00592       } else {
00593         mItem->setPalette( mStandardPalette );
00594       }
00595 
00596       Attendee *me = event->attendeeByMails( emails );
00597       if ( me != 0 ) {
00598         mItem->setReply( me->status() == Attendee::NeedsAction && me->RSVP() );
00599       } else
00600         mItem->setReply(false);
00601       return true;
00602     }
00603     bool visit( Todo *todo ) {
00604       QString text;
00605       if ( !KOPrefs::instance()->showAllDayTodo() )
00606         return false;
00607       QDateTime dt( mDate );
00608       if ( todo->hasDueDate() && !todo->doesFloat() &&
00609            todo->dtDue().time() != QTime( 0,0 ) && todo->dtDue().time().isValid() ) {
00610         text += KGlobal::locale()->formatTime( todo->dtDue().time() );
00611         text += ' ';
00612         dt.setTime( todo->dtDue().time() );
00613       }
00614       text += todo->summary();
00615 
00616       mItem = new MonthViewItem( todo, dt, text );
00617       if ( todo->doesRecur() ) {
00618         mDate < todo->dtDue().date() ?
00619         mItem->setTodoDone( true ) : mItem->setTodo( true );
00620       }
00621       else
00622         todo->isCompleted() ? mItem->setTodoDone( true ) : mItem->setTodo( true );
00623       mItem->setPalette( mStandardPalette );
00624       return true;
00625     }
00626   protected:
00627     MonthViewItem *mItem;
00628     QDate mDate;
00629     QPalette mStandardPalette;
00630     int mMultiDay;
00631 };
00632 
00633 
00634 void MonthViewCell::addIncidence( Incidence *incidence, CreateItemVisitor& v, int multiDay )
00635 {
00636   if ( v.act( incidence, mDate, mStandardPalette, multiDay ) ) {
00637     MonthViewItem *item = v.item();
00638     if ( item ) {
00639       item->setAlarm( incidence->isAlarmEnabled() );
00640       item->setRecur( incidence->recurrenceType() );
00641 
00642       QColor resourceColor = KOHelper::resourceColor( monthView()->calendar(), incidence );
00643       if ( !resourceColor.isValid() )
00644         resourceColor = KOPrefs::instance()->unsetCategoryColor();
00645       item->setResourceColor( resourceColor );
00646 
00647       // FIXME: Find the correct position (time-wise) to insert the item.
00648       //        Currently, the items are displayed in "random" order instead of
00649       //        chronologically sorted.
00650       uint i = 0;
00651       int pos = -1;
00652       QDateTime dt( item->incidenceDateTime() );
00653 
00654       while ( i < mItemList->count() && pos<0 ) {
00655         QListBoxItem *item = mItemList->item( i );
00656         MonthViewItem *mvitem = dynamic_cast<MonthViewItem*>( item );
00657         if ( mvitem && mvitem->incidenceDateTime()>dt ) {
00658           pos = i;
00659         }
00660         ++i;
00661       }
00662       mItemList->insertItem( item, pos );
00663     }
00664   }
00665 }
00666 
00667 void MonthViewCell::removeIncidence( Incidence *incidence )
00668 {
00669   for ( uint i = 0; i < mItemList->count(); ++i ) {
00670     MonthViewItem *item = static_cast<MonthViewItem *>(mItemList->item( i ) );
00671     if ( item && item->incidence() &&
00672          item->incidence()->uid() == incidence->uid() ) {
00673       mItemList->removeItem( i );
00674       --i;
00675     }
00676   }
00677 }
00678 
00679 void MonthViewCell::updateConfig()
00680 {
00681   setFont( KOPrefs::instance()->mMonthViewFont );
00682 
00683   QFontMetrics fm( font() );
00684   mLabelSize = fm.size( 0, "30" ) +
00685                QSize( mLabel->frameWidth() * 2, mLabel->frameWidth() * 2 ) +
00686                QSize( 2, 2 );
00687 //  mStandardPalette = mOriginalPalette;
00688   QColor bg = mStandardPalette.color( QPalette::Active, QColorGroup::Background );
00689   int h,s,v;
00690   bg.getHsv( &h, &s, &v );
00691   if ( date().month() %2 == 0 ) {
00692     if ( v < 128 ) {
00693       bg = bg.light( 125 );
00694     } else {
00695       bg = bg.dark( 125 );
00696     }
00697   }
00698   setPaletteBackgroundColor( bg );
00699 //  mStandardPalette.setColor( QColorGroup::Background, bg);*/
00700 
00701   mHolidayPalette = mStandardPalette;
00702   mHolidayPalette.setColor( QColorGroup::Foreground,
00703                             KOPrefs::instance()->holidayColor() );
00704   mHolidayPalette.setColor( QColorGroup::Text,
00705                             KOPrefs::instance()->holidayColor() );
00706   mTodayPalette = mStandardPalette;
00707   mTodayPalette.setColor( QColorGroup::Foreground,
00708                           KOPrefs::instance()->highlightColor() );
00709   mTodayPalette.setColor( QColorGroup::Text,
00710                           KOPrefs::instance()->highlightColor() );
00711   updateCell();
00712 
00713   mItemList->setBackground( mPrimary, KOGlobals::self()->isWorkDay( mDate ) );
00714 }
00715 
00716 void MonthViewCell::enableScrollBars( bool enabled )
00717 {
00718   if ( enabled ) {
00719     mItemList->setVScrollBarMode( QScrollView::Auto );
00720     mItemList->setHScrollBarMode( QScrollView::Auto );
00721   } else {
00722     mItemList->setVScrollBarMode( QScrollView::AlwaysOff );
00723     mItemList->setHScrollBarMode( QScrollView::AlwaysOff );
00724   }
00725 }
00726 
00727 Incidence *MonthViewCell::selectedIncidence()
00728 {
00729   int index = mItemList->currentItem();
00730   if ( index < 0 ) return 0;
00731 
00732   MonthViewItem *item =
00733       static_cast<MonthViewItem *>( mItemList->item( index ) );
00734 
00735   if ( !item ) return 0;
00736 
00737   return item->incidence();
00738 }
00739 
00740 QDate MonthViewCell::selectedIncidenceDate()
00741 {
00742   QDate qd;
00743   int index = mItemList->currentItem();
00744   if ( index < 0 ) return qd;
00745 
00746   MonthViewItem *item =
00747       static_cast<MonthViewItem *>( mItemList->item( index ) );
00748 
00749   if ( !item ) return qd;
00750 
00751   return item->incidenceDateTime().date();
00752 }
00753 
00754 void MonthViewCell::select()
00755 {
00756 
00757   isSelected = true;
00758 
00759   // setSelectedCell will deselect currently selected cells
00760   mMonthView->setSelectedCell( this );
00761 
00762   if( KOPrefs::instance()->enableMonthScroll() )
00763     enableScrollBars( true );
00764 
00765   // don't mess up the cell when it represents today
00766   if( mDate != QDate::currentDate() ) {
00767     mItemList->setFrameStyle( QFrame::Sunken | QFrame::Panel );
00768     mItemList->setLineWidth( 3 );
00769   }
00770 }
00771 
00772 void MonthViewCell::deselect()
00773 {
00774   isSelected = false;
00775 
00776   mItemList->clearSelection();
00777   mItemList->setFrameStyle( QFrame::Plain | QFrame::Panel );
00778   setFrameWidth();
00779 
00780   enableScrollBars( false );
00781 }
00782 
00783 void MonthViewCell::resizeEvent ( QResizeEvent * )
00784 {
00785   mLabel->move( width() - mLabel->width(), height() - mLabel->height() );
00786 }
00787 
00788 void MonthViewCell::defaultAction( QListBoxItem *item )
00789 {
00790   select();
00791 
00792   if ( !item ) {
00793     emit newEventSignal( 0/*ResourceCalendar*/, QString()/*subResource*/, date() );
00794   } else {
00795     MonthViewItem *eventItem = static_cast<MonthViewItem *>( item );
00796     Incidence *incidence = eventItem->incidence();
00797     if ( incidence ) mMonthView->defaultAction( incidence );
00798   }
00799 }
00800 
00801 void MonthViewCell::contextMenu( QListBoxItem *item )
00802 {
00803   select();
00804 
00805   if ( item ) {
00806     MonthViewItem *eventItem = static_cast<MonthViewItem *>( item );
00807     Incidence *incidence = eventItem->incidence();
00808     if ( incidence ) {
00809       mMonthView->showEventContextMenu( monthView()->calendar(), incidence, mDate );
00810     }
00811   }  else {
00812     mMonthView->showGeneralContextMenu();
00813   }
00814 }
00815 
00816 
00817 KOMonthView::KOMonthView( Calendar *calendar, QWidget *parent, const char *name )
00818     : KOEventView( calendar, parent, name ),
00819       mDaysPerWeek( 7 ), mNumWeeks( 6 ), mNumCells( mDaysPerWeek * mNumWeeks ),
00820       mShortDayLabels( false ), mWidthLongDayLabel( 0 ), mSelectedCell( 0 )
00821 {
00822   mCells.setAutoDelete( true );
00823 
00824   QGridLayout *dayLayout = new QGridLayout( this );
00825 
00826   QFont bfont = font();
00827   bfont.setBold( true );
00828 
00829   QFont mfont = bfont;
00830   mfont.setPointSize( 20 );
00831 
00832   // month name on top
00833   mLabel = new QLabel( this );
00834   mLabel->setFont( mfont );
00835   mLabel->setAlignment( AlignCenter );
00836   mLabel->setLineWidth( 0 );
00837   mLabel->setFrameStyle( QFrame::Plain );
00838 
00839   dayLayout->addMultiCellWidget( mLabel, 0, 0, 0, mDaysPerWeek );
00840 
00841   // create the day of the week labels (Sun, Mon, etc) and add them to
00842   // the layout.
00843   mDayLabels.resize( mDaysPerWeek );
00844   int i;
00845   for( i = 0; i < mDaysPerWeek; i++ ) {
00846     QLabel *label = new QLabel( this );
00847     label->setFont( bfont );
00848     label->setFrameStyle( QFrame::Panel | QFrame::Raised );
00849     label->setLineWidth( 1 );
00850     label->setAlignment( AlignCenter );
00851 
00852     mDayLabels.insert( i, label );
00853 
00854     dayLayout->addWidget( label, 1, i );
00855     dayLayout->addColSpacing( i, 10 );
00856     dayLayout->setColStretch( i, 1 );
00857   }
00858 
00859   int row, col;
00860 
00861   mCells.resize( mNumCells );
00862   for( row = 0; row < mNumWeeks; ++row ) {
00863     for( col = 0; col < mDaysPerWeek; ++col ) {
00864       MonthViewCell *cell = new MonthViewCell( this );
00865       mCells.insert( row * mDaysPerWeek + col, cell );
00866       dayLayout->addWidget( cell, row + 2, col );
00867 
00868       connect( cell, SIGNAL(defaultAction(Incidence *)),
00869                SLOT(defaultAction(Incidence *)) );
00870       connect( cell, SIGNAL(newEventSignal(ResourceCalendar *,const QString &,const QDate &)),
00871                SIGNAL(newEventSignal(ResourceCalendar *,const QString &,const QDate &)) );
00872     }
00873     dayLayout->setRowStretch( row + 2, 1 );
00874   }
00875 
00876   mEventContextMenu = eventPopup();
00877 
00878   updateConfig();
00879 
00880   emit incidenceSelected( 0, QDate() );
00881 }
00882 
00883 KOMonthView::~KOMonthView()
00884 {
00885   delete mEventContextMenu;
00886 }
00887 
00888 int KOMonthView::maxDatesHint()
00889 {
00890   return mNumCells;
00891 }
00892 
00893 int KOMonthView::currentDateCount()
00894 {
00895   return mNumCells;
00896 }
00897 
00898 Incidence::List KOMonthView::selectedIncidences()
00899 {
00900   Incidence::List selected;
00901 
00902   if ( mSelectedCell ) {
00903     Incidence *incidence = mSelectedCell->selectedIncidence();
00904     if ( incidence ) selected.append( incidence );
00905   }
00906 
00907   return selected;
00908 }
00909 
00910 DateList KOMonthView::selectedIncidenceDates()
00911 {
00912   DateList selected;
00913 
00914   if ( mSelectedCell ) {
00915     QDate qd = mSelectedCell->selectedIncidenceDate();
00916     if ( qd.isValid() ) selected.append( qd );
00917   }
00918 
00919   return selected;
00920 }
00921 
00922 bool KOMonthView::eventDurationHint( QDateTime &startDt, QDateTime &endDt, bool &allDay )
00923 {
00924   if ( mSelectedCell ) {
00925     startDt.setDate( mSelectedCell->date() );
00926     endDt.setDate( mSelectedCell->date() );
00927     allDay = true;
00928     return true;
00929   }
00930   return false;
00931 }
00932 
00933 void KOMonthView::updateConfig()
00934 {
00935   mWeekStartDay = KGlobal::locale()->weekStartDay();
00936 
00937   QFontMetrics fontmetric( mDayLabels[0]->font() );
00938   mWidthLongDayLabel = 0;
00939 
00940   for ( int i = 0; i < 7; ++i ) {
00941     int width =
00942         fontmetric.width( KOGlobals::self()->calendarSystem()->weekDayName( i + 1 ) );
00943     if ( width > mWidthLongDayLabel ) mWidthLongDayLabel = width;
00944   }
00945 
00946   updateDayLabels();
00947 
00948   for ( uint i = 0; i < mCells.count(); ++i ) {
00949     mCells[i]->updateConfig();
00950   }
00951 
00952   showLabel( !KOPrefs::instance()->fullViewMonth() );
00953 }
00954 
00955 void KOMonthView::updateDayLabels()
00956 {
00957   kdDebug(5850) << "KOMonthView::updateDayLabels()" << endl;
00958 
00959   const KCalendarSystem*calsys=KOGlobals::self()->calendarSystem();
00960   int currDay;
00961   for ( int i = 0; i < 7; i++ ) {
00962     currDay = i+mWeekStartDay;
00963     if ( currDay > 7 ) currDay -= 7;
00964     mDayLabels[i]->setText( calsys->weekDayName( currDay, mShortDayLabels ) );
00965   }
00966 }
00967 
00968 void KOMonthView::showDates( const QDate &start, const QDate & )
00969 {
00970 //  kdDebug(5850) << "KOMonthView::showDates(): " << start.toString() << endl;
00971 
00972   const KCalendarSystem *calSys = KOGlobals::self()->calendarSystem();
00973 
00974   mDateToCell.clear();
00975 
00976   // show first day of month on top for readability issues
00977   mStartDate = start.addDays( -start.day() + 1 );
00978   // correct begin of week
00979   int weekdayCol=( mStartDate.dayOfWeek() + 7 - mWeekStartDay ) % 7;
00980   mStartDate = mStartDate.addDays( -weekdayCol );
00981 
00982   mLabel->setText( i18n( "monthname year", "%1 %2" )
00983                    .arg( calSys->monthName( start ) )
00984                    .arg( calSys->year( start ) ) );
00985 
00986   showLabel( !KOPrefs::instance()->fullViewMonth() );
00987 
00988   bool primary = false;
00989   uint i;
00990   for( i = 0; i < mCells.size(); ++i ) {
00991     QDate date = mStartDate.addDays( i );
00992     if ( calSys->day( date ) == 1 ) {
00993       primary = !primary;
00994     }
00995 
00996     mCells[i]->setDate( date );
00997     mDateToCell[ date ] = mCells[ i ];
00998     if( date == start ) {
00999       mCells[i]->select();
01000     }
01001 
01002     mCells[i]->setPrimary( primary );
01003 
01004     bool isHoliday = calSys->dayOfWeek( date ) == calSys->weekDayOfPray()
01005                   || !KOGlobals::self()->isWorkDay( date );
01006     mCells[i]->setHoliday( isHoliday );
01007 
01008     // add holiday, if present
01009     QStringList holidays( KOGlobals::self()->holiday( date ) );
01010     mCells[i]->setHolidayString( holidays.join( i18n("delimiter for joining holiday names", ", " ) ) );
01011   }
01012 
01013   updateView();
01014 }
01015 
01016 QDateTime KOMonthView::selectionStart()
01017 {
01018   if ( mSelectedCell) {
01019     return QDateTime( mSelectedCell->date() );
01020   } else {
01021     return QDateTime();
01022   }
01023 }
01024 
01025 QDateTime KOMonthView::selectionEnd()
01026 {
01027   // Only one cell can be selected (for now)
01028   return selectionStart();
01029 }
01030 
01031 void KOMonthView::showIncidences( const Incidence::List &, const QDate & )
01032 {
01033   kdDebug(5850) << "KOMonthView::showIncidences( const Incidence::List & ) is not implemented yet." << endl;
01034 }
01035 
01036 class KOMonthView::GetDateVisitor : public IncidenceBase::Visitor
01037 {
01038   public:
01039     GetDateVisitor() {}
01040 
01041     bool act( IncidenceBase *incidence )
01042     {
01043       return incidence->accept( *this );
01044     }
01045     QDateTime startDate() const { return mStartDate; }
01046     QDateTime endDate() const { return mEndDate; }
01047 
01048   protected:
01049     bool visit( Event *event ) {
01050       mStartDate = event->dtStart();
01051       mEndDate = event->dtEnd();
01052       return true;
01053     }
01054     bool visit( Todo *todo ) {
01055       if ( todo->hasDueDate() ) {
01056         if ( todo->dtDue().time() != QTime( 0, 0 ) &&
01057              todo->dtDue().time().isValid() ) {
01058           mStartDate = todo->dtDue();
01059           mEndDate = todo->dtDue();
01060         } else {
01061           mStartDate = QDateTime( todo->dtDue().date(), QTime( 23,59 ) );
01062           mEndDate = mStartDate;
01063         }
01064       }// else
01065 //         return false;
01066       return true;
01067     }
01068     bool visit( Journal *journal ) {
01069       mStartDate = journal->dtStart();
01070       mEndDate = journal->dtStart();
01071       return true;
01072     }
01073   protected:
01074     QDateTime mStartDate;
01075     QDateTime mEndDate;
01076 };
01077 
01078 void KOMonthView::changeIncidenceDisplayAdded( Incidence *incidence, MonthViewCell::CreateItemVisitor& v)
01079 {
01080   GetDateVisitor gdv;
01081 
01082   if ( !gdv.act( incidence ) ) {
01083     kdDebug(5850) << "Visiting GetDateVisitor failed." << endl;
01084     return;
01085   }
01086 
01087   bool floats = incidence->doesFloat();
01088 
01089   if ( incidence->doesRecur() ) {
01090     for ( uint i = 0; i < mCells.count(); ++i ) {
01091       if ( incidence->recursOn( mCells[i]->date() ) ) {
01092 
01093         // handle multiday events
01094         int length = gdv.startDate().daysTo( gdv.endDate().addSecs( floats ? 0 : -1 ).date() );
01095         for ( int j = 0; j <= length && i+j < mCells.count(); ++j ) {
01096           mCells[i+j]->addIncidence( incidence, v, j );
01097         }
01098       }
01099     }
01100   } else {
01101     // addSecs(-1) is added to handle 0:00 cases (because it's non-inclusive according to rfc)
01102     if ( gdv.endDate().isValid() ) {
01103       QDate endDate = gdv.endDate().addSecs( floats ? 0 : -1).date();
01104       for ( QDate date = gdv.startDate().date();
01105             date <= endDate; date = date.addDays( 1 ) ) {
01106         MonthViewCell *mvc = mDateToCell[ date ];
01107         if ( mvc ) mvc->addIncidence( incidence, v );
01108       }
01109     }
01110   }
01111 }
01112 
01113 void KOMonthView::changeIncidenceDisplay( Incidence *incidence, int action )
01114 {
01115   MonthViewCell::CreateItemVisitor v;
01116   switch ( action ) {
01117     case KOGlobals::INCIDENCEADDED:
01118       changeIncidenceDisplayAdded( incidence, v );
01119       break;
01120     case KOGlobals::INCIDENCEEDITED:
01121       for( uint i = 0; i < mCells.count(); i++ )
01122         mCells[i]->removeIncidence( incidence );
01123       changeIncidenceDisplayAdded( incidence, v );
01124       break;
01125     case KOGlobals::INCIDENCEDELETED:
01126       for( uint i = 0; i < mCells.count(); i++ )
01127         mCells[i]->removeIncidence( incidence );
01128       break;
01129     default:
01130       return;
01131   }
01132 }
01133 
01134 void KOMonthView::updateView()
01135 {
01136   for( uint i = 0; i < mCells.count(); ++i ) {
01137     mCells[i]->updateCell();
01138   }
01139 
01140   Incidence::List incidences = calendar()->incidences();
01141   Incidence::List::ConstIterator it;
01142 
01143   MonthViewCell::CreateItemVisitor v;
01144   for ( it = incidences.begin(); it != incidences.end(); ++it )
01145     changeIncidenceDisplayAdded( *it, v );
01146 
01147   processSelectionChange();
01148 }
01149 
01150 void KOMonthView::resizeEvent( QResizeEvent * )
01151 {
01152   // select the appropriate heading string size. E.g. "Wednesday" or "Wed".
01153   // note this only changes the text if the requested size crosses the
01154   // threshold between big enough to support the full name and not big
01155   // enough.
01156   if( mDayLabels[0]->width() < mWidthLongDayLabel ) {
01157     if ( !mShortDayLabels ) {
01158       mShortDayLabels = true;
01159       updateDayLabels();
01160     }
01161   } else {
01162     if ( mShortDayLabels ) {
01163       mShortDayLabels = false;
01164       updateDayLabels();
01165     }
01166   }
01167 }
01168 
01169 void KOMonthView::showEventContextMenu( Calendar *cal, Incidence *incidence, const QDate &qd )
01170 {
01171   mEventContextMenu->showIncidencePopup( cal, incidence, qd );
01172 }
01173 
01174 void KOMonthView::showGeneralContextMenu()
01175 {
01176   showNewEventPopup();
01177 }
01178 
01179 void KOMonthView::setSelectedCell( MonthViewCell *cell )
01180 {
01181   if ( mSelectedCell && cell != mSelectedCell )
01182     mSelectedCell->deselect();
01183 
01184   mSelectedCell = cell;
01185 
01186   if ( !mSelectedCell )
01187     emit incidenceSelected( 0, QDate() );
01188   else
01189     if ( selectedIncidenceDates().isEmpty() ) {
01190       emit incidenceSelected( mSelectedCell->selectedIncidence(), QDate() );
01191     } else {
01192       emit incidenceSelected( mSelectedCell->selectedIncidence(), selectedIncidenceDates().first() );
01193     }
01194 }
01195 
01196 void KOMonthView::processSelectionChange()
01197 {
01198   Incidence::List incidences = selectedIncidences();
01199   if (incidences.count() > 0) {
01200     if ( selectedIncidenceDates().isEmpty() ) {
01201       emit incidenceSelected( incidences.first(), QDate() );
01202     } else {
01203       emit incidenceSelected( incidences.first(), selectedIncidenceDates().first() );
01204     }
01205   } else {
01206     emit incidenceSelected( 0, QDate() );
01207   }
01208 }
01209 
01210 void KOMonthView::clearSelection()
01211 {
01212   if ( mSelectedCell ) {
01213     mSelectedCell->deselect();
01214     mSelectedCell = 0;
01215   }
01216 }
01217 
01218 void KOMonthView::showLabel( bool show )
01219 {
01220   if ( show ) {
01221     mLabel->show();
01222   } else {
01223     mLabel->hide();
01224   }
01225 }
KDE Home | KDE Accessibility Home | Description of Access Keys