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