korganizer

koagendaitem.cpp
1 /*
2  This file is part of KOrganizer.
3 
4  Copyright (c) 2000,2001,2003 Cornelius Schumacher <schumacher@kde.org>
5  Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 
21  As a special exception, permission is given to link this program
22  with any edition of TQt, and distribute the resulting executable,
23  without including the source code for TQt in the source distribution.
24 */
25 
26 #include <tqtooltip.h>
27 #include <tqdragobject.h>
28 #include <tqpainter.h>
29 
30 #include <kiconloader.h>
31 #include <kdebug.h>
32 #include <tdelocale.h>
33 #include <kwordwrap.h>
34 #include <tdemessagebox.h>
35 
36 #include <libkcal/icaldrag.h>
37 #include <libkcal/vcaldrag.h>
38 #include <libtdepim/kvcarddrag.h>
39 #include <libemailfunctions/email.h>
40 #ifndef KORG_NOKABC
41 #include <tdeabc/addressee.h>
42 #include <tdeabc/vcardconverter.h>
43 #endif
44 
45 #include "koprefs.h"
46 #include "koglobals.h"
47 
48 #include "koincidencetooltip.h"
49 #include "koagendaitem.h"
50 #include "koagendaitem.moc"
51 
52 //--------------------------------------------------------------------------
53 
54 TQToolTipGroup *KOAgendaItem::mToolTipGroup = 0;
55 
56 TQPixmap *KOAgendaItem::alarmPxmp = 0;
57 TQPixmap *KOAgendaItem::recurPxmp = 0;
58 TQPixmap *KOAgendaItem::readonlyPxmp = 0;
59 TQPixmap *KOAgendaItem::replyPxmp = 0;
60 TQPixmap *KOAgendaItem::groupPxmp = 0;
61 TQPixmap *KOAgendaItem::groupPxmpTentative = 0;
62 TQPixmap *KOAgendaItem::organizerPxmp = 0;
63 
64 //--------------------------------------------------------------------------
65 
66 KOAgendaItem::KOAgendaItem( Calendar *calendar, Incidence *incidence,
67  const TQDate &qd, TQWidget *parent,
68  int itemPos, int itemCount,
69  const char *name, WFlags f ) :
70  TQWidget( parent, name, f ), mCalendar( calendar ), mIncidence( incidence ), mDate( qd ),
71  mLabelText( mIncidence->summary() ), mIconAlarm( false ),
72  mIconRecur( false ), mIconReadonly( false ), mIconReply( false ),
73  mIconGroup( false ), mIconGroupTentative( false ), mIconOrganizer( false ),
74  mSpecialEvent( false ),
75  mItemPos( itemPos ), mItemCount( itemCount ),
76  mMultiItemInfo( 0 ), mStartMoveInfo( 0 )
77 {
78  setBackgroundMode( TQt::NoBackground );
79 
80  setCellXY( 0, 0, 1 );
81  setCellXRight( 0 );
82  setMouseTracking( true );
83  mResourceColor = TQColor();
84  updateIcons();
85 
86  // select() does nothing, if state hasn't change, so preset mSelected.
87  mSelected = true;
88  select( false );
89 
90  KOIncidenceToolTip::add( this, mCalendar, incidence, mDate, toolTipGroup() );
91  setAcceptDrops( true );
92 }
93 
94 void KOAgendaItem::updateIcons()
95 {
96  if ( !mIncidence ) return;
97  mIconReadonly = mIncidence->isReadOnly();
98  mIconRecur = mIncidence->doesRecur();
99  mIconAlarm = mIncidence->isAlarmEnabled();
100  if ( mIncidence->attendeeCount() > 1 ) {
101  if ( KOPrefs::instance()->thatIsMe( mIncidence->organizer().email() ) ) {
102  mIconReply = false;
103  mIconGroup = false;
104  mIconGroupTentative = false;
105  mIconOrganizer = true;
106  } else {
107  Attendee *me = mIncidence->attendeeByMails( KOPrefs::instance()->allEmails() );
108  if ( me ) {
109  if ( me->status() == Attendee::NeedsAction && me->RSVP() ) {
110  mIconReply = true;
111  mIconGroup = false;
112  mIconGroupTentative = false;
113  mIconOrganizer = false;
114  } else if ( me->status() == Attendee::Tentative ) {
115  mIconReply = false;
116  mIconGroup = false;
117  mIconGroupTentative = true;
118  mIconOrganizer = false;
119  } else {
120  mIconReply = false;
121  mIconGroup = true;
122  mIconGroupTentative = false;
123  mIconOrganizer = false;
124  }
125  } else {
126  mIconReply = false;
127  mIconGroup = true;
128  mIconGroupTentative = false;
129  mIconOrganizer = false;
130  }
131  }
132  }
133  update();
134 }
135 
136 
137 void KOAgendaItem::select( bool selected )
138 {
139  if ( mSelected == selected ) return;
140  mSelected = selected;
141 
142  update();
143 }
144 
145 bool KOAgendaItem::dissociateFromMultiItem()
146 {
147  if ( !isMultiItem() ) return false;
148  KOAgendaItem *firstItem = firstMultiItem();
149  if ( firstItem == this ) firstItem = nextMultiItem();
150  KOAgendaItem *lastItem = lastMultiItem();
151  if ( lastItem == this ) lastItem = prevMultiItem();
152 
153  KOAgendaItem *prevItem = prevMultiItem();
154  KOAgendaItem *nextItem = nextMultiItem();
155 
156  if ( prevItem ) {
157  prevItem->setMultiItem( firstItem,
158  prevItem->prevMultiItem(),
159  nextItem, lastItem );
160  }
161  if ( nextItem ) {
162  nextItem->setMultiItem( firstItem, prevItem,
163  nextItem->prevMultiItem(),
164  lastItem );
165  }
166  delete mMultiItemInfo;
167  mMultiItemInfo = 0;
168  return true;
169 }
170 
171 bool KOAgendaItem::setIncidence( Incidence *i )
172 {
173  mIncidence = i;
174  updateIcons();
175  return true;
176 }
177 
178 /*
179  Return height of item in units of agenda cells
180 */
181 int KOAgendaItem::cellHeight() const
182 {
183  return mCellYBottom - mCellYTop + 1;
184 }
185 
186 /*
187  Return height of item in units of agenda cells
188 */
189 int KOAgendaItem::cellWidth() const
190 {
191  return mCellXRight - mCellXLeft + 1;
192 }
193 
194 void KOAgendaItem::setItemDate( const TQDate &qd )
195 {
196  mDate = qd;
197 }
198 
199 void KOAgendaItem::setCellXY( int X, int YTop, int YBottom )
200 {
201  mCellXLeft = X;
202  mCellYTop = YTop;
203  mCellYBottom = YBottom;
204 }
205 
206 void KOAgendaItem::setCellXRight( int xright )
207 {
208  mCellXRight = xright;
209 }
210 
211 void KOAgendaItem::setCellX( int XLeft, int XRight )
212 {
213  mCellXLeft = XLeft;
214  mCellXRight = XRight;
215 }
216 
217 void KOAgendaItem::setCellY( int YTop, int YBottom )
218 {
219  mCellYTop = YTop;
220  mCellYBottom = YBottom;
221 }
222 
223 void KOAgendaItem::setMultiItem( KOAgendaItem *first, KOAgendaItem *prev,
224  KOAgendaItem *next, KOAgendaItem *last )
225 {
226  if ( !mMultiItemInfo ) {
227  mMultiItemInfo = new MultiItemInfo;
228  }
229  mMultiItemInfo->mFirstMultiItem = first;
230  mMultiItemInfo->mPrevMultiItem = prev;
231  mMultiItemInfo->mNextMultiItem = next;
232  mMultiItemInfo->mLastMultiItem = last;
233 }
234 bool KOAgendaItem::isMultiItem()
235 {
236  return mMultiItemInfo;
237 }
238 KOAgendaItem* KOAgendaItem::prependMoveItem(KOAgendaItem* e)
239 {
240  if (!e) return e;
241 
242  KOAgendaItem*first=0, *last=0;
243  if (isMultiItem()) {
244  first=mMultiItemInfo->mFirstMultiItem;
245  last=mMultiItemInfo->mLastMultiItem;
246  }
247  if (!first) first=this;
248  if (!last) last=this;
249 
250  e->setMultiItem(0, 0, first, last);
251  first->setMultiItem(e, e, first->nextMultiItem(), first->lastMultiItem() );
252 
253  KOAgendaItem*tmp=first->nextMultiItem();
254  while (tmp) {
255  tmp->setMultiItem( e, tmp->prevMultiItem(), tmp->nextMultiItem(), tmp->lastMultiItem() );
256  tmp = tmp->nextMultiItem();
257  }
258 
259  if ( mStartMoveInfo && !e->moveInfo() ) {
260  e->mStartMoveInfo=new MultiItemInfo( *mStartMoveInfo );
261 // e->moveInfo()->mFirstMultiItem = moveInfo()->mFirstMultiItem;
262 // e->moveInfo()->mLastMultiItem = moveInfo()->mLastMultiItem;
263  e->moveInfo()->mPrevMultiItem = 0;
264  e->moveInfo()->mNextMultiItem = first;
265  }
266 
267  if (first && first->moveInfo()) {
268  first->moveInfo()->mPrevMultiItem = e;
269  }
270  return e;
271 }
272 
273 KOAgendaItem* KOAgendaItem::appendMoveItem(KOAgendaItem* e)
274 {
275  if (!e) return e;
276 
277  KOAgendaItem*first=0, *last=0;
278  if (isMultiItem()) {
279  first=mMultiItemInfo->mFirstMultiItem;
280  last=mMultiItemInfo->mLastMultiItem;
281  }
282  if (!first) first=this;
283  if (!last) last=this;
284 
285  e->setMultiItem( first, last, 0, 0 );
286  KOAgendaItem*tmp=first;
287 
288  while (tmp) {
289  tmp->setMultiItem(tmp->firstMultiItem(), tmp->prevMultiItem(), tmp->nextMultiItem(), e);
290  tmp = tmp->nextMultiItem();
291  }
292  last->setMultiItem( last->firstMultiItem(), last->prevMultiItem(), e, e);
293 
294  if ( mStartMoveInfo && !e->moveInfo() ) {
295  e->mStartMoveInfo=new MultiItemInfo( *mStartMoveInfo );
296 // e->moveInfo()->mFirstMultiItem = moveInfo()->mFirstMultiItem;
297 // e->moveInfo()->mLastMultiItem = moveInfo()->mLastMultiItem;
298  e->moveInfo()->mPrevMultiItem = last;
299  e->moveInfo()->mNextMultiItem = 0;
300  }
301  if (last && last->moveInfo()) {
302  last->moveInfo()->mNextMultiItem = e;
303  }
304  return e;
305 }
306 
307 KOAgendaItem* KOAgendaItem::removeMoveItem(KOAgendaItem* e)
308 {
309  if (isMultiItem()) {
310  KOAgendaItem *first = mMultiItemInfo->mFirstMultiItem;
311  KOAgendaItem *next, *prev;
312  KOAgendaItem *last = mMultiItemInfo->mLastMultiItem;
313  if (!first) first = this;
314  if (!last) last = this;
315  if ( first==e ) {
316  first = first->nextMultiItem();
317  first->setMultiItem( 0, 0, first->nextMultiItem(), first->lastMultiItem() );
318  }
319  if ( last==e ) {
320  last=last->prevMultiItem();
321  last->setMultiItem( last->firstMultiItem(), last->prevMultiItem(), 0, 0 );
322  }
323 
324  KOAgendaItem *tmp = first;
325  if ( first==last ) {
326  delete mMultiItemInfo;
327  tmp = 0;
328  mMultiItemInfo = 0;
329  }
330  while ( tmp ) {
331  next = tmp->nextMultiItem();
332  prev = tmp->prevMultiItem();
333  if ( e==next ) {
334  next = next->nextMultiItem();
335  }
336  if ( e==prev ) {
337  prev = prev->prevMultiItem();
338  }
339  tmp->setMultiItem((tmp==first)?0:first, (tmp==prev)?0:prev, (tmp==next)?0:next, (tmp==last)?0:last);
340  tmp = tmp->nextMultiItem();
341  }
342  }
343 
344  return e;
345 }
346 
347 
348 void KOAgendaItem::startMove()
349 {
350  KOAgendaItem* first = this;
351  if ( isMultiItem() && mMultiItemInfo->mFirstMultiItem ) {
352  first=mMultiItemInfo->mFirstMultiItem;
353  }
354  first->startMovePrivate();
355 }
356 
357 void KOAgendaItem::startMovePrivate()
358 {
359  mStartMoveInfo = new MultiItemInfo;
360  mStartMoveInfo->mStartCellXLeft = mCellXLeft;
361  mStartMoveInfo->mStartCellXRight = mCellXRight;
362  mStartMoveInfo->mStartCellYTop = mCellYTop;
363  mStartMoveInfo->mStartCellYBottom = mCellYBottom;
364  if (mMultiItemInfo) {
365  mStartMoveInfo->mFirstMultiItem = mMultiItemInfo->mFirstMultiItem;
366  mStartMoveInfo->mLastMultiItem = mMultiItemInfo->mLastMultiItem;
367  mStartMoveInfo->mPrevMultiItem = mMultiItemInfo->mPrevMultiItem;
368  mStartMoveInfo->mNextMultiItem = mMultiItemInfo->mNextMultiItem;
369  } else {
370  mStartMoveInfo->mFirstMultiItem = 0;
371  mStartMoveInfo->mLastMultiItem = 0;
372  mStartMoveInfo->mPrevMultiItem = 0;
373  mStartMoveInfo->mNextMultiItem = 0;
374  }
375  if ( isMultiItem() && mMultiItemInfo->mNextMultiItem )
376  {
377  mMultiItemInfo->mNextMultiItem->startMovePrivate();
378  }
379 }
380 
381 void KOAgendaItem::resetMove()
382 {
383  if ( mStartMoveInfo ) {
384  if ( mStartMoveInfo->mFirstMultiItem ) {
385  mStartMoveInfo->mFirstMultiItem->resetMovePrivate();
386  } else {
387  resetMovePrivate();
388  }
389  }
390 }
391 
392 void KOAgendaItem::resetMovePrivate()
393 {
394  if (mStartMoveInfo) {
395  mCellXLeft = mStartMoveInfo->mStartCellXLeft;
396  mCellXRight = mStartMoveInfo->mStartCellXRight;
397  mCellYTop = mStartMoveInfo->mStartCellYTop;
398  mCellYBottom = mStartMoveInfo->mStartCellYBottom;
399 
400  // if we don't have mMultiItemInfo, the item didn't span two days before,
401  // and wasn't moved over midnight, either, so we don't have to reset
402  // anything. Otherwise, restore from mMoveItemInfo
403  if ( mMultiItemInfo ) {
404  // It was already a multi-day info
405  mMultiItemInfo->mFirstMultiItem = mStartMoveInfo->mFirstMultiItem;
406  mMultiItemInfo->mPrevMultiItem = mStartMoveInfo->mPrevMultiItem;
407  mMultiItemInfo->mNextMultiItem = mStartMoveInfo->mNextMultiItem;
408  mMultiItemInfo->mLastMultiItem = mStartMoveInfo->mLastMultiItem;
409 
410  if ( !mStartMoveInfo->mFirstMultiItem ) {
411  // This was the first multi-item when the move started, delete all previous
412  KOAgendaItem*toDel=mStartMoveInfo->mPrevMultiItem;
413  KOAgendaItem*nowDel=0L;
414  while (toDel) {
415  nowDel=toDel;
416  if (nowDel->moveInfo()) {
417  toDel=nowDel->moveInfo()->mPrevMultiItem;
418  }
419  emit removeAgendaItem( nowDel );
420  }
421  mMultiItemInfo->mFirstMultiItem = 0L;
422  mMultiItemInfo->mPrevMultiItem = 0L;
423  }
424  if ( !mStartMoveInfo->mLastMultiItem ) {
425  // This was the last multi-item when the move started, delete all next
426  KOAgendaItem*toDel=mStartMoveInfo->mNextMultiItem;
427  KOAgendaItem*nowDel=0L;
428  while (toDel) {
429  nowDel=toDel;
430  if (nowDel->moveInfo()) {
431  toDel=nowDel->moveInfo()->mNextMultiItem;
432  }
433  emit removeAgendaItem( nowDel );
434  }
435  mMultiItemInfo->mLastMultiItem = 0L;
436  mMultiItemInfo->mNextMultiItem = 0L;
437  }
438 
439  if ( mStartMoveInfo->mFirstMultiItem==0 && mStartMoveInfo->mLastMultiItem==0 ) {
440  // it was a single-day event before we started the move.
441  delete mMultiItemInfo;
442  mMultiItemInfo = 0;
443  }
444  }
445  delete mStartMoveInfo;
446  mStartMoveInfo = 0;
447  }
448  emit showAgendaItem( this );
449  if ( nextMultiItem() ) {
450  nextMultiItem()->resetMovePrivate();
451  }
452 }
453 
454 void KOAgendaItem::endMove()
455 {
456  KOAgendaItem*first=firstMultiItem();
457  if (!first) first=this;
458  first->endMovePrivate();
459 }
460 
461 void KOAgendaItem::endMovePrivate()
462 {
463  if ( mStartMoveInfo ) {
464  // if first, delete all previous
465  if ( !firstMultiItem() || firstMultiItem()==this ) {
466  KOAgendaItem*toDel=mStartMoveInfo->mPrevMultiItem;
467  KOAgendaItem*nowDel = 0;
468  while (toDel) {
469  nowDel=toDel;
470  if (nowDel->moveInfo()) {
471  toDel=nowDel->moveInfo()->mPrevMultiItem;
472  }
473  emit removeAgendaItem( nowDel );
474  }
475  }
476  // if last, delete all next
477  if ( !lastMultiItem() || lastMultiItem()==this ) {
478  KOAgendaItem*toDel=mStartMoveInfo->mNextMultiItem;
479  KOAgendaItem*nowDel = 0;
480  while (toDel) {
481  nowDel=toDel;
482  if (nowDel->moveInfo()) {
483  toDel=nowDel->moveInfo()->mNextMultiItem;
484  }
485  emit removeAgendaItem( nowDel );
486  }
487  }
488  // also delete the moving info
489  delete mStartMoveInfo;
490  mStartMoveInfo=0;
491  if ( nextMultiItem() )
492  nextMultiItem()->endMovePrivate();
493  }
494 }
495 
496 void KOAgendaItem::moveRelative(int dx, int dy)
497 {
498  int newXLeft = cellXLeft() + dx;
499  int newXRight = cellXRight() + dx;
500  int newYTop = cellYTop() + dy;
501  int newYBottom = cellYBottom() + dy;
502  setCellXY(newXLeft,newYTop,newYBottom);
503  setCellXRight(newXRight);
504 }
505 
506 void KOAgendaItem::expandTop(int dy)
507 {
508  int newYTop = cellYTop() + dy;
509  int newYBottom = cellYBottom();
510  if (newYTop > newYBottom) newYTop = newYBottom;
511  setCellY(newYTop, newYBottom);
512 }
513 
514 void KOAgendaItem::expandBottom(int dy)
515 {
516  int newYTop = cellYTop();
517  int newYBottom = cellYBottom() + dy;
518  if (newYBottom < newYTop) newYBottom = newYTop;
519  setCellY(newYTop, newYBottom);
520 }
521 
522 void KOAgendaItem::expandLeft(int dx)
523 {
524  int newXLeft = cellXLeft() + dx;
525  int newXRight = cellXRight();
526  if ( newXLeft > newXRight ) newXLeft = newXRight;
527  setCellX( newXLeft, newXRight );
528 }
529 
530 void KOAgendaItem::expandRight(int dx)
531 {
532  int newXLeft = cellXLeft();
533  int newXRight = cellXRight() + dx;
534  if ( newXRight < newXLeft ) newXRight = newXLeft;
535  setCellX( newXLeft, newXRight );
536 }
537 
538 TQToolTipGroup *KOAgendaItem::toolTipGroup()
539 {
540  if (!mToolTipGroup) mToolTipGroup = new TQToolTipGroup(0);
541  return mToolTipGroup;
542 }
543 
544 void KOAgendaItem::dragEnterEvent( TQDragEnterEvent *e )
545 {
546 #ifndef KORG_NODND
547  if ( ICalDrag::canDecode( e ) || VCalDrag::canDecode( e ) ) {
548  e->ignore();
549  return;
550  }
551  if ( KVCardDrag::canDecode( e ) || TQTextDrag::canDecode( e ) )
552  e->accept();
553  else
554  e->ignore();
555 #endif
556 }
557 
558 void KOAgendaItem::addAttendee( const TQString &newAttendee )
559 {
560  kdDebug(5850) << " Email: " << newAttendee << endl;
561  TQString name, email;
562  KPIM::getNameAndMail( newAttendee, name, email );
563  if ( !( name.isEmpty() && email.isEmpty() ) ) {
564  mIncidence->addAttendee(new Attendee(name,email));
565  KMessageBox::information( this, i18n("Attendee \"%1\" added to the calendar item \"%2\"").arg(KPIM::normalizedAddress(name, email, TQString())).arg(text()), i18n("Attendee added"), "AttendeeDroppedAdded" );
566  }
567 
568 }
569 
570 void KOAgendaItem::dropEvent( TQDropEvent *e )
571 {
572  // 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.
573 #ifndef KORG_NODND
574  TQString text;
575 
576  bool decoded = TQTextDrag::decode( e, text );
577  if( decoded && text.startsWith( "file:" ) ) {
578  mIncidence->addAttachment( new Attachment( text ) );
579  return;
580  }
581 
582 #ifndef KORG_NOKABC
583  TDEABC::Addressee::List list;
584  if ( KVCardDrag::decode( e, list ) ) {
585  TDEABC::Addressee::List::Iterator it;
586  for ( it = list.begin(); it != list.end(); ++it ) {
587  TQString em( (*it).fullEmail() );
588  if ( em.isEmpty() ) {
589  em = (*it).realName();
590  }
591  addAttendee( em );
592  }
593  }
594 #else
595  if( decoded ) {
596  kdDebug(5850) << "Dropped : " << text << endl;
597 
598  TQStringList emails = TQStringList::split( ",", text );
599  for( TQStringList::ConstIterator it = emails.begin(); it != emails.end();
600  ++it ) {
601  addAttendee( *it );
602  }
603  }
604 #endif // KORG_NOKABC
605 
606 #endif // KORG_NODND
607 }
608 
609 
610 TQPtrList<KOAgendaItem> KOAgendaItem::conflictItems()
611 {
612  return mConflictItems;
613 }
614 
615 void KOAgendaItem::setConflictItems( TQPtrList<KOAgendaItem> ci )
616 {
617  mConflictItems = ci;
618  KOAgendaItem *item;
619  for ( item = mConflictItems.first(); item != 0;
620  item = mConflictItems.next() ) {
621  item->addConflictItem( this );
622  }
623 }
624 
625 void KOAgendaItem::addConflictItem( KOAgendaItem *ci )
626 {
627  if ( mConflictItems.find( ci ) < 0 ) mConflictItems.append( ci );
628 }
629 
630 TQString KOAgendaItem::label() const
631 {
632  return mLabelText;
633 }
634 
635 bool KOAgendaItem::overlaps( KOrg::CellItem *o ) const
636 {
637  KOAgendaItem *other = static_cast<KOAgendaItem *>( o );
638 
639  if ( cellXLeft() <= other->cellXRight() &&
640  cellXRight() >= other->cellXLeft() ) {
641  if ( ( cellYTop() <= other->cellYBottom() ) &&
642  ( cellYBottom() >= other->cellYTop() ) ) {
643  return true;
644  }
645  }
646 
647  return false;
648 }
649 
650 void KOAgendaItem::paintFrame( TQPainter *p, const TQColor &color )
651 {
652  TQColor oldpen(p->pen().color());
653  p->setPen( color );
654  p->drawRect( 0, 0, width(), height() );
655  p->drawRect( 1, 1, width() - 2, height() - 2 );
656  p->setPen( oldpen );
657 }
658 
659 static void conditionalPaint( TQPainter *p, bool cond, int &x, int ft,
660  const TQPixmap &pxmp )
661 {
662  if ( !cond ) return;
663 
664  p->drawPixmap( x, ft, pxmp );
665  x += pxmp.width() + ft;
666 }
667 
668 void KOAgendaItem::paintEventIcon( TQPainter *p, int &x, int ft )
669 {
670  if ( !mIncidence ) return;
671 
672  if ( mIncidence->type() == "Event" ) {
673  TQPixmap eventPxmp;
674  if ( mIncidence->customProperty( "KABC", "BIRTHDAY" ) == "YES" ) {
675  mSpecialEvent = true;
676  if ( mIncidence->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
677  eventPxmp = KOGlobals::self()->smallIcon( "calendaranniversary" );
678  } else {
679  eventPxmp = KOGlobals::self()->smallIcon( "calendarbirthday" );
680  }
681  conditionalPaint( p, true, x, ft, eventPxmp );
682  }
683  // per kolab/issue4349 we don't draw a regular appointment icon (to save space)
684  }
685 
686 }
687 
688 void KOAgendaItem::paintTodoIcon( TQPainter *p, int &x, int ft )
689 {
690  if ( !mIncidence ) return;
691  static const TQPixmap todoPxmp =
692  KOGlobals::self()->smallIcon( "todo" );
693  static const TQPixmap completedPxmp =
694  KOGlobals::self()->smallIcon( "checkedbox" );
695  if ( mIncidence->type() != "Todo" )
696  return;
697  bool b = ( static_cast<Todo *>( mIncidence ) )->isCompleted();
698  conditionalPaint( p, !b, x, ft, todoPxmp );
699  conditionalPaint( p, b, x, ft, completedPxmp );
700 }
701 
702 void KOAgendaItem::paintAlarmIcon( TQPainter *p, int &x, int ft )
703 {
704  if (!mIconAlarm) return;
705  int y = ft;
706  // if we can't fit it all, bottom align it, more or less, so
707  // it can be guessed better, visually
708  if ( visibleRect().height() - ft < alarmPxmp->height() )
709  y -= ( alarmPxmp->height() - visibleRect().height() - ft );
710  p->drawPixmap( x, y, *alarmPxmp );
711  x += alarmPxmp->width() + ft;
712 }
713 
714 void KOAgendaItem::paintIcons( TQPainter *p, int &x, int ft )
715 {
716  paintEventIcon( p, x, ft );
717  paintTodoIcon( p, x, ft );
718  if ( !mSpecialEvent ) {
719  paintAlarmIcon( p, x, ft );
720  }
721  conditionalPaint( p, mIconRecur && !mSpecialEvent, x, ft, *recurPxmp );
722  conditionalPaint( p, mIconReadonly && !mSpecialEvent, x, ft, *readonlyPxmp );
723  conditionalPaint( p, mIconReply, x, ft, *replyPxmp );
724  conditionalPaint( p, mIconGroup, x, ft, *groupPxmp );
725  conditionalPaint( p, mIconGroupTentative, x, ft, *groupPxmpTentative );
726  conditionalPaint( p, mIconOrganizer, x, ft, *organizerPxmp );
727 }
728 
729 void KOAgendaItem::paintEvent( TQPaintEvent *ev )
730 {
731  //HACK
732  // to reproduce a crash:
733  // 1. start Kontact with the Calendar as the initial module
734  // 2. immediately select the summary (which must include appt and to-do)
735  // causes a crash for me every time in this method unless we make
736  // the following check
737  if ( !mIncidence )return;
738 
739  TQRect visRect = visibleRect();
740  // when scrolling horizontally in the side-by-side view, the repainted area is clipped
741  // to the newly visible area, which is a problem since the content changes when visRect
742  // changes, so repaint the full item in that case
743  if ( ev->rect() != visRect && visRect.isValid() && ev->rect().isValid() ) {
744  repaint( visRect );
745  return;
746  }
747 
748  TQPainter p( this );
749  const int ft = 2; // frame thickness for layout, see paintFrame()
750  const int margin = 1 + ft; // frame + space between frame and content
751 
752  // General idea is to always show the icons (even in the all-day events).
753  // This creates a consistent fealing for the user when the view mode
754  // changes and therefore the available width changes.
755  // Also look at #17984
756 
757  if ( !alarmPxmp ) {
758  alarmPxmp = new TQPixmap( KOGlobals::self()->smallIcon("bell") );
759  recurPxmp = new TQPixmap( KOGlobals::self()->smallIcon("recur") );
760  readonlyPxmp = new TQPixmap( KOGlobals::self()->smallIcon("readonlyevent") );
761  replyPxmp = new TQPixmap( KOGlobals::self()->smallIcon("mail-reply-sender") );
762  groupPxmp = new TQPixmap( KOGlobals::self()->smallIcon("groupevent") );
763  groupPxmpTentative = new TQPixmap( KOGlobals::self()->smallIcon("groupeventtentative") );
764  organizerPxmp = new TQPixmap( KOGlobals::self()->smallIcon("organizer") );
765  }
766 
767  TQColor bgColor;
768  if ( mIncidence->type() == "Todo" ) {
769  if ( static_cast<Todo*>(mIncidence)->isOverdue() )
770  bgColor = KOPrefs::instance()->todoOverdueColor();
771  else if ( static_cast<Todo*>(mIncidence)->dtDue().date() ==
772  TQDateTime::currentDateTime().date() )
773  bgColor = KOPrefs::instance()->todoDueTodayColor();
774  }
775 
776  TQColor categoryColor;
777  TQStringList categories = mIncidence->categories();
778  TQString cat = categories.first();
779  if (cat.isEmpty())
780  categoryColor = KOPrefs::instance()->unsetCategoryColor();
781  else
782  categoryColor = *(KOPrefs::instance()->categoryColor(cat));
783 
784  TQColor resourceColor = mResourceColor;
785  if ( !resourceColor.isValid() )
786  resourceColor = categoryColor;
787 
788  TQColor frameColor;
789  if ( KOPrefs::instance()->agendaViewColors() == KOPrefs::ResourceOnly ||
790  KOPrefs::instance()->agendaViewColors() == KOPrefs::CategoryInsideResourceOutside ) {
791  frameColor = bgColor.isValid() ? bgColor : resourceColor;
792  } else {
793  frameColor = bgColor.isValid() ? bgColor : categoryColor;
794  }
795 
796  if ( !bgColor.isValid() ) {
797  if ( KOPrefs::instance()->agendaViewColors() == KOPrefs::ResourceOnly ||
798  KOPrefs::instance()->agendaViewColors() == KOPrefs::ResourceInsideCategoryOutside ) {
799  bgColor = resourceColor;
800  } else {
801  bgColor = categoryColor;
802  }
803  }
804 
805  if ( cat.isEmpty() &&
806  KOPrefs::instance()->agendaViewColors() == KOPrefs::ResourceInsideCategoryOutside ) {
807  frameColor = bgColor;
808  }
809 
810  if ( cat.isEmpty() &&
811  KOPrefs::instance()->agendaViewColors() == KOPrefs::CategoryInsideResourceOutside ) {
812  bgColor = frameColor;
813  }
814 
815  if ( mSelected ) {
816  frameColor = TQColor( 85 + frameColor.red() * 2/3,
817  85 + frameColor.green() * 2/3,
818  85 + frameColor.blue() * 2/3 );
819  } else {
820  frameColor = frameColor.dark( 115 );
821  }
822  TQColor textColor = getTextColor(bgColor);
823  p.setPen( textColor );
824  p.setBackgroundColor( bgColor );
825  p.setFont(KOPrefs::instance()->mAgendaViewFont);
826  TQFontMetrics fm = p.fontMetrics();
827 
828  int singleLineHeight = fm.boundingRect( mLabelText ).height();
829 
830  p.eraseRect( 0, 0, width(), height() );
831  paintFrame( &p, frameColor );
832 
833  // calculate the height of the full version (case 4) to test whether it is
834  // possible
835 
836  TQString shortH;
837  TQString longH;
838  if ( !isMultiItem() ) {
839  shortH = TDEGlobal::locale()->formatTime(mIncidence->dtStart().time());
840  if (mIncidence->type() != "Todo")
841  longH = i18n("%1 - %2").arg(shortH)
842  .arg(TDEGlobal::locale()->formatTime(mIncidence->dtEnd().time()));
843  else
844  longH = shortH;
845  } else if ( !mMultiItemInfo->mFirstMultiItem ) {
846  shortH = TDEGlobal::locale()->formatTime(mIncidence->dtStart().time());
847  longH = shortH;
848  } else {
849  shortH = TDEGlobal::locale()->formatTime(mIncidence->dtEnd().time());
850  longH = i18n("- %1").arg(shortH);
851  }
852 
853  KWordWrap *ww = KWordWrap::formatText( fm,
854  TQRect(0, 0, width() - (2 * margin), -1),
855  0,
856  mLabelText );
857  int th = ww->boundingRect().height();
858  delete ww;
859 
860  int hlHeight = TQMAX(fm.boundingRect(longH).height(),
861  TQMAX(alarmPxmp->height(), TQMAX(recurPxmp->height(),
862  TQMAX(readonlyPxmp->height(), TQMAX(replyPxmp->height(),
863  TQMAX(groupPxmp->height(), organizerPxmp->height()))))));
864 
865  bool completelyRenderable = th < (height() - 2 * ft - 2 - hlHeight);
866 
867  // case 1: do not draw text when not even a single line fits
868  // Don't do this any more, always try to print out the text. Even if
869  // it's just a few pixel, one can still guess the whole text from just four pixels' height!
870  if ( //( singleLineHeight > height()-4 ) || // ignore margin, be gentle.. Even ignore 2 pixel outside the item
871  ( width() < 16 ) ) {
872  int x = margin;
873  paintTodoIcon( &p, x, ft );
874  return;
875  }
876 
877  // case 2: draw a single line when no more space
878  if ( (2 * singleLineHeight) > (height() - 2 * margin) ) {
879  int x = margin, txtWidth;
880 
881  if ( mIncidence->doesFloat() ) {
882  x += visRect.left();
883  paintIcons( &p, x, ft );
884  txtWidth = visRect.right() - margin - x;
885  }
886  else {
887  paintIcons( &p, x, ft );
888  txtWidth = width() - margin - x;
889  }
890 
891  int y = ((height() - 2 * ft - singleLineHeight) / 2) + fm.ascent();
892  KWordWrap::drawFadeoutText( &p, x, y,
893  txtWidth, mLabelText );
894  return;
895  }
896 
897  // case 3: enough for 2-5 lines, but not for the header.
898  // Also used for the middle days in multi-events
899  if ( ((!completelyRenderable) && ((height() - (2 * margin)) <= (5 * singleLineHeight)) ) ||
900  (isMultiItem() && mMultiItemInfo->mNextMultiItem && mMultiItemInfo->mFirstMultiItem) ) {
901  int x = margin, txtWidth;
902 
903  if ( mIncidence->doesFloat() ) {
904  x += visRect.left();
905  paintIcons( &p, x, ft );
906  txtWidth = visRect.right() - margin - x;
907  }
908  else {
909  paintIcons( &p, x, ft );
910  txtWidth = width() - margin - x;
911  }
912 
913  ww = KWordWrap::formatText( fm,
914  TQRect( 0, 0, txtWidth,
915  (height() - (2 * margin)) ),
916  0,
917  mLabelText );
918 
919  //kdDebug() << "SIZES for " << mLabelText << ": " << width() << " :: " << txtWidth << endl;
920  ww->drawText( &p, x, margin, TQt::AlignHCenter | KWordWrap::FadeOut );
921  delete ww;
922  return;
923  }
924 
925  // case 4: paint everything, with header:
926  // consists of (vertically) ft + headline&icons + ft + text + margin
927  int y = 2 * ft + hlHeight;
928  if ( completelyRenderable )
929  y += (height() - (2 * ft) - margin - hlHeight - th) / 2;
930 
931  int x = margin, txtWidth, hTxtWidth, eventX;
932 
933  if ( mIncidence->doesFloat() ) {
934  shortH = longH = "";
935 
936  if ( (mIncidence->type() != "Todo") &&
937  (mIncidence->dtStart() != mIncidence->dtEnd()) ) { // multi days
938  shortH = longH =
939  i18n("%1 - %2")
940  .arg(TDEGlobal::locale()->formatDate(mIncidence->dtStart().date()))
941  .arg(TDEGlobal::locale()->formatDate(mIncidence->dtEnd().date()));
942 
943  // paint headline
944  p.fillRect( 0, 0, width(), (ft/2) + margin + hlHeight,
945  TQBrush( frameColor ) );
946  }
947 
948  x += visRect.left();
949  eventX = x;
950  txtWidth = visRect.right() - margin - x;
951  paintIcons( &p, x, ft );
952  hTxtWidth = visRect.right() - margin - x;
953  }
954  else {
955  // paint headline
956  p.fillRect( 0, 0, width(), (ft/2) + margin + hlHeight,
957  TQBrush( frameColor ) );
958 
959  txtWidth = width() - margin - x;
960  eventX = x;
961  paintIcons( &p, x, ft );
962  hTxtWidth = width() - margin - x;
963  }
964 
965  TQString headline;
966  int hw = fm.boundingRect( longH ).width();
967  if ( hw > hTxtWidth ) {
968  headline = shortH;
969  hw = fm.boundingRect( shortH ).width();
970  if ( hw < txtWidth )
971  x += (hTxtWidth - hw) / 2;
972  } else {
973  headline = longH;
974  x += (hTxtWidth - hw) / 2;
975  }
976  p.setBackgroundColor( frameColor );
977  p.setPen( getTextColor( frameColor ) );
978  KWordWrap::drawFadeoutText( &p, x, ft + fm.ascent(), hTxtWidth, headline );
979 
980  // draw event text
981  ww = KWordWrap::formatText( fm,
982  TQRect( 0, 0, txtWidth, height() - margin - y ),
983  0,
984  mLabelText );
985 
986  p.setBackgroundColor( bgColor );
987  p.setPen( textColor );
988  TQString ws = ww->wrappedString();
989  if ( ws.left( ws.length()-1 ).find( '\n' ) >= 0 )
990  ww->drawText( &p, eventX, y,
991  TQt::AlignAuto | KWordWrap::FadeOut );
992  else
993  ww->drawText( &p, eventX + (txtWidth-ww->boundingRect().width()-2*margin)/2,
994  y, TQt::AlignHCenter | KWordWrap::FadeOut );
995  delete ww;
996 }
997 
bool RSVP() const
PartStat status() const
static void add(TQWidget *widget, Calendar *calendar, Incidence *incidence, const TQDate &date=TQDate(), TQToolTipGroup *group=0, const TQString &longText="")