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