korganizer

koagendaitem.cpp

00001 /*
00002     This file is part of KOrganizer.
00003 
00004     Copyright (c) 2000,2001,2003 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 <qtooltip.h>
00027 #include <qdragobject.h>
00028 #include <qpainter.h>
00029 
00030 #include <kiconloader.h>
00031 #include <kdebug.h>
00032 #include <klocale.h>
00033 #include <kwordwrap.h>
00034 
00035 #include <libkcal/icaldrag.h>
00036 #include <libkcal/vcaldrag.h>
00037 #include <libkdepim/kvcarddrag.h>
00038 #ifndef KORG_NOKABC
00039 #include <kabc/addressee.h>
00040 #include <kabc/vcardconverter.h>
00041 #endif
00042 
00043 #include "koprefs.h"
00044 #include "koglobals.h"
00045 
00046 #include "koincidencetooltip.h"
00047 #include "koagendaitem.h"
00048 #include "koagendaitem.moc"
00049 
00050 //--------------------------------------------------------------------------
00051 
00052 QToolTipGroup *KOAgendaItem::mToolTipGroup = 0;
00053 
00054 QPixmap *KOAgendaItem::alarmPxmp = 0;
00055 QPixmap *KOAgendaItem::recurPxmp = 0;
00056 QPixmap *KOAgendaItem::readonlyPxmp = 0;
00057 QPixmap *KOAgendaItem::replyPxmp = 0;
00058 QPixmap *KOAgendaItem::groupPxmp = 0;
00059 QPixmap *KOAgendaItem::groupPxmpTentative = 0;
00060 QPixmap *KOAgendaItem::organizerPxmp = 0;
00061 
00062 //--------------------------------------------------------------------------
00063 
00064 KOAgendaItem::KOAgendaItem( Incidence *incidence, const QDate &qd, QWidget *parent,
00065                             const char *name, WFlags f ) :
00066   QWidget( parent, name, f ), mIncidence( incidence ), mDate( qd ),
00067   mLabelText( mIncidence->summary() ), mIconAlarm( false ),
00068   mIconRecur( false ), mIconReadonly( false ), mIconReply( false ),
00069   mIconGroup( false ), mIconGroupTentative( false ), mIconOrganizer( false ),
00070   mMultiItemInfo( 0 ), mStartMoveInfo( 0 )
00071 {
00072   setBackgroundMode( Qt::NoBackground );
00073 
00074   setCellXY( 0, 0, 1 );
00075   setCellXRight( 0 );
00076   setMouseTracking( true );
00077   mResourceColor = QColor();
00078   updateIcons();
00079 
00080   // select() does nothing, if state hasn't change, so preset mSelected.
00081   mSelected = true;
00082   select( false );
00083 
00084   KOIncidenceToolTip::add( this, incidence, toolTipGroup() );
00085   setAcceptDrops( true );
00086 }
00087 
00088 void KOAgendaItem::updateIcons()
00089 {
00090   if ( !mIncidence ) return;
00091   mIconReadonly = mIncidence->isReadOnly();
00092   mIconRecur = mIncidence->doesRecur();
00093   mIconAlarm = mIncidence->isAlarmEnabled();
00094   if ( mIncidence->attendeeCount() > 0 ) {
00095     if ( KOPrefs::instance()->thatIsMe( mIncidence->organizer().email() ) ) {
00096       mIconReply = false;
00097       mIconGroup = false;
00098       mIconGroupTentative = false;
00099       mIconOrganizer = true;
00100     } else {
00101       Attendee *me = mIncidence->attendeeByMails( KOPrefs::instance()->allEmails() );
00102       if ( me ) {
00103         if ( me->status() == Attendee::NeedsAction && me->RSVP() ) {
00104           mIconReply = true;
00105           mIconGroup = false;
00106           mIconGroupTentative = false;
00107           mIconOrganizer = false;
00108         } else if ( me->status() == Attendee::Tentative ) {
00109           mIconReply = false;
00110           mIconGroup = false;
00111           mIconGroupTentative = true;
00112           mIconOrganizer = false;
00113         } else {
00114           mIconReply = false;
00115           mIconGroup = true;
00116           mIconGroupTentative = false;
00117           mIconOrganizer = false;
00118         }
00119       } else {
00120         mIconReply = false;
00121         mIconGroup = true;
00122         mIconGroupTentative = false;
00123         mIconOrganizer = false;
00124       }
00125     }
00126   }
00127   update();
00128 }
00129 
00130 
00131 void KOAgendaItem::select( bool selected )
00132 {
00133   if ( mSelected == selected ) return;
00134   mSelected = selected;
00135 
00136   update();
00137 }
00138 
00139 bool KOAgendaItem::dissociateFromMultiItem()
00140 {
00141   if ( !isMultiItem() ) return false;
00142   KOAgendaItem *firstItem = firstMultiItem();
00143   if ( firstItem == this ) firstItem = nextMultiItem();
00144   KOAgendaItem *lastItem = lastMultiItem();
00145   if ( lastItem == this ) lastItem = prevMultiItem();
00146 
00147   KOAgendaItem *prevItem = prevMultiItem();
00148   KOAgendaItem *nextItem = nextMultiItem();
00149 
00150   if ( prevItem ) {
00151     prevItem->setMultiItem( firstItem,
00152                             prevItem->prevMultiItem(),
00153                             nextItem, lastItem );
00154   }
00155   if ( nextItem ) {
00156     nextItem->setMultiItem( firstItem, prevItem,
00157                             nextItem->prevMultiItem(),
00158                             lastItem );
00159   }
00160   delete mMultiItemInfo;
00161   return true;
00162 }
00163 
00164 bool KOAgendaItem::setIncidence( Incidence *i )
00165 {
00166   mIncidence = i;
00167   updateIcons();
00168   return true;
00169 }
00170 
00171 
00172 /*
00173   Return height of item in units of agenda cells
00174 */
00175 int KOAgendaItem::cellHeight() const
00176 {
00177   return mCellYBottom - mCellYTop + 1;
00178 }
00179 
00180 /*
00181   Return height of item in units of agenda cells
00182 */
00183 int KOAgendaItem::cellWidth() const
00184 {
00185   return mCellXRight - mCellXLeft + 1;
00186 }
00187 
00188 void KOAgendaItem::setItemDate( const QDate &qd )
00189 {
00190   mDate = qd;
00191 }
00192 
00193 void KOAgendaItem::setCellXY( int X, int YTop, int YBottom )
00194 {
00195   mCellXLeft = X;
00196   mCellYTop = YTop;
00197   mCellYBottom = YBottom;
00198 }
00199 
00200 void KOAgendaItem::setCellXRight( int xright )
00201 {
00202   mCellXRight = xright;
00203 }
00204 
00205 void KOAgendaItem::setCellX( int XLeft, int XRight )
00206 {
00207   mCellXLeft = XLeft;
00208   mCellXRight = XRight;
00209 }
00210 
00211 void KOAgendaItem::setCellY( int YTop, int YBottom )
00212 {
00213   mCellYTop = YTop;
00214   mCellYBottom = YBottom;
00215 }
00216 
00217 void KOAgendaItem::setMultiItem(KOAgendaItem *first, KOAgendaItem *prev,
00218                                 KOAgendaItem *next, KOAgendaItem *last)
00219 {
00220   if (!mMultiItemInfo) mMultiItemInfo=new MultiItemInfo;
00221   mMultiItemInfo->mFirstMultiItem = first;
00222   mMultiItemInfo->mPrevMultiItem = prev;
00223   mMultiItemInfo->mNextMultiItem = next;
00224   mMultiItemInfo->mLastMultiItem = last;
00225 }
00226 bool KOAgendaItem::isMultiItem()
00227 {
00228   return mMultiItemInfo;
00229 }
00230 KOAgendaItem* KOAgendaItem::prependMoveItem(KOAgendaItem* e)
00231 {
00232   if (!e) return e;
00233 
00234   KOAgendaItem*first=0, *last=0;
00235   if (isMultiItem()) {
00236     first=mMultiItemInfo->mFirstMultiItem;
00237     last=mMultiItemInfo->mLastMultiItem;
00238   }
00239   if (!first) first=this;
00240   if (!last) last=this;
00241 
00242   e->setMultiItem(0, 0, first, last);
00243   first->setMultiItem(e, e, first->nextMultiItem(), first->lastMultiItem() );
00244 
00245   KOAgendaItem*tmp=first->nextMultiItem();
00246   while (tmp) {
00247     tmp->setMultiItem( e, tmp->prevMultiItem(), tmp->nextMultiItem(), tmp->lastMultiItem() );
00248     tmp = tmp->nextMultiItem();
00249   }
00250 
00251   if ( mStartMoveInfo && !e->moveInfo() ) {
00252     e->mStartMoveInfo=new MultiItemInfo( *mStartMoveInfo );
00253 //    e->moveInfo()->mFirstMultiItem = moveInfo()->mFirstMultiItem;
00254 //    e->moveInfo()->mLastMultiItem = moveInfo()->mLastMultiItem;
00255     e->moveInfo()->mPrevMultiItem = 0;
00256     e->moveInfo()->mNextMultiItem = first;
00257   }
00258 
00259   if (first && first->moveInfo()) {
00260     first->moveInfo()->mPrevMultiItem = e;
00261   }
00262   return e;
00263 }
00264 
00265 KOAgendaItem* KOAgendaItem::appendMoveItem(KOAgendaItem* e)
00266 {
00267   if (!e) return e;
00268 
00269   KOAgendaItem*first=0, *last=0;
00270   if (isMultiItem()) {
00271     first=mMultiItemInfo->mFirstMultiItem;
00272     last=mMultiItemInfo->mLastMultiItem;
00273   }
00274   if (!first) first=this;
00275   if (!last) last=this;
00276 
00277   e->setMultiItem( first, last, 0, 0 );
00278   KOAgendaItem*tmp=first;
00279 
00280   while (tmp) {
00281     tmp->setMultiItem(tmp->firstMultiItem(), tmp->prevMultiItem(), tmp->nextMultiItem(), e);
00282     tmp = tmp->nextMultiItem();
00283   }
00284   last->setMultiItem( last->firstMultiItem(), last->prevMultiItem(), e, e);
00285 
00286   if ( mStartMoveInfo && !e->moveInfo() ) {
00287     e->mStartMoveInfo=new MultiItemInfo( *mStartMoveInfo );
00288 //    e->moveInfo()->mFirstMultiItem = moveInfo()->mFirstMultiItem;
00289 //    e->moveInfo()->mLastMultiItem = moveInfo()->mLastMultiItem;
00290     e->moveInfo()->mPrevMultiItem = last;
00291     e->moveInfo()->mNextMultiItem = 0;
00292   }
00293   if (last && last->moveInfo()) {
00294     last->moveInfo()->mNextMultiItem = e;
00295   }
00296   return e;
00297 }
00298 
00299 KOAgendaItem* KOAgendaItem::removeMoveItem(KOAgendaItem* e)
00300 {
00301   if (isMultiItem()) {
00302     KOAgendaItem *first = mMultiItemInfo->mFirstMultiItem;
00303     KOAgendaItem *next, *prev;
00304     KOAgendaItem *last = mMultiItemInfo->mLastMultiItem;
00305     if (!first) first = this;
00306     if (!last) last = this;
00307     if ( first==e ) {
00308       first = first->nextMultiItem();
00309       first->setMultiItem( 0, 0, first->nextMultiItem(), first->lastMultiItem() );
00310     }
00311     if ( last==e ) {
00312       last=last->prevMultiItem();
00313       last->setMultiItem( last->firstMultiItem(), last->prevMultiItem(), 0, 0 );
00314     }
00315 
00316     KOAgendaItem *tmp =  first;
00317     if ( first==last ) {
00318       delete mMultiItemInfo;
00319       tmp = 0;
00320       mMultiItemInfo = 0;
00321     }
00322     while ( tmp ) {
00323       next = tmp->nextMultiItem();
00324       prev = tmp->prevMultiItem();
00325       if ( e==next ) {
00326         next = next->nextMultiItem();
00327       }
00328       if ( e==prev ) {
00329         prev = prev->prevMultiItem();
00330       }
00331       tmp->setMultiItem((tmp==first)?0:first, (tmp==prev)?0:prev, (tmp==next)?0:next, (tmp==last)?0:last);
00332       tmp = tmp->nextMultiItem();
00333     }
00334   }
00335 
00336   return e;
00337 }
00338 
00339 
00340 void KOAgendaItem::startMove()
00341 {
00342   KOAgendaItem* first = this;
00343   if ( isMultiItem() && mMultiItemInfo->mFirstMultiItem ) {
00344     first=mMultiItemInfo->mFirstMultiItem;
00345   }
00346   first->startMovePrivate();
00347 }
00348 
00349 void KOAgendaItem::startMovePrivate()
00350 {
00351   mStartMoveInfo = new MultiItemInfo;
00352   mStartMoveInfo->mStartCellXLeft = mCellXLeft;
00353   mStartMoveInfo->mStartCellXRight = mCellXRight;
00354   mStartMoveInfo->mStartCellYTop = mCellYTop;
00355   mStartMoveInfo->mStartCellYBottom = mCellYBottom;
00356   if (mMultiItemInfo) {
00357     mStartMoveInfo->mFirstMultiItem = mMultiItemInfo->mFirstMultiItem;
00358     mStartMoveInfo->mLastMultiItem = mMultiItemInfo->mLastMultiItem;
00359     mStartMoveInfo->mPrevMultiItem = mMultiItemInfo->mPrevMultiItem;
00360     mStartMoveInfo->mNextMultiItem = mMultiItemInfo->mNextMultiItem;
00361   } else {
00362     mStartMoveInfo->mFirstMultiItem = 0;
00363     mStartMoveInfo->mLastMultiItem = 0;
00364     mStartMoveInfo->mPrevMultiItem = 0;
00365     mStartMoveInfo->mNextMultiItem = 0;
00366   }
00367   if ( isMultiItem() && mMultiItemInfo->mNextMultiItem )
00368   {
00369     mMultiItemInfo->mNextMultiItem->startMovePrivate();
00370   }
00371 }
00372 
00373 void KOAgendaItem::resetMove()
00374 {
00375   if ( mStartMoveInfo ) {
00376     if ( mStartMoveInfo->mFirstMultiItem ) {
00377       mStartMoveInfo->mFirstMultiItem->resetMovePrivate();
00378     } else {
00379       resetMovePrivate();
00380     }
00381   }
00382 }
00383 
00384 void KOAgendaItem::resetMovePrivate()
00385 {
00386   if (mStartMoveInfo) {
00387     mCellXLeft = mStartMoveInfo->mStartCellXLeft;
00388     mCellXRight = mStartMoveInfo->mStartCellXRight;
00389     mCellYTop = mStartMoveInfo->mStartCellYTop;
00390     mCellYBottom = mStartMoveInfo->mStartCellYBottom;
00391 
00392     // if we don't have mMultiItemInfo, the item didn't span two days before,
00393     // and wasn't moved over midnight, either, so we don't have to reset
00394     // anything. Otherwise, restore from mMoveItemInfo
00395     if ( mMultiItemInfo ) {
00396       // It was already a multi-day info
00397       mMultiItemInfo->mFirstMultiItem = mStartMoveInfo->mFirstMultiItem;
00398       mMultiItemInfo->mPrevMultiItem = mStartMoveInfo->mPrevMultiItem;
00399       mMultiItemInfo->mNextMultiItem = mStartMoveInfo->mNextMultiItem;
00400       mMultiItemInfo->mLastMultiItem = mStartMoveInfo->mLastMultiItem;
00401 
00402       if ( !mStartMoveInfo->mFirstMultiItem ) {
00403         // This was the first multi-item when the move started, delete all previous
00404         KOAgendaItem*toDel=mStartMoveInfo->mPrevMultiItem;
00405         KOAgendaItem*nowDel=0L;
00406         while (toDel) {
00407           nowDel=toDel;
00408           if (nowDel->moveInfo()) {
00409             toDel=nowDel->moveInfo()->mPrevMultiItem;
00410           }
00411           emit removeAgendaItem( nowDel );
00412         }
00413         mMultiItemInfo->mFirstMultiItem = 0L;
00414         mMultiItemInfo->mPrevMultiItem = 0L;
00415       }
00416       if ( !mStartMoveInfo->mLastMultiItem ) {
00417         // This was the last multi-item when the move started, delete all next
00418         KOAgendaItem*toDel=mStartMoveInfo->mNextMultiItem;
00419         KOAgendaItem*nowDel=0L;
00420         while (toDel) {
00421           nowDel=toDel;
00422           if (nowDel->moveInfo()) {
00423             toDel=nowDel->moveInfo()->mNextMultiItem;
00424           }
00425           emit removeAgendaItem( nowDel );
00426         }
00427         mMultiItemInfo->mLastMultiItem = 0L;
00428         mMultiItemInfo->mNextMultiItem = 0L;
00429       }
00430 
00431       if ( mStartMoveInfo->mFirstMultiItem==0 && mStartMoveInfo->mLastMultiItem==0 ) {
00432         // it was a single-day event before we started the move.
00433         delete mMultiItemInfo;
00434         mMultiItemInfo = 0;
00435       }
00436     }
00437     delete mStartMoveInfo;
00438     mStartMoveInfo = 0;
00439   }
00440   emit showAgendaItem( this );
00441   if ( nextMultiItem() ) {
00442     nextMultiItem()->resetMovePrivate();
00443   }
00444 }
00445 
00446 void KOAgendaItem::endMove()
00447 {
00448   KOAgendaItem*first=firstMultiItem();
00449   if (!first) first=this;
00450   first->endMovePrivate();
00451 }
00452 
00453 void KOAgendaItem::endMovePrivate()
00454 {
00455   if ( mStartMoveInfo ) {
00456     // if first, delete all previous
00457     if ( !firstMultiItem() || firstMultiItem()==this ) {
00458       KOAgendaItem*toDel=mStartMoveInfo->mPrevMultiItem;
00459       KOAgendaItem*nowDel = 0;
00460       while (toDel) {
00461         nowDel=toDel;
00462         if (nowDel->moveInfo()) {
00463           toDel=nowDel->moveInfo()->mPrevMultiItem;
00464         }
00465         emit removeAgendaItem( nowDel );
00466       }
00467     }
00468     // if last, delete all next
00469     if ( !lastMultiItem() || lastMultiItem()==this ) {
00470       KOAgendaItem*toDel=mStartMoveInfo->mNextMultiItem;
00471       KOAgendaItem*nowDel = 0;
00472       while (toDel) {
00473         nowDel=toDel;
00474         if (nowDel->moveInfo()) {
00475           toDel=nowDel->moveInfo()->mNextMultiItem;
00476         }
00477         emit removeAgendaItem( nowDel );
00478       }
00479     }
00480     // also delete the moving info
00481     delete mStartMoveInfo;
00482     mStartMoveInfo=0;
00483     if ( nextMultiItem() )
00484       nextMultiItem()->endMovePrivate();
00485   }
00486 }
00487 
00488 void KOAgendaItem::moveRelative(int dx, int dy)
00489 {
00490   int newXLeft = cellXLeft() + dx;
00491   int newXRight = cellXRight() + dx;
00492   int newYTop = cellYTop() + dy;
00493   int newYBottom = cellYBottom() + dy;
00494   setCellXY(newXLeft,newYTop,newYBottom);
00495   setCellXRight(newXRight);
00496 }
00497 
00498 void KOAgendaItem::expandTop(int dy)
00499 {
00500   int newYTop = cellYTop() + dy;
00501   int newYBottom = cellYBottom();
00502   if (newYTop > newYBottom) newYTop = newYBottom;
00503   setCellY(newYTop, newYBottom);
00504 }
00505 
00506 void KOAgendaItem::expandBottom(int dy)
00507 {
00508   int newYTop = cellYTop();
00509   int newYBottom = cellYBottom() + dy;
00510   if (newYBottom < newYTop) newYBottom = newYTop;
00511   setCellY(newYTop, newYBottom);
00512 }
00513 
00514 void KOAgendaItem::expandLeft(int dx)
00515 {
00516   int newXLeft = cellXLeft() + dx;
00517   int newXRight = cellXRight();
00518   if ( newXLeft > newXRight ) newXLeft = newXRight;
00519   setCellX( newXLeft, newXRight );
00520 }
00521 
00522 void KOAgendaItem::expandRight(int dx)
00523 {
00524   int newXLeft = cellXLeft();
00525   int newXRight = cellXRight() + dx;
00526   if ( newXRight < newXLeft ) newXRight = newXLeft;
00527   setCellX( newXLeft, newXRight );
00528 }
00529 
00530 QToolTipGroup *KOAgendaItem::toolTipGroup()
00531 {
00532   if (!mToolTipGroup) mToolTipGroup = new QToolTipGroup(0);
00533   return mToolTipGroup;
00534 }
00535 
00536 void KOAgendaItem::dragEnterEvent( QDragEnterEvent *e )
00537 {
00538 #ifndef KORG_NODND
00539   if ( ICalDrag::canDecode( e ) || VCalDrag::canDecode( e ) ) {
00540     e->ignore();
00541     return;
00542   }
00543   if ( KVCardDrag::canDecode( e ) || QTextDrag::canDecode( e ) )
00544     e->accept();
00545   else
00546     e->ignore();
00547 #endif
00548 }
00549 
00550 void KOAgendaItem::addAttendee( const QString &newAttendee )
00551 {
00552   kdDebug(5850) << " Email: " << newAttendee << endl;
00553   // TODO: Use proper email parsing instead of simply grepping for "<" and "@"
00554   int pos = newAttendee.find("<");
00555   QString name = newAttendee.left(pos);
00556   QString email = newAttendee.mid(pos);
00557   if (!email.isEmpty()) {
00558     mIncidence->addAttendee(new Attendee(name,email));
00559   } else if (name.contains("@")) {
00560     mIncidence->addAttendee(new Attendee(name,name));
00561   } else {
00562     mIncidence->addAttendee(new Attendee(name,QString::null));
00563   }
00564 }
00565 
00566 void KOAgendaItem::dropEvent( QDropEvent *e )
00567 {
00568 #ifndef KORG_NODND
00569   QString text;
00570 
00571   bool decoded = QTextDrag::decode( e, text );
00572   if( decoded && text.startsWith( "file:" ) ) {
00573     mIncidence->addAttachment( new Attachment( text ) );
00574     return;
00575   }
00576 
00577 #ifndef KORG_NOKABC
00578   QString vcards;
00579   KABC::VCardConverter converter;
00580 
00581   KVCardDrag::decode( e, vcards );
00582   KABC::Addressee::List list = converter.parseVCards( vcards );
00583   KABC::Addressee::List::Iterator it;
00584   for ( it = list.begin(); it != list.end(); ++it ) {
00585     QString em( (*it).fullEmail() );
00586     if (em.isEmpty()) {
00587       em=(*it).realName();
00588     }
00589     addAttendee( em );
00590   }
00591 #else
00592   if( decoded ) {
00593     kdDebug(5850) << "Dropped : " << text << endl;
00594 
00595     QStringList emails = QStringList::split( ",", text );
00596     for( QStringList::ConstIterator it = emails.begin(); it != emails.end();
00597         ++it ) {
00598         addAttendee( *it );
00599     }
00600   }
00601 #endif // KORG_NOKABC
00602 
00603 #endif // KORG_NODND
00604 }
00605 
00606 
00607 QPtrList<KOAgendaItem> KOAgendaItem::conflictItems()
00608 {
00609   return mConflictItems;
00610 }
00611 
00612 void KOAgendaItem::setConflictItems( QPtrList<KOAgendaItem> ci )
00613 {
00614   mConflictItems = ci;
00615   KOAgendaItem *item;
00616   for ( item = mConflictItems.first(); item != 0;
00617         item = mConflictItems.next() ) {
00618     item->addConflictItem( this );
00619   }
00620 }
00621 
00622 void KOAgendaItem::addConflictItem( KOAgendaItem *ci )
00623 {
00624   if ( mConflictItems.find( ci ) < 0 ) mConflictItems.append( ci );
00625 }
00626 
00627 QString KOAgendaItem::label() const
00628 {
00629   return mLabelText;
00630 }
00631 
00632 bool KOAgendaItem::overlaps( KOrg::CellItem *o ) const
00633 {
00634   KOAgendaItem *other = static_cast<KOAgendaItem *>( o );
00635 
00636   if ( cellXLeft() <= other->cellXRight() &&
00637        cellXRight() >= other->cellXLeft() ) {
00638     if ( ( cellYTop() <= other->cellYBottom() ) &&
00639          ( cellYBottom() >= other->cellYTop() ) ) {
00640       return true;
00641     }
00642   }
00643 
00644   return false;
00645 }
00646 
00647 void KOAgendaItem::paintFrame( QPainter *p, const QColor &color )
00648 {
00649   QColor oldpen(p->pen().color());
00650   p->setPen( color );
00651   p->drawRect( 0, 0, width(), height() );
00652   p->drawRect( 1, 1, width() - 2, height() - 2 );
00653   p->setPen( oldpen );
00654 }
00655 
00656 static void conditionalPaint( QPainter *p, bool cond, int &x, int ft,
00657                               const QPixmap &pxmp )
00658 {
00659   if ( !cond ) return;
00660 
00661   p->drawPixmap( x, ft, pxmp );
00662   x += pxmp.width() + ft;
00663 }
00664 
00665 void KOAgendaItem::paintEventIcon( QPainter *p, int &x, int ft )
00666 {
00667   if ( !mIncidence ) return;
00668   static const QPixmap eventPxmp =
00669     KOGlobals::self()->smallIcon( "appointment" );
00670   if ( mIncidence->type() != "Event" )
00671     return;
00672   conditionalPaint( p, true, x, ft, eventPxmp );
00673 }
00674 
00675 void KOAgendaItem::paintTodoIcon( QPainter *p, int &x, int ft )
00676 {
00677   if ( !mIncidence ) return;
00678   static const QPixmap todoPxmp =
00679     KOGlobals::self()->smallIcon( "todo" );
00680   static const QPixmap completedPxmp =
00681     KOGlobals::self()->smallIcon( "checkedbox" );
00682   if ( mIncidence->type() != "Todo" )
00683     return;
00684   bool b = ( static_cast<Todo *>( mIncidence ) )->isCompleted();
00685   conditionalPaint( p, !b, x, ft, todoPxmp );
00686   conditionalPaint( p, b, x, ft, completedPxmp );
00687 }
00688 
00689 void KOAgendaItem::paintIcons( QPainter *p, int &x, int ft )
00690 {
00691   paintEventIcon( p, x, ft );
00692   paintTodoIcon( p, x, ft );
00693   conditionalPaint( p, mIconAlarm,          x, ft, *alarmPxmp );
00694   conditionalPaint( p, mIconRecur,          x, ft, *recurPxmp );
00695   conditionalPaint( p, mIconReadonly,       x, ft, *readonlyPxmp );
00696   conditionalPaint( p, mIconReply,          x, ft, *replyPxmp );
00697   conditionalPaint( p, mIconGroup,          x, ft, *groupPxmp );
00698   conditionalPaint( p, mIconGroupTentative, x, ft, *groupPxmpTentative );
00699   conditionalPaint( p, mIconOrganizer,      x, ft, *organizerPxmp );
00700 }
00701 
00702 void KOAgendaItem::paintEvent( QPaintEvent * )
00703 {
00704   //HACK
00705   // to reproduce a crash:
00706   // 1. start Kontact with the Calendar as the initial module
00707   // 2. immediately select the summary (which must include appt and to-do)
00708   // causes a crash for me every time in this method unless we make
00709   // the following check
00710   if ( !mIncidence )return;
00711 
00712   QPainter p( this );
00713   const int ft = 2; // frame thickness for layout, see paintFrame()
00714   const int margin = 1 + ft; // frame + space between frame and content
00715 
00716   // General idea is to always show the icons (even in the all-day events).
00717   // This creates a consistent fealing for the user when the view mode
00718   // changes and therefore the available width changes.
00719   // Also look at #17984
00720 
00721   if ( !alarmPxmp ) {
00722     alarmPxmp          = new QPixmap( KOGlobals::self()->smallIcon("bell") );
00723     recurPxmp          = new QPixmap( KOGlobals::self()->smallIcon("recur") );
00724     readonlyPxmp       = new QPixmap( KOGlobals::self()->smallIcon("readonlyevent") );
00725     replyPxmp          = new QPixmap( KOGlobals::self()->smallIcon("mail_reply") );
00726     groupPxmp          = new QPixmap( KOGlobals::self()->smallIcon("groupevent") );
00727     groupPxmpTentative = new QPixmap( KOGlobals::self()->smallIcon("groupeventtentative") );
00728     organizerPxmp      = new QPixmap( KOGlobals::self()->smallIcon("organizer") );
00729   }
00730 
00731   QColor bgColor;
00732   if ( mIncidence->type() == "Todo" ) {
00733     if ( static_cast<Todo*>(mIncidence)->isOverdue() )
00734       bgColor = KOPrefs::instance()->todoOverdueColor();
00735     else if ( static_cast<Todo*>(mIncidence)->dtDue().date() ==
00736               QDateTime::currentDateTime().date() )
00737       bgColor = KOPrefs::instance()->todoDueTodayColor();
00738   }
00739 
00740   if ( !bgColor.isValid() ) {
00741     QStringList categories = mIncidence->categories();
00742     QString cat = categories.first();
00743     if (cat.isEmpty())
00744       bgColor = KOPrefs::instance()->mEventColor;
00745     else
00746       bgColor = *(KOPrefs::instance()->categoryColor(cat));
00747   }
00748   QColor frameColor;
00749   if ( KOPrefs::instance()->agendaViewUsesResourceColor()
00750     && mResourceColor.isValid() ) {
00751      frameColor = mSelected ? QColor( 85 + mResourceColor.red() * 2/3,
00752                                       85 + mResourceColor.green() * 2/3,
00753                                       85 + mResourceColor.blue() * 2/3 )
00754                                 : mResourceColor;
00755   } else {
00756     frameColor = mSelected ? QColor( 85 + bgColor.red() * 2/3,
00757                                      85 + bgColor.green() * 2/3,
00758                                      85 + bgColor.blue() * 2/3 )
00759                                 : bgColor.dark(115);
00760   }
00761   QColor textColor = getTextColor(bgColor);
00762   p.setPen( textColor );
00763   p.setBackgroundColor( bgColor );
00764   p.setFont(KOPrefs::instance()->mAgendaViewFont);
00765   QFontMetrics fm = p.fontMetrics();
00766 
00767   int singleLineHeight = fm.boundingRect( mLabelText ).height();
00768 
00769   p.eraseRect( 0, 0, width(), height() );
00770   paintFrame( &p, frameColor );
00771 
00772   // calculate the height of the full version (case 4) to test whether it is
00773   // possible
00774 
00775   QString shortH;
00776   QString longH;
00777   if ( !isMultiItem() ) {
00778     shortH = KGlobal::locale()->formatTime(mIncidence->dtStart().time());
00779     if (mIncidence->type() != "Todo")
00780       longH = i18n("%1 - %2").arg(shortH)
00781                .arg(KGlobal::locale()->formatTime(mIncidence->dtEnd().time()));
00782     else
00783       longH = shortH;
00784   } else if ( !mMultiItemInfo->mFirstMultiItem ) {
00785     shortH = KGlobal::locale()->formatTime(mIncidence->dtStart().time());
00786     longH = shortH;
00787   } else {
00788     shortH = KGlobal::locale()->formatTime(mIncidence->dtEnd().time());
00789     longH = i18n("- %1").arg(shortH);
00790   }
00791 
00792   KWordWrap *ww = KWordWrap::formatText( fm,
00793                                          QRect(0, 0, width() - (2 * margin), -1),
00794                                          0,
00795                                          mLabelText );
00796   int th = ww->boundingRect().height();
00797   delete ww;
00798 
00799   int hlHeight = QMAX(fm.boundingRect(longH).height(),
00800      QMAX(alarmPxmp->height(), QMAX(recurPxmp->height(),
00801      QMAX(readonlyPxmp->height(), QMAX(replyPxmp->height(),
00802      QMAX(groupPxmp->height(), organizerPxmp->height()))))));
00803 
00804   bool completelyRenderable = th < (height() - 2 * ft - 2 - hlHeight);
00805 
00806   // case 1: do not draw text when not even a single line fits
00807   // Don't do this any more, always try to print out the text. Even if
00808   // it's just a few pixel, one can still guess the whole text from just four pixels' height!
00809   if ( //( singleLineHeight > height()-4 ) || // ignore margin, be gentle.. Even ignore 2 pixel outside the item
00810        ( width() < 16 ) ) {
00811     int x = margin;
00812     paintTodoIcon( &p, x, ft );
00813     return;
00814   }
00815 
00816   // Used for multi-day events to make sure the summary is on screen
00817   QRect visRect=visibleRect();
00818 
00819   // case 2: draw a single line when no more space
00820   if ( (2 * singleLineHeight) > (height() - 2 * margin) ) {
00821     int x = margin, txtWidth;
00822 
00823     if ( mIncidence->doesFloat() ) {
00824       x += visRect.left();
00825       paintIcons( &p, x, ft );
00826       txtWidth = visRect.right() - margin - x;
00827     }
00828     else {
00829       paintIcons( &p, x, ft );
00830       txtWidth = width() - margin - x;
00831     }
00832 
00833     int y = ((height() - 2 * ft - singleLineHeight) / 2) + fm.ascent();
00834     KWordWrap::drawFadeoutText( &p, x, y,
00835                                 txtWidth, mLabelText );
00836     return;
00837   }
00838 
00839   // case 3: enough for 2-5 lines, but not for the header.
00840   //         Also used for the middle days in multi-events
00841   if ( ((!completelyRenderable) && ((height() - (2 * margin)) <= (5 * singleLineHeight)) ) ||
00842        (isMultiItem() && mMultiItemInfo->mNextMultiItem && mMultiItemInfo->mFirstMultiItem) ) {
00843     int x = margin, txtWidth;
00844 
00845     if ( mIncidence->doesFloat() ) {
00846       x += visRect.left();
00847       paintIcons( &p, x, ft );
00848       txtWidth = visRect.right() - margin - x;
00849     }
00850     else {
00851       paintIcons( &p, x, ft );
00852       txtWidth = width() - margin - x;
00853     }
00854 
00855     ww = KWordWrap::formatText( fm,
00856                                 QRect( 0, 0, txtWidth,
00857                                 (height() - (2 * margin)) ),
00858                                 0,
00859                                 mLabelText );
00860 
00861     //kdDebug() << "SIZES for " << mLabelText <<  ": " << width() << " :: " << txtWidth << endl;
00862     ww->drawText( &p, x, margin, Qt::AlignHCenter | KWordWrap::FadeOut );
00863     delete ww;
00864     return;
00865   }
00866 
00867   // case 4: paint everything, with header:
00868   // consists of (vertically) ft + headline&icons + ft + text + margin
00869   int y = 2 * ft + hlHeight;
00870   if ( completelyRenderable )
00871     y += (height() - (2 * ft) - margin - hlHeight - th) / 2;
00872 
00873   int x = margin, txtWidth, hTxtWidth, eventX;
00874 
00875   if ( mIncidence->doesFloat() ) {
00876     shortH = longH = "";
00877 
00878     if ( (mIncidence->type() != "Todo") &&
00879          (mIncidence->dtStart() != mIncidence->dtEnd()) ) { // multi days
00880       shortH = longH =
00881         i18n("%1 - %2")
00882              .arg(KGlobal::locale()->formatDate(mIncidence->dtStart().date()))
00883              .arg(KGlobal::locale()->formatDate(mIncidence->dtEnd().date()));
00884 
00885       // paint headline
00886       p.fillRect( 0, 0, width(), (ft/2) + margin + hlHeight,
00887                   QBrush( frameColor ) );
00888     }
00889 
00890     x += visRect.left();
00891     eventX = x;
00892     txtWidth = visRect.right() - margin - x;
00893     paintIcons( &p, x, ft );
00894     hTxtWidth = visRect.right() - margin - x;
00895   }
00896   else {
00897     // paint headline
00898     p.fillRect( 0, 0, width(), (ft/2) + margin + hlHeight,
00899                 QBrush( frameColor ) );
00900 
00901     txtWidth = width() - margin - x;
00902     eventX = x;
00903     paintIcons( &p, x, ft );
00904     hTxtWidth = width() - margin - x;
00905   }
00906 
00907   QString headline;
00908   int hw = fm.boundingRect( longH ).width();
00909   if ( hw > hTxtWidth ) {
00910     headline = shortH;
00911     hw = fm.boundingRect( shortH ).width();
00912     if ( hw < txtWidth )
00913       x += (hTxtWidth - hw) / 2;
00914   } else {
00915     headline = longH;
00916     x += (hTxtWidth - hw) / 2;
00917   }
00918   p.setBackgroundColor( frameColor );
00919   p.setPen( getTextColor( frameColor ) );
00920   KWordWrap::drawFadeoutText( &p, x, ft + fm.ascent(), hTxtWidth, headline );
00921 
00922   // draw event text
00923   ww = KWordWrap::formatText( fm,
00924                               QRect( 0, 0, txtWidth, height() - margin - y ),
00925                               0,
00926                               mLabelText );
00927 
00928   p.setBackgroundColor( bgColor );
00929   p.setPen( textColor );
00930   QString ws = ww->wrappedString();
00931   if ( ws.left( ws.length()-1 ).find( '\n' ) >= 0 )
00932     ww->drawText( &p, eventX, y,
00933                   Qt::AlignAuto | KWordWrap::FadeOut );
00934   else
00935     ww->drawText( &p, eventX + (txtWidth-ww->boundingRect().width()-2*margin)/2,
00936                   y, Qt::AlignHCenter | KWordWrap::FadeOut );
00937   delete ww;
00938 }
00939 
KDE Home | KDE Accessibility Home | Description of Access Keys