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