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