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