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