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