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
54TQToolTipGroup *KOAgendaItem::mToolTipGroup = 0;
55
56TQPixmap *KOAgendaItem::alarmPxmp = 0;
57TQPixmap *KOAgendaItem::recurPxmp = 0;
58TQPixmap *KOAgendaItem::readonlyPxmp = 0;
59TQPixmap *KOAgendaItem::replyPxmp = 0;
60TQPixmap *KOAgendaItem::groupPxmp = 0;
61TQPixmap *KOAgendaItem::groupPxmpTentative = 0;
62TQPixmap *KOAgendaItem::organizerPxmp = 0;
63
64//--------------------------------------------------------------------------
65
66KOAgendaItem::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
94void 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
137void KOAgendaItem::select( bool selected )
138{
139 if ( mSelected == selected ) return;
140 mSelected = selected;
141
142 update();
143}
144
145bool 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
171bool 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*/
181int KOAgendaItem::cellHeight() const
182{
183 return mCellYBottom - mCellYTop + 1;
184}
185
186/*
187 Return height of item in units of agenda cells
188*/
189int KOAgendaItem::cellWidth() const
190{
191 return mCellXRight - mCellXLeft + 1;
192}
193
194void KOAgendaItem::setItemDate( const TQDate &qd )
195{
196 mDate = qd;
197}
198
199void KOAgendaItem::setCellXY( int X, int YTop, int YBottom )
200{
201 mCellXLeft = X;
202 mCellYTop = YTop;
203 mCellYBottom = YBottom;
204}
205
206void KOAgendaItem::setCellXRight( int xright )
207{
208 mCellXRight = xright;
209}
210
211void KOAgendaItem::setCellX( int XLeft, int XRight )
212{
213 mCellXLeft = XLeft;
214 mCellXRight = XRight;
215}
216
217void KOAgendaItem::setCellY( int YTop, int YBottom )
218{
219 mCellYTop = YTop;
220 mCellYBottom = YBottom;
221}
222
223void 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}
234bool KOAgendaItem::isMultiItem()
235{
236 return mMultiItemInfo;
237}
238KOAgendaItem* 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
273KOAgendaItem* 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
307KOAgendaItem* 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
348void KOAgendaItem::startMove()
349{
350 KOAgendaItem* first = this;
351 if ( isMultiItem() && mMultiItemInfo->mFirstMultiItem ) {
352 first=mMultiItemInfo->mFirstMultiItem;
353 }
354 first->startMovePrivate();
355}
356
357void 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
381void KOAgendaItem::resetMove()
382{
383 if ( mStartMoveInfo ) {
384 if ( mStartMoveInfo->mFirstMultiItem ) {
385 mStartMoveInfo->mFirstMultiItem->resetMovePrivate();
386 } else {
387 resetMovePrivate();
388 }
389 }
390}
391
392void 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
454void KOAgendaItem::endMove()
455{
456 KOAgendaItem*first=firstMultiItem();
457 if (!first) first=this;
458 first->endMovePrivate();
459}
460
461void 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
496void 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
506void 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
514void 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
522void 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
530void 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
538TQToolTipGroup *KOAgendaItem::toolTipGroup()
539{
540 if (!mToolTipGroup) mToolTipGroup = new TQToolTipGroup(0);
541 return mToolTipGroup;
542}
543
544void 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
558void 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
570void 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
610TQPtrList<KOAgendaItem> KOAgendaItem::conflictItems()
611{
612 return mConflictItems;
613}
614
615void 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
625void KOAgendaItem::addConflictItem( KOAgendaItem *ci )
626{
627 if ( mConflictItems.find( ci ) < 0 ) mConflictItems.append( ci );
628}
629
630TQString KOAgendaItem::label() const
631{
632 return mLabelText;
633}
634
635bool 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
650void 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
659static 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
668void 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
688void 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
702void 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
714void 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
729void 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="")