korganizer

komonthview.cpp

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