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 TQt, and distribute the resulting executable,
00023     without including the source code for TQt in the source distribution.
00024 */
00025 
00026 #include <tqtooltip.h>
00027 #include <tqdragobject.h>
00028 #include <tqpainter.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 TQToolTipGroup *KOAgendaItem::mToolTipGroup = 0;
00055 
00056 TQPixmap *KOAgendaItem::alarmPxmp = 0;
00057 TQPixmap *KOAgendaItem::recurPxmp = 0;
00058 TQPixmap *KOAgendaItem::readonlyPxmp = 0;
00059 TQPixmap *KOAgendaItem::replyPxmp = 0;
00060 TQPixmap *KOAgendaItem::groupPxmp = 0;
00061 TQPixmap *KOAgendaItem::groupPxmpTentative = 0;
00062 TQPixmap *KOAgendaItem::organizerPxmp = 0;
00063 
00064 //--------------------------------------------------------------------------
00065 
00066 KOAgendaItem::KOAgendaItem( Calendar *calendar, Incidence *incidence,
00067                             const TQDate &qd, TQWidget *parent,
00068                             int itemPos, int itemCount,
00069                             const char *name, WFlags f ) :
00070   TQWidget( 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( TQt::NoBackground );
00079 
00080   setCellXY( 0, 0, 1 );
00081   setCellXRight( 0 );
00082   setMouseTracking( true );
00083   mResourceColor = TQColor();
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 TQDate &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 *first, KOAgendaItem *prev,
00224                                  KOAgendaItem *next, KOAgendaItem *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* KOAgendaItem::prependMoveItem(KOAgendaItem* 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* KOAgendaItem::appendMoveItem(KOAgendaItem* 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* KOAgendaItem::removeMoveItem(KOAgendaItem* e)
00308 {
00309   if (isMultiItem()) {
00310     KOAgendaItem *first = mMultiItemInfo->mFirstMultiItem;
00311     KOAgendaItem *next, *prev;
00312     KOAgendaItem *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 *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*toDel=mStartMoveInfo->mPrevMultiItem;
00413         KOAgendaItem*nowDel=0L;
00414         while (toDel) {
00415           nowDel=toDel;
00416           if (nowDel->moveInfo()) {
00417             toDel=nowDel->moveInfo()->mPrevMultiItem;
00418           }
00419           emit removeAgendaItem( 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*toDel=mStartMoveInfo->mNextMultiItem;
00427         KOAgendaItem*nowDel=0L;
00428         while (toDel) {
00429           nowDel=toDel;
00430           if (nowDel->moveInfo()) {
00431             toDel=nowDel->moveInfo()->mNextMultiItem;
00432           }
00433           emit removeAgendaItem( nowDel );
00434         }
00435         mMultiItemInfo->mLastMultiItem = 0L;
00436         mMultiItemInfo->mNextMultiItem = 0L;
00437       }
00438 
00439       if ( mStartMoveInfo->mFirstMultiItem==0 && mStartMoveInfo->mLastMultiItem==0 ) {
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( 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()==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( nowDel );
00474       }
00475     }
00476     // if last, delete all next
00477     if ( !lastMultiItem() || lastMultiItem()==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( 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 TQToolTipGroup *KOAgendaItem::toolTipGroup()
00539 {
00540   if (!mToolTipGroup) mToolTipGroup = new TQToolTipGroup(0);
00541   return mToolTipGroup;
00542 }
00543 
00544 void KOAgendaItem::dragEnterEvent( TQDragEnterEvent *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 ) || TQTextDrag::canDecode( e ) )
00552     e->accept();
00553   else
00554     e->ignore();
00555 #endif
00556 }
00557 
00558 void KOAgendaItem::addAttendee( const TQString &newAttendee )
00559 {
00560   kdDebug(5850) << " Email: " << newAttendee << endl;
00561   TQString 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, TQString())).arg(text()), i18n("Attendee added"), "AttendeeDroppedAdded" );
00566   }
00567 
00568 }
00569 
00570 void KOAgendaItem::dropEvent( TQDropEvent *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   TQString text;
00575 
00576   bool decoded = TQTextDrag::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       TQString 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     TQStringList emails = TQStringList::split( ",", text );
00599     for( TQStringList::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 TQPtrList<KOAgendaItem> KOAgendaItem::conflictItems()
00611 {
00612   return mConflictItems;
00613 }
00614 
00615 void KOAgendaItem::setConflictItems( TQPtrList<KOAgendaItem> ci )
00616 {
00617   mConflictItems = ci;
00618   KOAgendaItem *item;
00619   for ( item = mConflictItems.first(); item != 0;
00620         item = mConflictItems.next() ) {
00621     item->addConflictItem( this );
00622   }
00623 }
00624 
00625 void KOAgendaItem::addConflictItem( KOAgendaItem *ci )
00626 {
00627   if ( mConflictItems.find( ci ) < 0 ) mConflictItems.append( ci );
00628 }
00629 
00630 TQString KOAgendaItem::label() const
00631 {
00632   return mLabelText;
00633 }
00634 
00635 bool KOAgendaItem::overlaps( KOrg::CellItem *o ) const
00636 {
00637   KOAgendaItem *other = static_cast<KOAgendaItem *>( o );
00638 
00639   if ( cellXLeft() <= other->cellXRight() &&
00640        cellXRight() >= other->cellXLeft() ) {
00641     if ( ( cellYTop() <= other->cellYBottom() ) &&
00642          ( cellYBottom() >= other->cellYTop() ) ) {
00643       return true;
00644     }
00645   }
00646 
00647   return false;
00648 }
00649 
00650 void KOAgendaItem::paintFrame( TQPainter *p, const TQColor &color )
00651 {
00652   TQColor oldpen(p->pen().color());
00653   p->setPen( color );
00654   p->drawRect( 0, 0, width(), height() );
00655   p->drawRect( 1, 1, width() - 2, height() - 2 );
00656   p->setPen( oldpen );
00657 }
00658 
00659 static void conditionalPaint( TQPainter *p, bool cond, int &x, int ft,
00660                               const TQPixmap &pxmp )
00661 {
00662   if ( !cond ) return;
00663 
00664   p->drawPixmap( x, ft, pxmp );
00665   x += pxmp.width() + ft;
00666 }
00667 
00668 void KOAgendaItem::paintEventIcon( TQPainter *p, int &x, int ft )
00669 {
00670   if ( !mIncidence ) return;
00671 
00672   if ( mIncidence->type() == "Event" ) {
00673     TQPixmap eventPxmp;
00674     if ( mIncidence->customProperty( "KABC", "BIRTHDAY" ) == "YES" ) {
00675       mSpecialEvent = true;
00676       if ( mIncidence->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
00677         eventPxmp = KOGlobals::self()->smallIcon( "calendaranniversary" );
00678       } else {
00679         eventPxmp = KOGlobals::self()->smallIcon( "calendarbirthday" );
00680       }
00681       conditionalPaint( p, true, x, ft, eventPxmp );
00682     }
00683     // per kolab/issue4349 we don't draw a regular appointment icon (to save space)
00684   }
00685 
00686 }
00687 
00688 void KOAgendaItem::paintTodoIcon( TQPainter *p, int &x, int ft )
00689 {
00690   if ( !mIncidence ) return;
00691   static const TQPixmap todoPxmp =
00692     KOGlobals::self()->smallIcon( "todo" );
00693   static const TQPixmap completedPxmp =
00694     KOGlobals::self()->smallIcon( "checkedbox" );
00695   if ( mIncidence->type() != "Todo" )
00696     return;
00697   bool b = ( static_cast<Todo *>( mIncidence ) )->isCompleted();
00698   conditionalPaint( p, !b, x, ft, todoPxmp );
00699   conditionalPaint( p, b, x, ft, completedPxmp );
00700 }
00701 
00702 void KOAgendaItem::paintAlarmIcon( TQPainter *p, int &x, int ft )
00703 {
00704   if (!mIconAlarm) return;
00705   int y = ft;
00706   // if we can't fit it all, bottom align it, more or less, so
00707   // it can be guessed better, visually
00708   if ( visibleRect().height() - ft < alarmPxmp->height() )
00709       y -= ( alarmPxmp->height() - visibleRect().height() - ft );
00710   p->drawPixmap( x, y, *alarmPxmp );
00711   x += alarmPxmp->width() + ft;
00712 }
00713 
00714 void KOAgendaItem::paintIcons( TQPainter *p, int &x, int ft )
00715 {
00716   paintEventIcon( p, x, ft );
00717   paintTodoIcon( p, x, ft );
00718   if ( !mSpecialEvent ) {
00719     paintAlarmIcon( p, x, ft );
00720   }
00721   conditionalPaint( p, mIconRecur && !mSpecialEvent, x, ft, *recurPxmp );
00722   conditionalPaint( p, mIconReadonly && !mSpecialEvent, x, ft, *readonlyPxmp );
00723   conditionalPaint( p, mIconReply,          x, ft, *replyPxmp );
00724   conditionalPaint( p, mIconGroup,          x, ft, *groupPxmp );
00725   conditionalPaint( p, mIconGroupTentative, x, ft, *groupPxmpTentative );
00726   conditionalPaint( p, mIconOrganizer,      x, ft, *organizerPxmp );
00727 }
00728 
00729 void KOAgendaItem::paintEvent( TQPaintEvent *ev )
00730 {
00731   //HACK
00732   // to reproduce a crash:
00733   // 1. start Kontact with the Calendar as the initial module
00734   // 2. immediately select the summary (which must include appt and to-do)
00735   // causes a crash for me every time in this method unless we make
00736   // the following check
00737   if ( !mIncidence )return;
00738 
00739   TQRect visRect = visibleRect();
00740   // when scrolling horizontally in the side-by-side view, the repainted area is clipped
00741   // to the newly visible area, which is a problem since the content changes when visRect
00742   // changes, so repaint the full item in that case
00743   if ( ev->rect() != visRect && visRect.isValid() && ev->rect().isValid() ) {
00744     repaint( visRect );
00745     return;
00746   }
00747 
00748   TQPainter p( this );
00749   const int ft = 2; // frame thickness for layout, see paintFrame()
00750   const int margin = 1 + ft; // frame + space between frame and content
00751 
00752   // General idea is to always show the icons (even in the all-day events).
00753   // This creates a consistent fealing for the user when the view mode
00754   // changes and therefore the available width changes.
00755   // Also look at #17984
00756 
00757   if ( !alarmPxmp ) {
00758     alarmPxmp          = new TQPixmap( KOGlobals::self()->smallIcon("bell") );
00759     recurPxmp          = new TQPixmap( KOGlobals::self()->smallIcon("recur") );
00760     readonlyPxmp       = new TQPixmap( KOGlobals::self()->smallIcon("readonlyevent") );
00761     replyPxmp          = new TQPixmap( KOGlobals::self()->smallIcon("mail_reply") );
00762     groupPxmp          = new TQPixmap( KOGlobals::self()->smallIcon("groupevent") );
00763     groupPxmpTentative = new TQPixmap( KOGlobals::self()->smallIcon("groupeventtentative") );
00764     organizerPxmp      = new TQPixmap( KOGlobals::self()->smallIcon("organizer") );
00765   }
00766 
00767   TQColor bgColor;
00768   if ( mIncidence->type() == "Todo" ) {
00769     if ( static_cast<Todo*>(mIncidence)->isOverdue() )
00770       bgColor = KOPrefs::instance()->todoOverdueColor();
00771     else if ( static_cast<Todo*>(mIncidence)->dtDue().date() ==
00772               TQDateTime::currentDateTime().date() )
00773       bgColor = KOPrefs::instance()->todoDueTodayColor();
00774   }
00775 
00776   TQColor categoryColor;
00777   TQStringList categories = mIncidence->categories();
00778   TQString cat = categories.first();
00779   if (cat.isEmpty())
00780     categoryColor = KOPrefs::instance()->unsetCategoryColor();
00781   else
00782     categoryColor = *(KOPrefs::instance()->categoryColor(cat));
00783 
00784   TQColor resourceColor = mResourceColor;
00785   if ( !resourceColor.isValid() )
00786     resourceColor = categoryColor;
00787 
00788   TQColor frameColor;
00789   if ( KOPrefs::instance()->agendaViewColors() == KOPrefs::ResourceOnly ||
00790        KOPrefs::instance()->agendaViewColors() == KOPrefs::CategoryInsideResourceOutside ) {
00791     frameColor = bgColor.isValid() ? bgColor : resourceColor;
00792   } else {
00793     frameColor = bgColor.isValid() ? bgColor : categoryColor;
00794   }
00795 
00796   if ( !bgColor.isValid() ) {
00797     if ( KOPrefs::instance()->agendaViewColors() == KOPrefs::ResourceOnly ||
00798          KOPrefs::instance()->agendaViewColors() == KOPrefs::ResourceInsideCategoryOutside ) {
00799       bgColor = resourceColor;
00800     } else {
00801       bgColor = categoryColor;
00802     }
00803   }
00804 
00805   if ( cat.isEmpty() &&
00806        KOPrefs::instance()->agendaViewColors() == KOPrefs::ResourceInsideCategoryOutside ) {
00807     frameColor = bgColor;
00808   }
00809 
00810   if ( cat.isEmpty() &&
00811        KOPrefs::instance()->agendaViewColors() == KOPrefs::CategoryInsideResourceOutside ) {
00812     bgColor = frameColor;
00813   }
00814 
00815   if ( mSelected ) {
00816     frameColor = TQColor( 85 + frameColor.red() * 2/3,
00817                         85 + frameColor.green() * 2/3,
00818                         85 + frameColor.blue() * 2/3 );
00819   } else {
00820     frameColor = frameColor.dark( 115 );
00821   }
00822   TQColor textColor = getTextColor(bgColor);
00823   p.setPen( textColor );
00824   p.setBackgroundColor( bgColor );
00825   p.setFont(KOPrefs::instance()->mAgendaViewFont);
00826   TQFontMetrics fm = p.fontMetrics();
00827 
00828   int singleLineHeight = fm.boundingRect( mLabelText ).height();
00829 
00830   p.eraseRect( 0, 0, width(), height() );
00831   paintFrame( &p, frameColor );
00832 
00833   // calculate the height of the full version (case 4) to test whether it is
00834   // possible
00835 
00836   TQString shortH;
00837   TQString longH;
00838   if ( !isMultiItem() ) {
00839     shortH = KGlobal::locale()->formatTime(mIncidence->dtStart().time());
00840     if (mIncidence->type() != "Todo")
00841       longH = i18n("%1 - %2").arg(shortH)
00842                .arg(KGlobal::locale()->formatTime(mIncidence->dtEnd().time()));
00843     else
00844       longH = shortH;
00845   } else if ( !mMultiItemInfo->mFirstMultiItem ) {
00846     shortH = KGlobal::locale()->formatTime(mIncidence->dtStart().time());
00847     longH = shortH;
00848   } else {
00849     shortH = KGlobal::locale()->formatTime(mIncidence->dtEnd().time());
00850     longH = i18n("- %1").arg(shortH);
00851   }
00852 
00853   KWordWrap *ww = KWordWrap::formatText( fm,
00854                                          TQRect(0, 0, width() - (2 * margin), -1),
00855                                          0,
00856                                          mLabelText );
00857   int th = ww->boundingRect().height();
00858   delete ww;
00859 
00860   int hlHeight = TQMAX(fm.boundingRect(longH).height(),
00861      TQMAX(alarmPxmp->height(), TQMAX(recurPxmp->height(),
00862      TQMAX(readonlyPxmp->height(), TQMAX(replyPxmp->height(),
00863      TQMAX(groupPxmp->height(), organizerPxmp->height()))))));
00864 
00865   bool completelyRenderable = th < (height() - 2 * ft - 2 - hlHeight);
00866 
00867   // case 1: do not draw text when not even a single line fits
00868   // Don't do this any more, always try to print out the text. Even if
00869   // it's just a few pixel, one can still guess the whole text from just four pixels' height!
00870   if ( //( singleLineHeight > height()-4 ) || // ignore margin, be gentle.. Even ignore 2 pixel outside the item
00871        ( width() < 16 ) ) {
00872     int x = margin;
00873     paintTodoIcon( &p, x, ft );
00874     return;
00875   }
00876 
00877   // case 2: draw a single line when no more space
00878   if ( (2 * singleLineHeight) > (height() - 2 * margin) ) {
00879     int x = margin, txtWidth;
00880 
00881     if ( mIncidence->doesFloat() ) {
00882       x += visRect.left();
00883       paintIcons( &p, x, ft );
00884       txtWidth = visRect.right() - margin - x;
00885     }
00886     else {
00887       paintIcons( &p, x, ft );
00888       txtWidth = width() - margin - x;
00889     }
00890 
00891     int y = ((height() - 2 * ft - singleLineHeight) / 2) + fm.ascent();
00892     KWordWrap::drawFadeoutText( &p, x, y,
00893                                 txtWidth, mLabelText );
00894     return;
00895   }
00896 
00897   // case 3: enough for 2-5 lines, but not for the header.
00898   //         Also used for the middle days in multi-events
00899   if ( ((!completelyRenderable) && ((height() - (2 * margin)) <= (5 * singleLineHeight)) ) ||
00900        (isMultiItem() && mMultiItemInfo->mNextMultiItem && mMultiItemInfo->mFirstMultiItem) ) {
00901     int x = margin, txtWidth;
00902 
00903     if ( mIncidence->doesFloat() ) {
00904       x += visRect.left();
00905       paintIcons( &p, x, ft );
00906       txtWidth = visRect.right() - margin - x;
00907     }
00908     else {
00909       paintIcons( &p, x, ft );
00910       txtWidth = width() - margin - x;
00911     }
00912 
00913     ww = KWordWrap::formatText( fm,
00914                                 TQRect( 0, 0, txtWidth,
00915                                 (height() - (2 * margin)) ),
00916                                 0,
00917                                 mLabelText );
00918 
00919     //kdDebug() << "SIZES for " << mLabelText <<  ": " << width() << " :: " << txtWidth << endl;
00920     ww->drawText( &p, x, margin, TQt::AlignHCenter | KWordWrap::FadeOut );
00921     delete ww;
00922     return;
00923   }
00924 
00925   // case 4: paint everything, with header:
00926   // consists of (vertically) ft + headline&icons + ft + text + margin
00927   int y = 2 * ft + hlHeight;
00928   if ( completelyRenderable )
00929     y += (height() - (2 * ft) - margin - hlHeight - th) / 2;
00930 
00931   int x = margin, txtWidth, hTxtWidth, eventX;
00932 
00933   if ( mIncidence->doesFloat() ) {
00934     shortH = longH = "";
00935 
00936     if ( (mIncidence->type() != "Todo") &&
00937          (mIncidence->dtStart() != mIncidence->dtEnd()) ) { // multi days
00938       shortH = longH =
00939         i18n("%1 - %2")
00940              .arg(KGlobal::locale()->formatDate(mIncidence->dtStart().date()))
00941              .arg(KGlobal::locale()->formatDate(mIncidence->dtEnd().date()));
00942 
00943       // paint headline
00944       p.fillRect( 0, 0, width(), (ft/2) + margin + hlHeight,
00945                   TQBrush( frameColor ) );
00946     }
00947 
00948     x += visRect.left();
00949     eventX = x;
00950     txtWidth = visRect.right() - margin - x;
00951     paintIcons( &p, x, ft );
00952     hTxtWidth = visRect.right() - margin - x;
00953   }
00954   else {
00955     // paint headline
00956     p.fillRect( 0, 0, width(), (ft/2) + margin + hlHeight,
00957                 TQBrush( frameColor ) );
00958 
00959     txtWidth = width() - margin - x;
00960     eventX = x;
00961     paintIcons( &p, x, ft );
00962     hTxtWidth = width() - margin - x;
00963   }
00964 
00965   TQString headline;
00966   int hw = fm.boundingRect( longH ).width();
00967   if ( hw > hTxtWidth ) {
00968     headline = shortH;
00969     hw = fm.boundingRect( shortH ).width();
00970     if ( hw < txtWidth )
00971       x += (hTxtWidth - hw) / 2;
00972   } else {
00973     headline = longH;
00974     x += (hTxtWidth - hw) / 2;
00975   }
00976   p.setBackgroundColor( frameColor );
00977   p.setPen( getTextColor( frameColor ) );
00978   KWordWrap::drawFadeoutText( &p, x, ft + fm.ascent(), hTxtWidth, headline );
00979 
00980   // draw event text
00981   ww = KWordWrap::formatText( fm,
00982                               TQRect( 0, 0, txtWidth, height() - margin - y ),
00983                               0,
00984                               mLabelText );
00985 
00986   p.setBackgroundColor( bgColor );
00987   p.setPen( textColor );
00988   TQString ws = ww->wrappedString();
00989   if ( ws.left( ws.length()-1 ).find( '\n' ) >= 0 )
00990     ww->drawText( &p, eventX, y,
00991                   TQt::AlignAuto | KWordWrap::FadeOut );
00992   else
00993     ww->drawText( &p, eventX + (txtWidth-ww->boundingRect().width()-2*margin)/2,
00994                   y, TQt::AlignHCenter | KWordWrap::FadeOut );
00995   delete ww;
00996 }
00997