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   mSpecialEvent( false ), mMultiItemInfo( 0 ), mStartMoveInfo( 0 )
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, mDate, 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   KABC::Addressee::List list;
00580   if ( KVCardDrag::decode( e, list ) ) {
00581     KABC::Addressee::List::Iterator it;
00582     for ( it = list.begin(); it != list.end(); ++it ) {
00583       QString em( (*it).fullEmail() );
00584       if ( em.isEmpty() ) {
00585         em = (*it).realName();
00586       }
00587       addAttendee( em );
00588     }
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 
00668   if ( mIncidence->type() == "Event" ) {
00669     QPixmap eventPxmp;
00670     if ( mIncidence->customProperty( "KABC", "BIRTHDAY" ) == "YES" ) {
00671       mSpecialEvent = true;
00672       if ( mIncidence->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
00673         eventPxmp = KOGlobals::self()->smallIcon( "calendaranniversary" );
00674       } else {
00675         eventPxmp = KOGlobals::self()->smallIcon( "calendarbirthday" );
00676       }
00677       conditionalPaint( p, true, x, ft, eventPxmp );
00678     }
00679     // per kolab/issue4349 we don't draw a regular appointment icon (to save space)
00680   }
00681 
00682 }
00683 
00684 void KOAgendaItem::paintTodoIcon( QPainter *p, int &x, int ft )
00685 {
00686   if ( !mIncidence ) return;
00687   static const QPixmap todoPxmp =
00688     KOGlobals::self()->smallIcon( "todo" );
00689   static const QPixmap completedPxmp =
00690     KOGlobals::self()->smallIcon( "checkedbox" );
00691   if ( mIncidence->type() != "Todo" )
00692     return;
00693   bool b = ( static_cast<Todo *>( mIncidence ) )->isCompleted();
00694   conditionalPaint( p, !b, x, ft, todoPxmp );
00695   conditionalPaint( p, b, x, ft, completedPxmp );
00696 }
00697 
00698 void KOAgendaItem::paintAlarmIcon( QPainter *p, int &x, int ft )
00699 {
00700   if (!mIconAlarm) return;
00701   int y = ft;
00702   // if we can't fit it all, bottom align it, more or less, so
00703   // it can be guessed better, visually
00704   if ( visibleRect().height() - ft < alarmPxmp->height() )
00705       y -= ( alarmPxmp->height() - visibleRect().height() - ft );
00706   p->drawPixmap( x, y, *alarmPxmp );
00707   x += alarmPxmp->width() + ft;
00708 }
00709 
00710 void KOAgendaItem::paintIcons( QPainter *p, int &x, int ft )
00711 {
00712   paintEventIcon( p, x, ft );
00713   paintTodoIcon( p, x, ft );
00714   if ( !mSpecialEvent ) {
00715     paintAlarmIcon( p, x, ft );
00716   }
00717   conditionalPaint( p, mIconRecur && !mSpecialEvent, x, ft, *recurPxmp );
00718   conditionalPaint( p, mIconReadonly && !mSpecialEvent, x, ft, *readonlyPxmp );
00719   conditionalPaint( p, mIconReply,          x, ft, *replyPxmp );
00720   conditionalPaint( p, mIconGroup,          x, ft, *groupPxmp );
00721   conditionalPaint( p, mIconGroupTentative, x, ft, *groupPxmpTentative );
00722   conditionalPaint( p, mIconOrganizer,      x, ft, *organizerPxmp );
00723 }
00724 
00725 void KOAgendaItem::paintEvent( QPaintEvent *ev )
00726 {
00727   //HACK
00728   // to reproduce a crash:
00729   // 1. start Kontact with the Calendar as the initial module
00730   // 2. immediately select the summary (which must include appt and to-do)
00731   // causes a crash for me every time in this method unless we make
00732   // the following check
00733   if ( !mIncidence )return;
00734 
00735   QRect visRect = visibleRect();
00736   // when scrolling horizontally in the side-by-side view, the repainted area is clipped
00737   // to the newly visible area, which is a problem since the content changes when visRect
00738   // changes, so repaint the full item in that case
00739   if ( ev->rect() != visRect && visRect.isValid() && ev->rect().isValid() ) {
00740     repaint( visRect );
00741     return;
00742   }
00743 
00744   QPainter p( this );
00745   const int ft = 2; // frame thickness for layout, see paintFrame()
00746   const int margin = 1 + ft; // frame + space between frame and content
00747 
00748   // General idea is to always show the icons (even in the all-day events).
00749   // This creates a consistent fealing for the user when the view mode
00750   // changes and therefore the available width changes.
00751   // Also look at #17984
00752 
00753   if ( !alarmPxmp ) {
00754     alarmPxmp          = new QPixmap( KOGlobals::self()->smallIcon("bell") );
00755     recurPxmp          = new QPixmap( KOGlobals::self()->smallIcon("recur") );
00756     readonlyPxmp       = new QPixmap( KOGlobals::self()->smallIcon("readonlyevent") );
00757     replyPxmp          = new QPixmap( KOGlobals::self()->smallIcon("mail_reply") );
00758     groupPxmp          = new QPixmap( KOGlobals::self()->smallIcon("groupevent") );
00759     groupPxmpTentative = new QPixmap( KOGlobals::self()->smallIcon("groupeventtentative") );
00760     organizerPxmp      = new QPixmap( KOGlobals::self()->smallIcon("organizer") );
00761   }
00762 
00763   QColor bgColor;
00764   if ( mIncidence->type() == "Todo" ) {
00765     if ( static_cast<Todo*>(mIncidence)->isOverdue() )
00766       bgColor = KOPrefs::instance()->todoOverdueColor();
00767     else if ( static_cast<Todo*>(mIncidence)->dtDue().date() ==
00768               QDateTime::currentDateTime().date() )
00769       bgColor = KOPrefs::instance()->todoDueTodayColor();
00770   }
00771 
00772   QColor categoryColor;
00773   QStringList categories = mIncidence->categories();
00774   QString cat = categories.first();
00775   if (cat.isEmpty())
00776     categoryColor = KOPrefs::instance()->unsetCategoryColor();
00777   else
00778     categoryColor = *(KOPrefs::instance()->categoryColor(cat));
00779 
00780   QColor resourceColor = mResourceColor;
00781   if ( !resourceColor.isValid() )
00782     resourceColor = categoryColor;
00783 
00784   QColor frameColor;
00785   if ( KOPrefs::instance()->agendaViewColors() == KOPrefs::ResourceOnly ||
00786        KOPrefs::instance()->agendaViewColors() == KOPrefs::CategoryInsideResourceOutside ) {
00787     frameColor = bgColor.isValid() ? bgColor : resourceColor;
00788   } else {
00789     frameColor = bgColor.isValid() ? bgColor : categoryColor;
00790   }
00791 
00792   if ( !bgColor.isValid() ) {
00793     if ( KOPrefs::instance()->agendaViewColors() == KOPrefs::ResourceOnly ||
00794          KOPrefs::instance()->agendaViewColors() == KOPrefs::ResourceInsideCategoryOutside ) {
00795       bgColor = resourceColor;
00796     } else {
00797       bgColor = categoryColor;
00798     }
00799   }
00800 
00801   if ( cat.isEmpty() &&
00802        KOPrefs::instance()->agendaViewColors() == KOPrefs::ResourceInsideCategoryOutside ) {
00803     frameColor = bgColor;
00804   }
00805 
00806   if ( cat.isEmpty() &&
00807        KOPrefs::instance()->agendaViewColors() == KOPrefs::CategoryInsideResourceOutside ) {
00808     bgColor = frameColor;
00809   }
00810 
00811   if ( mSelected ) {
00812     frameColor = QColor( 85 + frameColor.red() * 2/3,
00813                         85 + frameColor.green() * 2/3,
00814                         85 + frameColor.blue() * 2/3 );
00815   } else {
00816     frameColor = frameColor.dark( 115 );
00817   }
00818   QColor textColor = getTextColor(bgColor);
00819   p.setPen( textColor );
00820   p.setBackgroundColor( bgColor );
00821   p.setFont(KOPrefs::instance()->mAgendaViewFont);
00822   QFontMetrics fm = p.fontMetrics();
00823 
00824   int singleLineHeight = fm.boundingRect( mLabelText ).height();
00825 
00826   p.eraseRect( 0, 0, width(), height() );
00827   paintFrame( &p, frameColor );
00828 
00829   // calculate the height of the full version (case 4) to test whether it is
00830   // possible
00831 
00832   QString shortH;
00833   QString longH;
00834   if ( !isMultiItem() ) {
00835     shortH = KGlobal::locale()->formatTime(mIncidence->dtStart().time());
00836     if (mIncidence->type() != "Todo")
00837       longH = i18n("%1 - %2").arg(shortH)
00838                .arg(KGlobal::locale()->formatTime(mIncidence->dtEnd().time()));
00839     else
00840       longH = shortH;
00841   } else if ( !mMultiItemInfo->mFirstMultiItem ) {
00842     shortH = KGlobal::locale()->formatTime(mIncidence->dtStart().time());
00843     longH = shortH;
00844   } else {
00845     shortH = KGlobal::locale()->formatTime(mIncidence->dtEnd().time());
00846     longH = i18n("- %1").arg(shortH);
00847   }
00848 
00849   KWordWrap *ww = KWordWrap::formatText( fm,
00850                                          QRect(0, 0, width() - (2 * margin), -1),
00851                                          0,
00852                                          mLabelText );
00853   int th = ww->boundingRect().height();
00854   delete ww;
00855 
00856   int hlHeight = QMAX(fm.boundingRect(longH).height(),
00857      QMAX(alarmPxmp->height(), QMAX(recurPxmp->height(),
00858      QMAX(readonlyPxmp->height(), QMAX(replyPxmp->height(),
00859      QMAX(groupPxmp->height(), organizerPxmp->height()))))));
00860 
00861   bool completelyRenderable = th < (height() - 2 * ft - 2 - hlHeight);
00862 
00863   // case 1: do not draw text when not even a single line fits
00864   // Don't do this any more, always try to print out the text. Even if
00865   // it's just a few pixel, one can still guess the whole text from just four pixels' height!
00866   if ( //( singleLineHeight > height()-4 ) || // ignore margin, be gentle.. Even ignore 2 pixel outside the item
00867        ( width() < 16 ) ) {
00868     int x = margin;
00869     paintTodoIcon( &p, x, ft );
00870     return;
00871   }
00872 
00873   // case 2: draw a single line when no more space
00874   if ( (2 * singleLineHeight) > (height() - 2 * margin) ) {
00875     int x = margin, txtWidth;
00876 
00877     if ( mIncidence->doesFloat() ) {
00878       x += visRect.left();
00879       paintIcons( &p, x, ft );
00880       txtWidth = visRect.right() - margin - x;
00881     }
00882     else {
00883       paintIcons( &p, x, ft );
00884       txtWidth = width() - margin - x;
00885     }
00886 
00887     int y = ((height() - 2 * ft - singleLineHeight) / 2) + fm.ascent();
00888     KWordWrap::drawFadeoutText( &p, x, y,
00889                                 txtWidth, mLabelText );
00890     return;
00891   }
00892 
00893   // case 3: enough for 2-5 lines, but not for the header.
00894   //         Also used for the middle days in multi-events
00895   if ( ((!completelyRenderable) && ((height() - (2 * margin)) <= (5 * singleLineHeight)) ) ||
00896        (isMultiItem() && mMultiItemInfo->mNextMultiItem && mMultiItemInfo->mFirstMultiItem) ) {
00897     int x = margin, txtWidth;
00898 
00899     if ( mIncidence->doesFloat() ) {
00900       x += visRect.left();
00901       paintIcons( &p, x, ft );
00902       txtWidth = visRect.right() - margin - x;
00903     }
00904     else {
00905       paintIcons( &p, x, ft );
00906       txtWidth = width() - margin - x;
00907     }
00908 
00909     ww = KWordWrap::formatText( fm,
00910                                 QRect( 0, 0, txtWidth,
00911                                 (height() - (2 * margin)) ),
00912                                 0,
00913                                 mLabelText );
00914 
00915     //kdDebug() << "SIZES for " << mLabelText <<  ": " << width() << " :: " << txtWidth << endl;
00916     ww->drawText( &p, x, margin, Qt::AlignHCenter | KWordWrap::FadeOut );
00917     delete ww;
00918     return;
00919   }
00920 
00921   // case 4: paint everything, with header:
00922   // consists of (vertically) ft + headline&icons + ft + text + margin
00923   int y = 2 * ft + hlHeight;
00924   if ( completelyRenderable )
00925     y += (height() - (2 * ft) - margin - hlHeight - th) / 2;
00926 
00927   int x = margin, txtWidth, hTxtWidth, eventX;
00928 
00929   if ( mIncidence->doesFloat() ) {
00930     shortH = longH = "";
00931 
00932     if ( (mIncidence->type() != "Todo") &&
00933          (mIncidence->dtStart() != mIncidence->dtEnd()) ) { // multi days
00934       shortH = longH =
00935         i18n("%1 - %2")
00936              .arg(KGlobal::locale()->formatDate(mIncidence->dtStart().date()))
00937              .arg(KGlobal::locale()->formatDate(mIncidence->dtEnd().date()));
00938 
00939       // paint headline
00940       p.fillRect( 0, 0, width(), (ft/2) + margin + hlHeight,
00941                   QBrush( frameColor ) );
00942     }
00943 
00944     x += visRect.left();
00945     eventX = x;
00946     txtWidth = visRect.right() - margin - x;
00947     paintIcons( &p, x, ft );
00948     hTxtWidth = visRect.right() - margin - x;
00949   }
00950   else {
00951     // paint headline
00952     p.fillRect( 0, 0, width(), (ft/2) + margin + hlHeight,
00953                 QBrush( frameColor ) );
00954 
00955     txtWidth = width() - margin - x;
00956     eventX = x;
00957     paintIcons( &p, x, ft );
00958     hTxtWidth = width() - margin - x;
00959   }
00960 
00961   QString headline;
00962   int hw = fm.boundingRect( longH ).width();
00963   if ( hw > hTxtWidth ) {
00964     headline = shortH;
00965     hw = fm.boundingRect( shortH ).width();
00966     if ( hw < txtWidth )
00967       x += (hTxtWidth - hw) / 2;
00968   } else {
00969     headline = longH;
00970     x += (hTxtWidth - hw) / 2;
00971   }
00972   p.setBackgroundColor( frameColor );
00973   p.setPen( getTextColor( frameColor ) );
00974   KWordWrap::drawFadeoutText( &p, x, ft + fm.ascent(), hTxtWidth, headline );
00975 
00976   // draw event text
00977   ww = KWordWrap::formatText( fm,
00978                               QRect( 0, 0, txtWidth, height() - margin - y ),
00979                               0,
00980                               mLabelText );
00981 
00982   p.setBackgroundColor( bgColor );
00983   p.setPen( textColor );
00984   QString ws = ww->wrappedString();
00985   if ( ws.left( ws.length()-1 ).find( '\n' ) >= 0 )
00986     ww->drawText( &p, eventX, y,
00987                   Qt::AlignAuto | KWordWrap::FadeOut );
00988   else
00989     ww->drawText( &p, eventX + (txtWidth-ww->boundingRect().width()-2*margin)/2,
00990                   y, Qt::AlignHCenter | KWordWrap::FadeOut );
00991   delete ww;
00992 }
00993 
KDE Home | KDE Accessibility Home | Description of Access Keys