2
Copyright (c) 2000,2001,2003 Cornelius Schumacher <schumacher@kde.org>
3
Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
5
This program is free software; you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation; either version 2 of the License, or
8
(at your option) any later version.
10
This program is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
GNU General Public License for more details.
15
You should have received a copy of the GNU General Public License along
16
with this program; if not, write to the Free Software Foundation, Inc.,
17
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
As a special exception, permission is given to link this program
20
with any edition of Qt, and distribute the resulting executable,
21
without including the source code for Qt in the source distribution.
23
#include "agendaitem.h"
24
#include "eventview.h"
27
#include "prefs_base.h" // for enums
29
#include <calendarsupport/kcalprefs.h>
30
#include <calendarsupport/utils.h>
32
#include <KABC/VCardDrag>
34
#include <KCalUtils/ICalDrag>
35
#include <KCalUtils/VCalDrag>
36
#include <KCalUtils/IncidenceFormatter>
38
#include <KPIMUtils/Email>
41
#include <KMessageBox>
44
#include <QDragEnterEvent>
46
#include <QPixmapCache>
49
using namespace KCalCore;
50
using namespace EventViews;
52
//-----------------------------------------------------------------------------
54
QPixmap *AgendaItem::alarmPxmp = 0;
55
QPixmap *AgendaItem::recurPxmp = 0;
56
QPixmap *AgendaItem::readonlyPxmp = 0;
57
QPixmap *AgendaItem::replyPxmp = 0;
58
QPixmap *AgendaItem::groupPxmp = 0;
59
QPixmap *AgendaItem::groupPxmpTent = 0;
60
QPixmap *AgendaItem::organizerPxmp = 0;
61
QPixmap *AgendaItem::eventPxmp = 0;
63
//-----------------------------------------------------------------------------
65
AgendaItem::AgendaItem( EventView *eventView, CalendarSupport::Calendar *calendar,
66
const Akonadi::Item &item,
67
int itemPos, int itemCount,
68
const QDate &qd, bool isSelected, QWidget *parent )
69
: QWidget( parent ), mEventView( eventView ), mCalendar( calendar ), mIncidence( item ),
70
mDate( qd ), mValid( true ), mCloned( false ), mSelected( isSelected ), mSpecialEvent( false )
72
if ( !CalendarSupport::hasIncidence( mIncidence ) ) {
77
KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( item );
78
Q_ASSERT( incidence );
79
if ( incidence->customProperty( "KABC", "BIRTHDAY" ) == "YES" ||
80
incidence->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
81
const int years = EventViews::yearDiff( incidence->dtStart().date(), qd );
83
incidence = KCalCore::Incidence::Ptr( incidence->clone() );
84
incidence->setReadOnly( false );
85
incidence->setSummary( i18np( "%2 (1 year)", "%2 (%1 years)", years, incidence->summary() ) );
86
incidence->setReadOnly( true );
88
mIncidence.setPayload<KCalCore::Incidence::Ptr>( incidence );
92
mLabelText = incidence->summary();
95
mIconReadonly = false;
98
mIconGroupTent = false;
99
mIconOrganizer = false;
104
mItemCount = itemCount;
106
QPalette pal = palette();
107
pal.setColor( QPalette::Window, Qt::transparent );
110
setCellXY( 0, 0, 1 );
112
setMouseTracking( true );
113
mResourceColor = QColor();
116
setAcceptDrops( true );
119
AgendaItem::~AgendaItem()
123
void AgendaItem::updateIcons()
128
KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( mIncidence );
129
Q_ASSERT( incidence );
130
mIconReadonly = incidence->isReadOnly();
131
mIconRecur = incidence->recurs();
132
mIconAlarm = incidence->hasEnabledAlarms();
133
if ( incidence->attendeeCount() > 1 ) {
134
if ( mEventView->kcalPreferences()->thatIsMe( incidence->organizer()->email() ) ) {
137
mIconGroupTent = false;
138
mIconOrganizer = true;
140
KCalCore::Attendee::Ptr me = incidence->attendeeByMails( mEventView->kcalPreferences()->allEmails() );
142
if ( me->status() == KCalCore::Attendee::NeedsAction && me->RSVP() ) {
145
mIconGroupTent = false;
146
mIconOrganizer = false;
147
} else if ( me->status() == KCalCore::Attendee::Tentative ) {
150
mIconGroupTent = true;
151
mIconOrganizer = false;
155
mIconGroupTent = false;
156
mIconOrganizer = false;
161
mIconGroupTent = false;
162
mIconOrganizer = false;
169
void AgendaItem::select( bool selected )
171
if ( mSelected != selected ) {
172
mSelected = selected;
177
bool AgendaItem::dissociateFromMultiItem()
179
if ( !isMultiItem() ) {
183
AgendaItem::QPtr firstItem = firstMultiItem();
184
if ( firstItem == this ) {
185
firstItem = nextMultiItem();
188
AgendaItem::QPtr lastItem = lastMultiItem();
189
if ( lastItem == this ) {
190
lastItem = prevMultiItem();
193
AgendaItem::QPtr prevItem = prevMultiItem();
194
AgendaItem::QPtr nextItem = nextMultiItem();
197
prevItem->setMultiItem( firstItem, prevItem->prevMultiItem(), nextItem, lastItem );
200
nextItem->setMultiItem( firstItem, prevItem, nextItem->prevMultiItem(), lastItem );
202
delete mMultiItemInfo;
207
void AgendaItem::setIncidence( const Akonadi::Item &incidence )
210
if ( CalendarSupport::hasIncidence( incidence ) ) {
212
mIncidence = incidence;
213
mLabelText = CalendarSupport::incidence( incidence )->summary();
219
Return height of item in units of agenda cells
221
int AgendaItem::cellHeight() const
223
return mCellYBottom - mCellYTop + 1;
227
Return height of item in units of agenda cells
229
int AgendaItem::cellWidth() const
231
return mCellXRight - mCellXLeft + 1;
234
void AgendaItem::setItemDate( const QDate &qd )
239
void AgendaItem::setCellXY( int X, int YTop, int YBottom )
243
mCellYBottom = YBottom;
246
void AgendaItem::setCellXRight( int XRight )
248
mCellXRight = XRight;
251
void AgendaItem::setCellX( int XLeft, int XRight )
254
mCellXRight = XRight;
257
void AgendaItem::setCellY( int YTop, int YBottom )
260
mCellYBottom = YBottom;
263
void AgendaItem::setMultiItem( AgendaItem::QPtr first, AgendaItem::QPtr prev,
264
AgendaItem::QPtr next, AgendaItem::QPtr last )
266
if ( !mMultiItemInfo ) {
267
mMultiItemInfo = new MultiItemInfo;
269
mMultiItemInfo->mFirstMultiItem = first;
270
mMultiItemInfo->mPrevMultiItem = prev;
271
mMultiItemInfo->mNextMultiItem = next;
272
mMultiItemInfo->mLastMultiItem = last;
275
bool AgendaItem::isMultiItem() const
277
return mMultiItemInfo;
280
AgendaItem::QPtr AgendaItem::prependMoveItem( AgendaItem::QPtr e )
286
AgendaItem::QPtr first = 0, last = 0;
287
if ( isMultiItem() ) {
288
first = mMultiItemInfo->mFirstMultiItem;
289
last = mMultiItemInfo->mLastMultiItem;
298
e->setMultiItem( 0, 0, first, last );
299
first->setMultiItem( e, e, first->nextMultiItem(), first->lastMultiItem() );
301
AgendaItem::QPtr tmp = first->nextMultiItem();
303
tmp->setMultiItem( e, tmp->prevMultiItem(), tmp->nextMultiItem(), tmp->lastMultiItem() );
304
tmp = tmp->nextMultiItem();
307
if ( mStartMoveInfo && !e->moveInfo() ) {
308
e->mStartMoveInfo=new MultiItemInfo( *mStartMoveInfo );
309
// e->moveInfo()->mFirstMultiItem = moveInfo()->mFirstMultiItem;
310
// e->moveInfo()->mLastMultiItem = moveInfo()->mLastMultiItem;
311
e->moveInfo()->mPrevMultiItem = 0;
312
e->moveInfo()->mNextMultiItem = first;
315
if ( first && first->moveInfo() ) {
316
first->moveInfo()->mPrevMultiItem = e;
321
AgendaItem::QPtr AgendaItem::appendMoveItem( AgendaItem::QPtr e )
327
AgendaItem::QPtr first = 0, last = 0;
328
if ( isMultiItem() ) {
329
first = mMultiItemInfo->mFirstMultiItem;
330
last = mMultiItemInfo->mLastMultiItem;
339
e->setMultiItem( first, last, 0, 0 );
340
AgendaItem::QPtr tmp = first;
343
tmp->setMultiItem( tmp->firstMultiItem(), tmp->prevMultiItem(), tmp->nextMultiItem(), e );
344
tmp = tmp->nextMultiItem();
346
last->setMultiItem( last->firstMultiItem(), last->prevMultiItem(), e, e );
348
if ( mStartMoveInfo && !e->moveInfo() ) {
349
e->mStartMoveInfo=new MultiItemInfo( *mStartMoveInfo );
350
// e->moveInfo()->mFirstMultiItem = moveInfo()->mFirstMultiItem;
351
// e->moveInfo()->mLastMultiItem = moveInfo()->mLastMultiItem;
352
e->moveInfo()->mPrevMultiItem = last;
353
e->moveInfo()->mNextMultiItem = 0;
355
if ( last && last->moveInfo() ) {
356
last->moveInfo()->mNextMultiItem = e;
361
AgendaItem::QPtr AgendaItem::removeMoveItem( AgendaItem::QPtr e )
363
if ( isMultiItem() ) {
364
AgendaItem::QPtr first = mMultiItemInfo->mFirstMultiItem;
365
AgendaItem::QPtr next, prev;
366
AgendaItem::QPtr last = mMultiItemInfo->mLastMultiItem;
374
first = first->nextMultiItem();
375
first->setMultiItem( 0, 0, first->nextMultiItem(), first->lastMultiItem() );
378
last = last->prevMultiItem();
379
last->setMultiItem( last->firstMultiItem(), last->prevMultiItem(), 0, 0 );
382
AgendaItem::QPtr tmp = first;
383
if ( first == last ) {
384
delete mMultiItemInfo;
389
next = tmp->nextMultiItem();
390
prev = tmp->prevMultiItem();
392
next = next->nextMultiItem();
395
prev = prev->prevMultiItem();
397
tmp->setMultiItem( ( tmp == first ) ? 0 : first,
398
( tmp == prev ) ? 0 : prev,
399
( tmp == next ) ? 0 : next,
400
( tmp == last ) ? 0 : last );
401
tmp = tmp->nextMultiItem();
408
void AgendaItem::startMove()
410
AgendaItem::QPtr first = this;
411
if ( isMultiItem() && mMultiItemInfo->mFirstMultiItem ) {
412
first=mMultiItemInfo->mFirstMultiItem;
414
first->startMovePrivate();
417
void AgendaItem::startMovePrivate()
419
mStartMoveInfo = new MultiItemInfo;
420
mStartMoveInfo->mStartCellXLeft = mCellXLeft;
421
mStartMoveInfo->mStartCellXRight = mCellXRight;
422
mStartMoveInfo->mStartCellYTop = mCellYTop;
423
mStartMoveInfo->mStartCellYBottom = mCellYBottom;
424
if ( mMultiItemInfo ) {
425
mStartMoveInfo->mFirstMultiItem = mMultiItemInfo->mFirstMultiItem;
426
mStartMoveInfo->mLastMultiItem = mMultiItemInfo->mLastMultiItem;
427
mStartMoveInfo->mPrevMultiItem = mMultiItemInfo->mPrevMultiItem;
428
mStartMoveInfo->mNextMultiItem = mMultiItemInfo->mNextMultiItem;
430
mStartMoveInfo->mFirstMultiItem = 0;
431
mStartMoveInfo->mLastMultiItem = 0;
432
mStartMoveInfo->mPrevMultiItem = 0;
433
mStartMoveInfo->mNextMultiItem = 0;
435
if ( isMultiItem() && mMultiItemInfo->mNextMultiItem ) {
436
mMultiItemInfo->mNextMultiItem->startMovePrivate();
440
void AgendaItem::resetMove()
442
if ( mStartMoveInfo ) {
443
if ( mStartMoveInfo->mFirstMultiItem ) {
444
mStartMoveInfo->mFirstMultiItem->resetMovePrivate();
451
void AgendaItem::resetMovePrivate()
453
if ( mStartMoveInfo ) {
454
mCellXLeft = mStartMoveInfo->mStartCellXLeft;
455
mCellXRight = mStartMoveInfo->mStartCellXRight;
456
mCellYTop = mStartMoveInfo->mStartCellYTop;
457
mCellYBottom = mStartMoveInfo->mStartCellYBottom;
459
// if we don't have mMultiItemInfo, the item didn't span two days before,
460
// and wasn't moved over midnight, either, so we don't have to reset
461
// anything. Otherwise, restore from mMoveItemInfo
462
if ( mMultiItemInfo ) {
463
// It was already a multi-day info
464
mMultiItemInfo->mFirstMultiItem = mStartMoveInfo->mFirstMultiItem;
465
mMultiItemInfo->mPrevMultiItem = mStartMoveInfo->mPrevMultiItem;
466
mMultiItemInfo->mNextMultiItem = mStartMoveInfo->mNextMultiItem;
467
mMultiItemInfo->mLastMultiItem = mStartMoveInfo->mLastMultiItem;
469
if ( !mStartMoveInfo->mFirstMultiItem ) {
470
// This was the first multi-item when the move started, delete all previous
471
AgendaItem::QPtr toDel = mStartMoveInfo->mPrevMultiItem;
472
AgendaItem::QPtr nowDel = 0;
475
if ( nowDel->moveInfo() ) {
476
toDel = nowDel->moveInfo()->mPrevMultiItem;
478
emit removeAgendaItem( nowDel );
480
mMultiItemInfo->mFirstMultiItem = 0;
481
mMultiItemInfo->mPrevMultiItem = 0;
483
if ( !mStartMoveInfo->mLastMultiItem ) {
484
// This was the last multi-item when the move started, delete all next
485
AgendaItem::QPtr toDel = mStartMoveInfo->mNextMultiItem;
486
AgendaItem::QPtr nowDel = 0;
489
if ( nowDel->moveInfo() ) {
490
toDel=nowDel->moveInfo()->mNextMultiItem;
492
emit removeAgendaItem( nowDel );
494
mMultiItemInfo->mLastMultiItem = 0;
495
mMultiItemInfo->mNextMultiItem = 0;
498
if ( mStartMoveInfo->mFirstMultiItem == 0 && mStartMoveInfo->mLastMultiItem == 0 ) {
499
// it was a single-day event before we started the move.
500
delete mMultiItemInfo;
504
delete mStartMoveInfo;
507
emit showAgendaItem( this );
508
if ( nextMultiItem() ) {
509
nextMultiItem()->resetMovePrivate();
513
void AgendaItem::endMove()
515
AgendaItem::QPtr first = firstMultiItem();
519
first->endMovePrivate();
522
void AgendaItem::endMovePrivate()
524
if ( mStartMoveInfo ) {
525
// if first, delete all previous
526
if ( !firstMultiItem() || firstMultiItem() == this ) {
527
AgendaItem::QPtr toDel = mStartMoveInfo->mPrevMultiItem;
528
AgendaItem::QPtr nowDel = 0;
531
if ( nowDel->moveInfo() ) {
532
toDel=nowDel->moveInfo()->mPrevMultiItem;
534
emit removeAgendaItem( nowDel );
537
// if last, delete all next
538
if ( !lastMultiItem() || lastMultiItem() == this ) {
539
AgendaItem::QPtr toDel=mStartMoveInfo->mNextMultiItem;
540
AgendaItem::QPtr nowDel = 0;
543
if ( nowDel->moveInfo() ) {
544
toDel=nowDel->moveInfo()->mNextMultiItem;
546
emit removeAgendaItem( nowDel );
549
// also delete the moving info
550
delete mStartMoveInfo;
552
if ( nextMultiItem() ) {
553
nextMultiItem()->endMovePrivate();
558
void AgendaItem::moveRelative( int dx, int dy )
560
int newXLeft = cellXLeft() + dx;
561
int newXRight = cellXRight() + dx;
562
int newYTop = cellYTop() + dy;
563
int newYBottom = cellYBottom() + dy;
564
setCellXY( newXLeft, newYTop, newYBottom );
565
setCellXRight( newXRight );
568
void AgendaItem::expandTop( int dy, const bool allowOverLimit )
570
int newYTop = cellYTop() + dy;
571
int newYBottom = cellYBottom();
572
if ( newYTop > newYBottom && !allowOverLimit ) {
573
newYTop = newYBottom;
575
setCellY( newYTop, newYBottom );
578
void AgendaItem::expandBottom( int dy )
580
int newYTop = cellYTop();
581
int newYBottom = cellYBottom() + dy;
582
if ( newYBottom < newYTop ) {
583
newYBottom = newYTop;
585
setCellY( newYTop, newYBottom );
588
void AgendaItem::expandLeft( int dx )
590
int newXLeft = cellXLeft() + dx;
591
int newXRight = cellXRight();
592
if ( newXLeft > newXRight ) {
593
newXLeft = newXRight;
595
setCellX( newXLeft, newXRight );
598
void AgendaItem::expandRight( int dx )
600
int newXLeft = cellXLeft();
601
int newXRight = cellXRight() + dx;
602
if ( newXRight < newXLeft ) {
603
newXRight = newXLeft;
605
setCellX( newXLeft, newXRight );
608
void AgendaItem::dragEnterEvent( QDragEnterEvent *e )
611
const QMimeData *md = e->mimeData();
612
if ( KCalUtils::ICalDrag::canDecode( md ) || KCalUtils::VCalDrag::canDecode( md ) ) {
613
// TODO: Allow dragging events/todos onto other events to create a relation
617
if ( KABC::VCardDrag::canDecode( md ) || md->hasText() ) {
627
void AgendaItem::addAttendee( const QString &newAttendee )
633
const KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( mIncidence );
635
KPIMUtils::extractEmailAddressAndName( newAttendee, email, name );
636
if ( !( name.isEmpty() && email.isEmpty() ) ) {
637
incidence->addAttendee( KCalCore::Attendee::Ptr( new KCalCore::Attendee( name, email ) ) );
638
KMessageBox::information(
640
i18n( "Attendee \"%1\" added to the calendar item \"%2\"",
641
KPIMUtils::normalizedAddress( name, email, QString() ), text() ),
642
i18n( "Attendee added" ), "AttendeeDroppedAdded" );
646
void AgendaItem::dropEvent( QDropEvent *e )
648
// TODO: Organize this better: First check for attachment
649
// (not only file, also any other url!), then if it's a vcard,
650
// otherwise check for attendees, then if the data is binary,
651
// add a binary attachment.
657
const QMimeData *md = e->mimeData();
659
bool decoded = md->hasText();
660
QString text = md->text();
661
if ( decoded && text.startsWith( QLatin1String( "file:" ) ) ) {
662
const KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( mIncidence );
663
incidence->addAttachment( KCalCore::Attachment::Ptr( new KCalCore::Attachment( text ) ) );
667
KABC::Addressee::List list;
669
if ( KABC::VCardDrag::fromMimeData( md, list ) ) {
670
Q_FOREACH( const KABC::Addressee &addressee, list ) {
671
QString em( addressee.fullEmail() );
672
if ( em.isEmpty() ) {
673
em = addressee.realName();
683
QList<AgendaItem::QPtr> &AgendaItem::conflictItems()
685
return mConflictItems;
688
void AgendaItem::setConflictItems( QList<AgendaItem::QPtr> ci )
691
QList<AgendaItem::QPtr>::iterator it;
692
for ( it = mConflictItems.begin(); it != mConflictItems.end(); ++it ) {
693
(*it)->addConflictItem( this );
697
void AgendaItem::addConflictItem( AgendaItem::QPtr ci )
699
if ( !mConflictItems.contains( ci ) ) {
700
mConflictItems.append( ci );
704
QString AgendaItem::label() const
709
bool AgendaItem::overlaps( CellItem *o ) const
711
AgendaItem::QPtr other = static_cast<AgendaItem*>( o );
713
if ( cellXLeft() <= other->cellXRight() && cellXRight() >= other->cellXLeft() ) {
714
if ( ( cellYTop() <= other->cellYBottom() ) && ( cellYBottom() >= other->cellYTop() ) ) {
722
static void conditionalPaint( QPainter *p, bool condition, int &x, int y,
723
int ft, const QPixmap &pxmp )
726
p->drawPixmap( x, y, pxmp );
727
x += pxmp.width() + ft;
731
void AgendaItem::paintIcon( QPainter *p, int &x, int y, int ft )
734
Incidence::Ptr incidence = mIncidence.payload<KCalCore::Incidence::Ptr>();
735
if ( incidence->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
736
mSpecialEvent = true;
737
iconName = "view-calendar-wedding-anniversary";
738
} else if ( incidence->customProperty( "KABC", "BIRTHDAY" ) == "YES" ) {
739
mSpecialEvent = true;
740
// We don't draw icon. The icon is drawn already, because it's the Akonadi::Collection's icon
743
conditionalPaint( p, !iconName.isEmpty(), x, y, ft, cachedSmallIcon( iconName ) );
746
void AgendaItem::paintIcons( QPainter *p, int &x, int y, int ft )
748
if ( !mEventView->preferences()->enableAgendaItemIcons() ) {
752
paintIcon( p, x, y, ft );
754
QSet<EventView::ItemIcon> icons = mEventView->preferences()->agendaViewIcons();
756
if ( icons.contains( EventViews::EventView::CalendarCustomIcon ) ) {
757
const QString iconName = EventView::iconForItem( mIncidence );
758
if ( !iconName.isEmpty() && iconName != "view-calendar" && iconName != "office-calendar" ) {
759
conditionalPaint( p, true, x, y, ft, SmallIcon( iconName ) );
763
Incidence::Ptr incidence = mIncidence.payload<KCalCore::Incidence::Ptr>();
764
const bool isTodo = incidence && incidence->type() == Incidence::TypeTodo;
766
if ( isTodo && icons.contains( EventViews::EventView::TaskIcon ) ) {
767
KDateTime occurrenceDateTime = incidence->dateTime( Incidence::RoleRecurrenceStart );
768
occurrenceDateTime.setDate( mDate );
769
const QString iconName = incidence->iconName( occurrenceDateTime );
770
conditionalPaint( p, !mSpecialEvent, x, y, ft, SmallIcon( iconName ) );
773
if ( icons.contains( EventView::RecurringIcon ) )
774
conditionalPaint( p, mIconRecur && !mSpecialEvent, x, y, ft, *recurPxmp );
776
if ( icons.contains( EventView::ReminderIcon ) )
777
conditionalPaint( p, mIconAlarm && !mSpecialEvent, x, y, ft, *alarmPxmp );
779
if ( icons.contains( EventView::ReadOnlyIcon ) )
780
conditionalPaint( p, mIconReadonly && !mSpecialEvent, x, y, ft, *readonlyPxmp );
782
if ( icons.contains( EventView::ReplyIcon ) )
783
conditionalPaint( p, mIconReply, x, y, ft, *replyPxmp );
785
if ( icons.contains( EventView::AttendingIcon ) )
786
conditionalPaint( p, mIconGroup, x, y, ft, *groupPxmp );
788
if ( icons.contains( EventView::TentativeIcon ) )
789
conditionalPaint( p, mIconGroupTent, x, y, ft, *groupPxmpTent );
791
if ( icons.contains( EventView::OrganizerIcon ) )
792
conditionalPaint( p, mIconOrganizer, x, y, ft, *organizerPxmp );
795
void AgendaItem::paintEvent( QPaintEvent *ev )
801
QRect visRect = visibleRegion().boundingRect();
802
// when scrolling horizontally in the side-by-side view, the repainted area is clipped
803
// to the newly visible area, which is a problem since the content changes when visRect
804
// changes, so repaint the full item in that case
805
if ( ev->rect() != visRect && visRect.isValid() && ev->rect().isValid() ) {
811
p.setRenderHint( QPainter::Antialiasing );
812
const int fmargin = 0; // frame margin
813
const int ft = 1; // frame thickness for layout, see drawRoundedRect(),
814
// keep multiple of 2
815
const int margin = 5 + ft + fmargin ; // frame + space between frame and content
817
// General idea is to always show the icons (even in the all-day events).
818
// This creates a consistent feeling for the user when the view mode
819
// changes and therefore the available width changes.
820
// Also look at #17984
823
alarmPxmp = new QPixmap( SmallIcon( "task-reminder" ) );
824
recurPxmp = new QPixmap( SmallIcon( "appointment-recurring" ) );
825
readonlyPxmp = new QPixmap( SmallIcon( "object-locked" ) );
826
replyPxmp = new QPixmap( SmallIcon( "mail-reply-sender" ) );
827
groupPxmp = new QPixmap( SmallIcon( "meeting-attending" ) );
828
groupPxmpTent = new QPixmap( SmallIcon( "meeting-attending-tentative" ) );
829
organizerPxmp = new QPixmap( SmallIcon( "meeting-organizer" ) );
834
if ( CalendarSupport::hasTodo( mIncidence ) &&
835
!mEventView->preferences()->todosUseCategoryColors() ) {
836
Todo::Ptr todo = CalendarSupport::todo( mIncidence );
838
const QDate dueDate = todo->dtDue().toTimeSpec( CalendarSupport::KCalPrefs::instance()->timeSpec() ).date();
839
const QDate today = KDateTime::currentDateTime( CalendarSupport::KCalPrefs::instance()->timeSpec() ).date();
840
if ( todo->isOverdue() && today >= mDate ) {
841
bgColor = mEventView->preferences()->todoOverdueColor();
842
} else if ( dueDate == today && dueDate == mDate ) {
843
bgColor = mEventView->preferences()->todoDueTodayColor();
847
const KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( mIncidence );
848
Q_ASSERT( incidence );
849
QColor categoryColor;
850
const QStringList categories = incidence->categories();
852
if ( !categories.isEmpty() ) {
853
cat = categories.first();
856
categoryColor = cat.isEmpty() ? CalendarSupport::KCalPrefs::instance()->unsetCategoryColor() :
857
CalendarSupport::KCalPrefs::instance()->categoryColor( cat );
859
QColor resourceColor = mResourceColor;
860
if ( !resourceColor.isValid() ) {
861
resourceColor = categoryColor;
865
// TODO PrefsBase enums should probably be redefined in Prefs
866
if ( mEventView->preferences()->agendaViewColors() == PrefsBase::ResourceOnly ||
867
mEventView->preferences()->agendaViewColors() == PrefsBase::CategoryInsideResourceOutside ) {
868
frameColor = bgColor.isValid() ? bgColor : resourceColor;
870
frameColor = bgColor.isValid() ? bgColor : categoryColor;
873
if ( !bgColor.isValid() ) {
874
if ( mEventView->preferences()->agendaViewColors() == PrefsBase::ResourceOnly ||
875
mEventView->preferences()->agendaViewColors() == PrefsBase::ResourceInsideCategoryOutside ) {
876
bgColor = resourceColor;
878
bgColor = categoryColor;
882
if ( cat.isEmpty() &&
883
mEventView->preferences()->agendaViewColors() == PrefsBase::ResourceInsideCategoryOutside ) {
884
frameColor = bgColor;
887
if ( cat.isEmpty() &&
888
mEventView->preferences()->agendaViewColors() == PrefsBase::CategoryInsideResourceOutside ) {
889
bgColor = frameColor;
892
frameColor = EventView::itemFrameColor( frameColor, mSelected );
894
if ( !CalendarSupport::KCalPrefs::instance()->hasCategoryColor( cat ) ) {
895
categoryColor = resourceColor;
898
if ( !bgColor.isValid() ) {
899
bgColor = categoryColor;
903
bgColor = bgColor.light( EventView::BRIGHTNESS_FACTOR );
906
const QColor textColor = EventViews::getTextColor( bgColor );
907
p.setPen( textColor );
909
p.setFont( mEventView->preferences()->agendaViewFont() );
910
QFontMetrics fm = p.fontMetrics();
912
const int singleLineHeight = fm.boundingRect( mLabelText ).height();
914
const bool roundTop = !prevMultiItem();
915
const bool roundBottom = !nextMultiItem();
917
drawRoundedRect( &p, QRect( fmargin, fmargin, width() - fmargin * 2, height() - fmargin * 2 ),
918
mSelected, bgColor, true, ft, roundTop, roundBottom );
920
// calculate the height of the full version (case 4) to test whether it is
925
if ( !isMultiItem() ) {
926
// TODO: When we depend on kdepimlibs 4.7, replace with: shortH
927
// KGlobal::locale()->formatTime(incidence->dateTime( KCalCore::Incidence::RoleDisplayStart ).toTimeSpec( mEventView->preferences()->timeSpec() ).time() )
928
if ( incidence->type() == Incidence::TypeEvent ) {
929
shortH = KGlobal::locale()->formatTime( incidence->dtStart().toTimeSpec( mEventView->preferences()->timeSpec() ).time() );
930
} else if ( incidence->type() == Incidence::TypeTodo ) {
931
Todo::Ptr todo = CalendarSupport::todo( mIncidence );
933
shortH = KGlobal::locale()->formatTime( todo->dtDue().toTimeSpec( mEventView->preferences()->timeSpec() ).time() );
935
if ( CalendarSupport::hasEvent( mIncidence ) ) {
936
longH = i18n( "%1 - %2",
938
KGlobal::locale()->formatTime(
939
incidence->dateTime( KCalCore::Incidence::RoleEnd ).toTimeSpec(
940
mEventView->preferences()->timeSpec() ).time() ) );
944
} else if ( !mMultiItemInfo->mFirstMultiItem ) {
945
shortH = KGlobal::locale()->formatTime(
946
incidence->dtStart().toTimeSpec( mEventView->preferences()->timeSpec() ).time() );
949
shortH = KGlobal::locale()->formatTime(
950
incidence->dateTime( KCalCore::Incidence::RoleEnd ).toTimeSpec(
951
mEventView->preferences()->timeSpec() ).time() );
952
longH = i18n( "- %1", shortH );
955
KWordWrap *ww = KWordWrap::formatText(
956
fm, QRect( 0, 0, width() - ( 2 * margin ), -1 ), 0, mLabelText );
957
int th = ww->boundingRect().height();
960
int hlHeight = qMax( fm.boundingRect( longH ).height(),
961
qMax( alarmPxmp->height(),
962
qMax( recurPxmp->height(),
963
qMax( readonlyPxmp->height(),
964
qMax( replyPxmp->height(),
965
qMax( groupPxmp->height(),
966
organizerPxmp->height() ) ) ) ) ) );
968
const bool completelyRenderable = th < ( height() - 2 * ft - 2 - hlHeight );
970
// case 1: do not draw text when not even a single line fits
971
// Don't do this any more, always try to print out the text.
972
// Even if it's just a few pixel, one can still guess the whole
973
// text from just four pixels' height!
974
if ( //( singleLineHeight > height() - 4 ) ||
976
int x = qRound( ( width() - 16 ) / 2.0 );
977
paintIcon( &p, x/*by-ref*/, margin, ft );
981
// case 2: draw a single line when no more space
982
if ( ( 2 * singleLineHeight ) > ( height() - 2 * margin ) ) {
983
int x = margin, txtWidth;
985
if ( incidence->allDay() ) {
987
const int y = qRound( ( height() - 16 ) / 2.0 );
988
paintIcons( &p, x, y, ft );
989
txtWidth = visRect.right() - margin - x;
991
const int y = qRound( ( height() - 16 ) / 2.0 );
992
paintIcons( &p, x, y, ft );
993
txtWidth = width() - margin - x;
996
const int y = ( ( height() - singleLineHeight ) / 2 ) + fm.ascent();
997
KWordWrap::drawFadeoutText( &p, x, y, txtWidth, mLabelText );
1001
// case 3: enough for 2-5 lines, but not for the header.
1002
// Also used for the middle days in multi-events
1003
if ( ( ( !completelyRenderable ) &&
1004
( ( height() - ( 2 * margin ) ) <= ( 5 * singleLineHeight ) ) ) ||
1005
( isMultiItem() && mMultiItemInfo->mNextMultiItem && mMultiItemInfo->mFirstMultiItem ) ) {
1006
int x = margin, txtWidth;
1008
if ( incidence->allDay() ) {
1009
x += visRect.left();
1010
paintIcons( &p, x, margin, ft );
1011
txtWidth = visRect.right() - margin - x;
1013
paintIcons( &p, x, margin, ft );
1014
txtWidth = width() - margin - x;
1017
ww = KWordWrap::formatText(
1018
fm, QRect( 0, 0, txtWidth, ( height() - ( 2 * margin ) ) ), 0, mLabelText );
1020
ww->drawText( &p, x, margin, Qt::AlignHCenter | KWordWrap::FadeOut );
1025
// case 4: paint everything, with header:
1026
// consists of (vertically) ft + headline&icons + ft + text + margin
1027
int y = 2 * ft + hlHeight;
1028
if ( completelyRenderable ) {
1029
y += ( height() - ( 2 * ft ) - margin - hlHeight - th ) / 2;
1032
int x = margin, txtWidth, hTxtWidth, eventX;
1034
if ( incidence->allDay() ) {
1035
shortH = longH = "";
1037
if ( const KCalCore::Event::Ptr event = CalendarSupport::event( mIncidence ) ) {
1038
if ( event->isMultiDay( mEventView->preferences()->timeSpec() ) ) {
1039
// multi-day, all-day event
1042
KGlobal::locale()->formatDate(
1043
incidence->dtStart().toTimeSpec( mEventView->preferences()->timeSpec() ).date() ),
1044
KGlobal::locale()->formatDate(
1045
incidence->dateTime( KCalCore::Incidence::RoleEnd ).toTimeSpec(
1046
mEventView->preferences()->timeSpec() ).date() ) );
1052
QRect( fmargin, fmargin, width() - fmargin * 2, - fmargin * 2 + margin + hlHeight ),
1053
mSelected, frameColor, false, ft, roundTop, false );
1055
// single-day, all-day event
1060
QRect( fmargin, fmargin, width() - fmargin * 2, - fmargin * 2 + margin + hlHeight ),
1061
mSelected, frameColor, false, ft, roundTop, false );
1069
QRect( fmargin, fmargin, width() - fmargin * 2, - fmargin * 2 + margin + hlHeight ),
1070
mSelected, frameColor, false, ft, roundTop, false );
1073
x += visRect.left();
1075
txtWidth = visRect.right() - margin - x;
1076
paintIcons( &p, x, margin, ft );
1077
hTxtWidth = visRect.right() - margin - x;
1082
QRect( fmargin, fmargin, width() - fmargin * 2, - fmargin * 2 + margin + hlHeight ),
1083
mSelected, frameColor, false, ft, roundTop, false );
1085
txtWidth = width() - margin - x;
1087
paintIcons( &p, x, margin / 2, ft );
1088
hTxtWidth = width() - margin - x;
1092
int hw = fm.boundingRect( longH ).width();
1093
if ( hw > hTxtWidth ) {
1095
hw = fm.boundingRect( shortH ).width();
1096
if ( hw < txtWidth ) {
1097
x += ( hTxtWidth - hw ) / 2;
1101
x += ( hTxtWidth - hw ) / 2;
1103
p.setBackground( QBrush( frameColor ) );
1104
p.setPen( EventViews::getTextColor( frameColor ) );
1105
KWordWrap::drawFadeoutText( &p, x, ( margin + hlHeight + fm.ascent() ) / 2 - 2,
1106
hTxtWidth, headline );
1109
ww = KWordWrap::formatText(
1110
fm, QRect( 0, 0, txtWidth, height() - margin - y ), 0, mLabelText );
1112
p.setBackground( QBrush( bgColor ) );
1113
p.setPen( textColor );
1114
QString ws = ww->wrappedString();
1115
if ( ws.left( ws.length()-1 ).indexOf( '\n' ) >= 0 ) {
1116
ww->drawText( &p, eventX, y, Qt::AlignLeft | KWordWrap::FadeOut );
1118
ww->drawText( &p, eventX + ( txtWidth - ww->boundingRect().width() - 2 * margin ) / 2, y,
1119
Qt::AlignHCenter | KWordWrap::FadeOut );
1125
void AgendaItem::drawRoundedRect( QPainter *p, const QRect &rect,
1126
bool selected, const QColor &bgColor,
1127
bool frame, int ft, bool roundTop,
1136
r.adjust( 0, 0, 1, 1 );
1142
bool shrinkWidth = r.width() < 16;
1143
bool shrinkHeight = r.height() < 16;
1146
int sw = shrinkWidth ? 10 : 11;
1147
int sh = shrinkHeight ? 10 : 11;
1148
QRectF tr( r.x() + r.width() - sw - rnd, r.y() + rnd, sw, sh );
1149
QRectF tl( r.x() + rnd, r.y() + rnd, sw, sh );
1150
QRectF bl( r.x() + rnd, r.y() + r.height() - sh - 1 - rnd, sw, sh );
1151
QRectF br( r.x() + r.width() - sw - rnd, r.y() + r.height() - sh - 1 - rnd, sw, sh );
1154
path.moveTo( tr.topRight() );
1155
path.arcTo( tr, 0.0, 90.0 );
1156
path.lineTo( tl.topRight() );
1157
path.arcTo( tl, 90.0, 90.0 );
1159
path.moveTo( tr.topRight() );
1160
path.lineTo( tl.topLeft() );
1164
path.lineTo( bl.topLeft() );
1165
path.arcTo( bl, 180.0, 90.0 );
1166
path.lineTo( br.bottomLeft() );
1167
path.arcTo( br, 270.0, 90.0 );
1169
path.lineTo( bl.bottomLeft() );
1170
path.lineTo( br.bottomRight() );
1172
path.closeSubpath();
1176
QLinearGradient gradient( QPointF( r.x(), r.y() ), QPointF( r.x(), r.height() ) );
1179
QColor top = bgColor.dark( 250 );
1181
gradient.setColorAt( 0, top );
1182
gradient.setColorAt( 1, QColor( 255, 255, 255, 30 ) );
1184
gradient.setColorAt( 0, QColor( 255, 255, 255, 90 ) );
1185
gradient.setColorAt( 1, QColor( 0, 0, 0, 10 ) );
1188
p->setBrush( bgColor );
1189
p->setPen( Qt::NoPen );
1190
p->drawPath( path );
1192
p->setBrush( gradient );
1193
p->setPen( Qt::NoPen );
1194
p->drawPath( path );
1197
QString key( "ko_hsep" );
1198
if ( !QPixmapCache::find( key, separator ) ) {
1199
separator = QPixmap( ":/headerSeparator.png" );
1200
QPixmapCache::insert( key, separator );
1202
p->fillRect( QRect( r.x() + 3, r.y() + r.height() - 2, r.x() + r.width() - 4, 2 ),
1203
QBrush( separator ) );
1209
QLinearGradient gradient( QPointF( r.x(), r.y() ), QPointF( r.x(), r.height() ) );
1211
const KCalCore::Incidence::Ptr incidence = CalendarSupport::incidence( mIncidence );
1212
Q_ASSERT( incidence );
1214
if ( r.height() > 50 ) {
1215
if ( incidence->allDay() &&
1216
incidence->dtStart() == incidence->dateTime( KCalCore::Incidence::RoleEnd ) &&
1217
CalendarSupport::hasEvent( mIncidence ) ) {
1218
gradient.setColorAt( 0, bgColor.light( 130 ) );
1219
qreal t = 1.0 - ( r.height() - 18.0 ) / r.height();
1220
gradient.setColorAt( t, bgColor.light( 115 ) );
1221
qreal b = ( r.height() - 20.0 ) / r.height();
1222
gradient.setColorAt( b, bgColor );
1224
gradient.setColorAt( 0, bgColor.light( 115 ) );
1225
qreal b = ( r.height() - 20.0 ) / r.height();
1226
gradient.setColorAt( b, bgColor );
1228
gradient.setColorAt( 1, bgColor.dark( 110 ) );
1230
if ( incidence->allDay() &&
1231
incidence->dtStart() == incidence->dateTime( KCalCore::Incidence::RoleEnd ) &&
1232
!CalendarSupport::hasTodo( mIncidence ) ) {
1233
gradient.setColorAt( 0, bgColor.light( 130 ) );
1234
gradient.setColorAt( 0.35, bgColor.light( 115 ) );
1235
gradient.setColorAt( 0.65, bgColor );
1237
gradient.setColorAt( 0, bgColor.light( 115 ) );
1238
gradient.setColorAt( 0.65, bgColor );
1240
gradient.setColorAt( 1, bgColor.dark( 110 ) );
1243
p->setBrush( gradient );
1244
p->setPen( Qt::NoPen );
1245
p->drawPath( path );
1247
p->setRenderHint( QPainter::Antialiasing, false );
1249
if ( r.width() - 16 > 0 ) {
1251
QString key( "ko_t" );
1252
if ( !QPixmapCache::find( key, topLines ) ) {
1253
topLines = QPixmap( ":/topLines.png" );
1254
QPixmapCache::insert( key, topLines );
1256
p->setBrushOrigin( r.x() + 8, r.y() );
1257
p->fillRect( QRect( r.x() + 8, r.y(), r.width() - 16, 5 ),
1258
QBrush( topLines ) );
1260
QPixmap bottomLines;
1261
key = QString( "ko_b" );
1262
if ( !QPixmapCache::find( key, bottomLines ) ) {
1263
bottomLines = QPixmap( ":/bottomLines.png" );
1264
QPixmapCache::insert( key, bottomLines );
1266
p->setBrushOrigin( r.x() + 8, r.y() + r.height() - 6 );
1267
p->fillRect( QRect( r.x() + 8, r.y() + r.height() - 6, r.width() - 16, 6 ),
1268
QBrush( bottomLines ) );
1272
if ( r.height() - 16 > 0 ) {
1275
QString key( "ko_l" );
1276
if ( !QPixmapCache::find( key, leftLines ) ) {
1277
leftLines = QPixmap( ":/leftLines.png" );
1278
QPixmapCache::insert( key, leftLines );
1280
p->setBrushOrigin( r.x(), r.y() + 8 );
1281
p->fillRect( QRect( r.x(), r.y() + 8, 5, r.height() - 16 ),
1282
QBrush( leftLines ) );
1285
key = QString( "ko_r" );
1286
if ( !QPixmapCache::find( key, rightLines ) ) {
1287
rightLines = QPixmap( ":/rightLines.png" );
1288
QPixmapCache::insert( key, rightLines );
1290
p->setBrushOrigin( r.x() + r.width() - 5, r.y() + 8 );
1291
p->fillRect( QRect( r.x() + r.width() - 5, r.y() + 8, 5, r.height() - 16 ),
1292
QBrush( rightLines ) );
1295
// don't overlap the edges
1296
int lw = shrinkWidth ? r.width() / 2 : 8;
1297
int rw = shrinkWidth ? r.width() - lw : 8;
1298
int th = shrinkHeight ? r.height() / 2 : 8;
1299
int bh = shrinkHeight ? r.height() - th : 8;
1301
// keep the bottom round for items which ending at 00:15
1302
if( shrinkHeight && !roundTop && roundBottom && r.height() > 3 ) {
1308
QString key = roundTop ? QString( "ko_tl" ) : QString( "ko_rtl" );
1309
if ( !QPixmapCache::find( key, topLeft ) ) {
1310
topLeft = roundTop ? QPixmap( ":/roundTopLeft.png" ) : QPixmap( ":/rectangularTopLeft.png" );
1311
QPixmapCache::insert( key, topLeft );
1313
p->drawPixmap( r.x(), r.y(), topLeft, 0, 0, lw, th );
1316
key = roundTop ? QString( "ko_tr" ) : QString( "ko_rtr" );
1317
if ( !QPixmapCache::find( key, topRight ) ) {
1318
topRight = roundTop ? QPixmap( ":/roundTopRight.png" ) : QPixmap( ":/rectangularTopRight.png" );
1319
QPixmapCache::insert( key, topRight );
1321
p->drawPixmap( r.x() + r.width() - rw, r.y(), topRight, 8 - rw, 0, rw, th );
1324
key = roundBottom ? QString( "ko_bl" ) : QString( "ko_rbl" );
1325
if ( !QPixmapCache::find( key, bottomLeft ) ) {
1326
bottomLeft = roundBottom ? QPixmap( ":/roundBottomLeft.png" ) :
1327
QPixmap( ":/rectangularBottomLeft.png" );
1328
QPixmapCache::insert( key, bottomLeft );
1330
p->drawPixmap( r.x(), r.y() + r.height() - bh, bottomLeft, 0, 8 - bh, lw, bh );
1332
QPixmap bottomRight;
1333
key = roundBottom ? QString( "ko_br" ) : QString( "ko_rbr" );
1334
if ( !QPixmapCache::find( key, bottomRight ) ) {
1335
bottomRight = roundBottom ? QPixmap( ":/roundBottomRight.png" ) :
1336
QPixmap( ":/rectangularBottomRight.png" );
1337
QPixmapCache::insert( key, bottomRight );
1339
p->drawPixmap( r.x() + r.width() - rw, r.y() + r.height() - bh, bottomRight,
1340
8 - rw, 8 - bh, rw, 8 );
1345
bool AgendaItem::eventFilter( QObject *obj, QEvent *event )
1347
if ( event->type() == QEvent::Paint ) {
1350
// standard event processing
1351
return QObject::eventFilter( obj, event );
1355
bool AgendaItem::event( QEvent *event )
1357
if ( event->type() == QEvent::ToolTip ) {
1358
if( !mEventView->preferences()->enableToolTips() ) {
1360
} else if ( mValid ) {
1361
QHelpEvent *helpEvent = static_cast<QHelpEvent*>( event );
1363
helpEvent->globalPos(),
1364
KCalUtils::IncidenceFormatter::toolTipStr(
1365
CalendarSupport::displayName( mIncidence.parentCollection() ),
1366
CalendarSupport::incidence( mIncidence ),
1367
mDate, true, mEventView->preferences()->timeSpec() ),
1371
return QWidget::event( event );
1374
#include "agendaitem.moc"