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