You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdepim/korganizer/koagendaitem.cpp

998 lines
30 KiB

/*
This file is part of KOrganizer.
Copyright (c) 2000,2001,2003 Cornelius Schumacher <schumacher@kde.org>
Copyright (C) 2003-2004 Reinhold Kainhofer <reinhold@kainhofer.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
As a special exception, permission is given to link this program
with any edition of TQt, and distribute the resulting executable,
without including the source code for TQt in the source distribution.
*/
#include <tqtooltip.h>
#include <tqdragobject.h>
#include <tqpainter.h>
#include <kiconloader.h>
#include <kdebug.h>
#include <tdelocale.h>
#include <kwordwrap.h>
#include <tdemessagebox.h>
#include <libkcal/icaldrag.h>
#include <libkcal/vcaldrag.h>
#include <libtdepim/kvcarddrag.h>
#include <libemailfunctions/email.h>
#ifndef KORG_NOKABC
#include <tdeabc/addressee.h>
#include <tdeabc/vcardconverter.h>
#endif
#include "koprefs.h"
#include "koglobals.h"
#include "koincidencetooltip.h"
#include "koagendaitem.h"
#include "koagendaitem.moc"
//--------------------------------------------------------------------------
TQToolTipGroup *KOAgendaItem::mToolTipGroup = 0;
TQPixmap *KOAgendaItem::alarmPxmp = 0;
TQPixmap *KOAgendaItem::recurPxmp = 0;
TQPixmap *KOAgendaItem::readonlyPxmp = 0;
TQPixmap *KOAgendaItem::replyPxmp = 0;
TQPixmap *KOAgendaItem::groupPxmp = 0;
TQPixmap *KOAgendaItem::groupPxmpTentative = 0;
TQPixmap *KOAgendaItem::organizerPxmp = 0;
//--------------------------------------------------------------------------
KOAgendaItem::KOAgendaItem( Calendar *calendar, Incidence *incidence,
const TQDate &qd, TQWidget *parent,
int itemPos, int itemCount,
const char *name, WFlags f ) :
TQWidget( parent, name, f ), mCalendar( calendar ), mIncidence( incidence ), mDate( qd ),
mLabelText( mIncidence->summary() ), mIconAlarm( false ),
mIconRecur( false ), mIconReadonly( false ), mIconReply( false ),
mIconGroup( false ), mIconGroupTentative( false ), mIconOrganizer( false ),
mSpecialEvent( false ),
mItemPos( itemPos ), mItemCount( itemCount ),
mMultiItemInfo( 0 ), mStartMoveInfo( 0 )
{
setBackgroundMode( TQt::NoBackground );
setCellXY( 0, 0, 1 );
setCellXRight( 0 );
setMouseTracking( true );
mResourceColor = TQColor();
updateIcons();
// select() does nothing, if state hasn't change, so preset mSelected.
mSelected = true;
select( false );
KOIncidenceToolTip::add( this, mCalendar, incidence, mDate, toolTipGroup() );
setAcceptDrops( true );
}
void KOAgendaItem::updateIcons()
{
if ( !mIncidence ) return;
mIconReadonly = mIncidence->isReadOnly();
mIconRecur = mIncidence->doesRecur();
mIconAlarm = mIncidence->isAlarmEnabled();
if ( mIncidence->attendeeCount() > 1 ) {
if ( KOPrefs::instance()->thatIsMe( mIncidence->organizer().email() ) ) {
mIconReply = false;
mIconGroup = false;
mIconGroupTentative = false;
mIconOrganizer = true;
} else {
Attendee *me = mIncidence->attendeeByMails( KOPrefs::instance()->allEmails() );
if ( me ) {
if ( me->status() == Attendee::NeedsAction && me->RSVP() ) {
mIconReply = true;
mIconGroup = false;
mIconGroupTentative = false;
mIconOrganizer = false;
} else if ( me->status() == Attendee::Tentative ) {
mIconReply = false;
mIconGroup = false;
mIconGroupTentative = true;
mIconOrganizer = false;
} else {
mIconReply = false;
mIconGroup = true;
mIconGroupTentative = false;
mIconOrganizer = false;
}
} else {
mIconReply = false;
mIconGroup = true;
mIconGroupTentative = false;
mIconOrganizer = false;
}
}
}
update();
}
void KOAgendaItem::select( bool selected )
{
if ( mSelected == selected ) return;
mSelected = selected;
update();
}
bool KOAgendaItem::dissociateFromMultiItem()
{
if ( !isMultiItem() ) return false;
KOAgendaItem *firstItem = firstMultiItem();
if ( firstItem == this ) firstItem = nextMultiItem();
KOAgendaItem *lastItem = lastMultiItem();
if ( lastItem == this ) lastItem = prevMultiItem();
KOAgendaItem *prevItem = prevMultiItem();
KOAgendaItem *nextItem = nextMultiItem();
if ( prevItem ) {
prevItem->setMultiItem( firstItem,
prevItem->prevMultiItem(),
nextItem, lastItem );
}
if ( nextItem ) {
nextItem->setMultiItem( firstItem, prevItem,
nextItem->prevMultiItem(),
lastItem );
}
delete mMultiItemInfo;
mMultiItemInfo = 0;
return true;
}
bool KOAgendaItem::setIncidence( Incidence *i )
{
mIncidence = i;
updateIcons();
return true;
}
/*
Return height of item in units of agenda cells
*/
int KOAgendaItem::cellHeight() const
{
return mCellYBottom - mCellYTop + 1;
}
/*
Return height of item in units of agenda cells
*/
int KOAgendaItem::cellWidth() const
{
return mCellXRight - mCellXLeft + 1;
}
void KOAgendaItem::setItemDate( const TQDate &qd )
{
mDate = qd;
}
void KOAgendaItem::setCellXY( int X, int YTop, int YBottom )
{
mCellXLeft = X;
mCellYTop = YTop;
mCellYBottom = YBottom;
}
void KOAgendaItem::setCellXRight( int xright )
{
mCellXRight = xright;
}
void KOAgendaItem::setCellX( int XLeft, int XRight )
{
mCellXLeft = XLeft;
mCellXRight = XRight;
}
void KOAgendaItem::setCellY( int YTop, int YBottom )
{
mCellYTop = YTop;
mCellYBottom = YBottom;
}
void KOAgendaItem::setMultiItem( KOAgendaItem *first, KOAgendaItem *prev,
KOAgendaItem *next, KOAgendaItem *last )
{
if ( !mMultiItemInfo ) {
mMultiItemInfo = new MultiItemInfo;
}
mMultiItemInfo->mFirstMultiItem = first;
mMultiItemInfo->mPrevMultiItem = prev;
mMultiItemInfo->mNextMultiItem = next;
mMultiItemInfo->mLastMultiItem = last;
}
bool KOAgendaItem::isMultiItem()
{
return mMultiItemInfo;
}
KOAgendaItem* KOAgendaItem::prependMoveItem(KOAgendaItem* e)
{
if (!e) return e;
KOAgendaItem*first=0, *last=0;
if (isMultiItem()) {
first=mMultiItemInfo->mFirstMultiItem;
last=mMultiItemInfo->mLastMultiItem;
}
if (!first) first=this;
if (!last) last=this;
e->setMultiItem(0, 0, first, last);
first->setMultiItem(e, e, first->nextMultiItem(), first->lastMultiItem() );
KOAgendaItem*tmp=first->nextMultiItem();
while (tmp) {
tmp->setMultiItem( e, tmp->prevMultiItem(), tmp->nextMultiItem(), tmp->lastMultiItem() );
tmp = tmp->nextMultiItem();
}
if ( mStartMoveInfo && !e->moveInfo() ) {
e->mStartMoveInfo=new MultiItemInfo( *mStartMoveInfo );
// e->moveInfo()->mFirstMultiItem = moveInfo()->mFirstMultiItem;
// e->moveInfo()->mLastMultiItem = moveInfo()->mLastMultiItem;
e->moveInfo()->mPrevMultiItem = 0;
e->moveInfo()->mNextMultiItem = first;
}
if (first && first->moveInfo()) {
first->moveInfo()->mPrevMultiItem = e;
}
return e;
}
KOAgendaItem* KOAgendaItem::appendMoveItem(KOAgendaItem* e)
{
if (!e) return e;
KOAgendaItem*first=0, *last=0;
if (isMultiItem()) {
first=mMultiItemInfo->mFirstMultiItem;
last=mMultiItemInfo->mLastMultiItem;
}
if (!first) first=this;
if (!last) last=this;
e->setMultiItem( first, last, 0, 0 );
KOAgendaItem*tmp=first;
while (tmp) {
tmp->setMultiItem(tmp->firstMultiItem(), tmp->prevMultiItem(), tmp->nextMultiItem(), e);
tmp = tmp->nextMultiItem();
}
last->setMultiItem( last->firstMultiItem(), last->prevMultiItem(), e, e);
if ( mStartMoveInfo && !e->moveInfo() ) {
e->mStartMoveInfo=new MultiItemInfo( *mStartMoveInfo );
// e->moveInfo()->mFirstMultiItem = moveInfo()->mFirstMultiItem;
// e->moveInfo()->mLastMultiItem = moveInfo()->mLastMultiItem;
e->moveInfo()->mPrevMultiItem = last;
e->moveInfo()->mNextMultiItem = 0;
}
if (last && last->moveInfo()) {
last->moveInfo()->mNextMultiItem = e;
}
return e;
}
KOAgendaItem* KOAgendaItem::removeMoveItem(KOAgendaItem* e)
{
if (isMultiItem()) {
KOAgendaItem *first = mMultiItemInfo->mFirstMultiItem;
KOAgendaItem *next, *prev;
KOAgendaItem *last = mMultiItemInfo->mLastMultiItem;
if (!first) first = this;
if (!last) last = this;
if ( first==e ) {
first = first->nextMultiItem();
first->setMultiItem( 0, 0, first->nextMultiItem(), first->lastMultiItem() );
}
if ( last==e ) {
last=last->prevMultiItem();
last->setMultiItem( last->firstMultiItem(), last->prevMultiItem(), 0, 0 );
}
KOAgendaItem *tmp = first;
if ( first==last ) {
delete mMultiItemInfo;
tmp = 0;
mMultiItemInfo = 0;
}
while ( tmp ) {
next = tmp->nextMultiItem();
prev = tmp->prevMultiItem();
if ( e==next ) {
next = next->nextMultiItem();
}
if ( e==prev ) {
prev = prev->prevMultiItem();
}
tmp->setMultiItem((tmp==first)?0:first, (tmp==prev)?0:prev, (tmp==next)?0:next, (tmp==last)?0:last);
tmp = tmp->nextMultiItem();
}
}
return e;
}
void KOAgendaItem::startMove()
{
KOAgendaItem* first = this;
if ( isMultiItem() && mMultiItemInfo->mFirstMultiItem ) {
first=mMultiItemInfo->mFirstMultiItem;
}
first->startMovePrivate();
}
void KOAgendaItem::startMovePrivate()
{
mStartMoveInfo = new MultiItemInfo;
mStartMoveInfo->mStartCellXLeft = mCellXLeft;
mStartMoveInfo->mStartCellXRight = mCellXRight;
mStartMoveInfo->mStartCellYTop = mCellYTop;
mStartMoveInfo->mStartCellYBottom = mCellYBottom;
if (mMultiItemInfo) {
mStartMoveInfo->mFirstMultiItem = mMultiItemInfo->mFirstMultiItem;
mStartMoveInfo->mLastMultiItem = mMultiItemInfo->mLastMultiItem;
mStartMoveInfo->mPrevMultiItem = mMultiItemInfo->mPrevMultiItem;
mStartMoveInfo->mNextMultiItem = mMultiItemInfo->mNextMultiItem;
} else {
mStartMoveInfo->mFirstMultiItem = 0;
mStartMoveInfo->mLastMultiItem = 0;
mStartMoveInfo->mPrevMultiItem = 0;
mStartMoveInfo->mNextMultiItem = 0;
}
if ( isMultiItem() && mMultiItemInfo->mNextMultiItem )
{
mMultiItemInfo->mNextMultiItem->startMovePrivate();
}
}
void KOAgendaItem::resetMove()
{
if ( mStartMoveInfo ) {
if ( mStartMoveInfo->mFirstMultiItem ) {
mStartMoveInfo->mFirstMultiItem->resetMovePrivate();
} else {
resetMovePrivate();
}
}
}
void KOAgendaItem::resetMovePrivate()
{
if (mStartMoveInfo) {
mCellXLeft = mStartMoveInfo->mStartCellXLeft;
mCellXRight = mStartMoveInfo->mStartCellXRight;
mCellYTop = mStartMoveInfo->mStartCellYTop;
mCellYBottom = mStartMoveInfo->mStartCellYBottom;
// if we don't have mMultiItemInfo, the item didn't span two days before,
// and wasn't moved over midnight, either, so we don't have to reset
// anything. Otherwise, restore from mMoveItemInfo
if ( mMultiItemInfo ) {
// It was already a multi-day info
mMultiItemInfo->mFirstMultiItem = mStartMoveInfo->mFirstMultiItem;
mMultiItemInfo->mPrevMultiItem = mStartMoveInfo->mPrevMultiItem;
mMultiItemInfo->mNextMultiItem = mStartMoveInfo->mNextMultiItem;
mMultiItemInfo->mLastMultiItem = mStartMoveInfo->mLastMultiItem;
if ( !mStartMoveInfo->mFirstMultiItem ) {
// This was the first multi-item when the move started, delete all previous
KOAgendaItem*toDel=mStartMoveInfo->mPrevMultiItem;
KOAgendaItem*nowDel=0L;
while (toDel) {
nowDel=toDel;
if (nowDel->moveInfo()) {
toDel=nowDel->moveInfo()->mPrevMultiItem;
}
emit removeAgendaItem( nowDel );
}
mMultiItemInfo->mFirstMultiItem = 0L;
mMultiItemInfo->mPrevMultiItem = 0L;
}
if ( !mStartMoveInfo->mLastMultiItem ) {
// This was the last multi-item when the move started, delete all next
KOAgendaItem*toDel=mStartMoveInfo->mNextMultiItem;
KOAgendaItem*nowDel=0L;
while (toDel) {
nowDel=toDel;
if (nowDel->moveInfo()) {
toDel=nowDel->moveInfo()->mNextMultiItem;
}
emit removeAgendaItem( nowDel );
}
mMultiItemInfo->mLastMultiItem = 0L;
mMultiItemInfo->mNextMultiItem = 0L;
}
if ( mStartMoveInfo->mFirstMultiItem==0 && mStartMoveInfo->mLastMultiItem==0 ) {
// it was a single-day event before we started the move.
delete mMultiItemInfo;
mMultiItemInfo = 0;
}
}
delete mStartMoveInfo;
mStartMoveInfo = 0;
}
emit showAgendaItem( this );
if ( nextMultiItem() ) {
nextMultiItem()->resetMovePrivate();
}
}
void KOAgendaItem::endMove()
{
KOAgendaItem*first=firstMultiItem();
if (!first) first=this;
first->endMovePrivate();
}
void KOAgendaItem::endMovePrivate()
{
if ( mStartMoveInfo ) {
// if first, delete all previous
if ( !firstMultiItem() || firstMultiItem()==this ) {
KOAgendaItem*toDel=mStartMoveInfo->mPrevMultiItem;
KOAgendaItem*nowDel = 0;
while (toDel) {
nowDel=toDel;
if (nowDel->moveInfo()) {
toDel=nowDel->moveInfo()->mPrevMultiItem;
}
emit removeAgendaItem( nowDel );
}
}
// if last, delete all next
if ( !lastMultiItem() || lastMultiItem()==this ) {
KOAgendaItem*toDel=mStartMoveInfo->mNextMultiItem;
KOAgendaItem*nowDel = 0;
while (toDel) {
nowDel=toDel;
if (nowDel->moveInfo()) {
toDel=nowDel->moveInfo()->mNextMultiItem;
}
emit removeAgendaItem( nowDel );
}
}
// also delete the moving info
delete mStartMoveInfo;
mStartMoveInfo=0;
if ( nextMultiItem() )
nextMultiItem()->endMovePrivate();
}
}
void KOAgendaItem::moveRelative(int dx, int dy)
{
int newXLeft = cellXLeft() + dx;
int newXRight = cellXRight() + dx;
int newYTop = cellYTop() + dy;
int newYBottom = cellYBottom() + dy;
setCellXY(newXLeft,newYTop,newYBottom);
setCellXRight(newXRight);
}
void KOAgendaItem::expandTop(int dy)
{
int newYTop = cellYTop() + dy;
int newYBottom = cellYBottom();
if (newYTop > newYBottom) newYTop = newYBottom;
setCellY(newYTop, newYBottom);
}
void KOAgendaItem::expandBottom(int dy)
{
int newYTop = cellYTop();
int newYBottom = cellYBottom() + dy;
if (newYBottom < newYTop) newYBottom = newYTop;
setCellY(newYTop, newYBottom);
}
void KOAgendaItem::expandLeft(int dx)
{
int newXLeft = cellXLeft() + dx;
int newXRight = cellXRight();
if ( newXLeft > newXRight ) newXLeft = newXRight;
setCellX( newXLeft, newXRight );
}
void KOAgendaItem::expandRight(int dx)
{
int newXLeft = cellXLeft();
int newXRight = cellXRight() + dx;
if ( newXRight < newXLeft ) newXRight = newXLeft;
setCellX( newXLeft, newXRight );
}
TQToolTipGroup *KOAgendaItem::toolTipGroup()
{
if (!mToolTipGroup) mToolTipGroup = new TQToolTipGroup(0);
return mToolTipGroup;
}
void KOAgendaItem::dragEnterEvent( TQDragEnterEvent *e )
{
#ifndef KORG_NODND
if ( ICalDrag::canDecode( e ) || VCalDrag::canDecode( e ) ) {
e->ignore();
return;
}
if ( KVCardDrag::canDecode( e ) || TQTextDrag::canDecode( e ) )
e->accept();
else
e->ignore();
#endif
}
void KOAgendaItem::addAttendee( const TQString &newAttendee )
{
kdDebug(5850) << " Email: " << newAttendee << endl;
TQString name, email;
KPIM::getNameAndMail( newAttendee, name, email );
if ( !( name.isEmpty() && email.isEmpty() ) ) {
mIncidence->addAttendee(new Attendee(name,email));
KMessageBox::information( this, i18n("Attendee \"%1\" added to the calendar item \"%2\"").arg(KPIM::normalizedAddress(name, email, TQString())).arg(text()), i18n("Attendee added"), "AttendeeDroppedAdded" );
}
}
void KOAgendaItem::dropEvent( TQDropEvent *e )
{
// 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.
#ifndef KORG_NODND
TQString text;
bool decoded = TQTextDrag::decode( e, text );
if( decoded && text.startsWith( "file:" ) ) {
mIncidence->addAttachment( new Attachment( text ) );
return;
}
#ifndef KORG_NOKABC
TDEABC::Addressee::List list;
if ( KVCardDrag::decode( e, list ) ) {
TDEABC::Addressee::List::Iterator it;
for ( it = list.begin(); it != list.end(); ++it ) {
TQString em( (*it).fullEmail() );
if ( em.isEmpty() ) {
em = (*it).realName();
}
addAttendee( em );
}
}
#else
if( decoded ) {
kdDebug(5850) << "Dropped : " << text << endl;
TQStringList emails = TQStringList::split( ",", text );
for( TQStringList::ConstIterator it = emails.begin(); it != emails.end();
++it ) {
addAttendee( *it );
}
}
#endif // KORG_NOKABC
#endif // KORG_NODND
}
TQPtrList<KOAgendaItem> KOAgendaItem::conflictItems()
{
return mConflictItems;
}
void KOAgendaItem::setConflictItems( TQPtrList<KOAgendaItem> ci )
{
mConflictItems = ci;
KOAgendaItem *item;
for ( item = mConflictItems.first(); item != 0;
item = mConflictItems.next() ) {
item->addConflictItem( this );
}
}
void KOAgendaItem::addConflictItem( KOAgendaItem *ci )
{
if ( mConflictItems.find( ci ) < 0 ) mConflictItems.append( ci );
}
TQString KOAgendaItem::label() const
{
return mLabelText;
}
bool KOAgendaItem::overlaps( KOrg::CellItem *o ) const
{
KOAgendaItem *other = static_cast<KOAgendaItem *>( o );
if ( cellXLeft() <= other->cellXRight() &&
cellXRight() >= other->cellXLeft() ) {
if ( ( cellYTop() <= other->cellYBottom() ) &&
( cellYBottom() >= other->cellYTop() ) ) {
return true;
}
}
return false;
}
void KOAgendaItem::paintFrame( TQPainter *p, const TQColor &color )
{
TQColor oldpen(p->pen().color());
p->setPen( color );
p->drawRect( 0, 0, width(), height() );
p->drawRect( 1, 1, width() - 2, height() - 2 );
p->setPen( oldpen );
}
static void conditionalPaint( TQPainter *p, bool cond, int &x, int ft,
const TQPixmap &pxmp )
{
if ( !cond ) return;
p->drawPixmap( x, ft, pxmp );
x += pxmp.width() + ft;
}
void KOAgendaItem::paintEventIcon( TQPainter *p, int &x, int ft )
{
if ( !mIncidence ) return;
if ( mIncidence->type() == "Event" ) {
TQPixmap eventPxmp;
if ( mIncidence->customProperty( "KABC", "BIRTHDAY" ) == "YES" ) {
mSpecialEvent = true;
if ( mIncidence->customProperty( "KABC", "ANNIVERSARY" ) == "YES" ) {
eventPxmp = KOGlobals::self()->smallIcon( "calendaranniversary" );
} else {
eventPxmp = KOGlobals::self()->smallIcon( "calendarbirthday" );
}
conditionalPaint( p, true, x, ft, eventPxmp );
}
// per kolab/issue4349 we don't draw a regular appointment icon (to save space)
}
}
void KOAgendaItem::paintTodoIcon( TQPainter *p, int &x, int ft )
{
if ( !mIncidence ) return;
static const TQPixmap todoPxmp =
KOGlobals::self()->smallIcon( "todo" );
static const TQPixmap completedPxmp =
KOGlobals::self()->smallIcon( "checkedbox" );
if ( mIncidence->type() != "Todo" )
return;
bool b = ( static_cast<Todo *>( mIncidence ) )->isCompleted();
conditionalPaint( p, !b, x, ft, todoPxmp );
conditionalPaint( p, b, x, ft, completedPxmp );
}
void KOAgendaItem::paintAlarmIcon( TQPainter *p, int &x, int ft )
{
if (!mIconAlarm) return;
int y = ft;
// if we can't fit it all, bottom align it, more or less, so
// it can be guessed better, visually
if ( visibleRect().height() - ft < alarmPxmp->height() )
y -= ( alarmPxmp->height() - visibleRect().height() - ft );
p->drawPixmap( x, y, *alarmPxmp );
x += alarmPxmp->width() + ft;
}
void KOAgendaItem::paintIcons( TQPainter *p, int &x, int ft )
{
paintEventIcon( p, x, ft );
paintTodoIcon( p, x, ft );
if ( !mSpecialEvent ) {
paintAlarmIcon( p, x, ft );
}
conditionalPaint( p, mIconRecur && !mSpecialEvent, x, ft, *recurPxmp );
conditionalPaint( p, mIconReadonly && !mSpecialEvent, x, ft, *readonlyPxmp );
conditionalPaint( p, mIconReply, x, ft, *replyPxmp );
conditionalPaint( p, mIconGroup, x, ft, *groupPxmp );
conditionalPaint( p, mIconGroupTentative, x, ft, *groupPxmpTentative );
conditionalPaint( p, mIconOrganizer, x, ft, *organizerPxmp );
}
void KOAgendaItem::paintEvent( TQPaintEvent *ev )
{
//HACK
// to reproduce a crash:
// 1. start Kontact with the Calendar as the initial module
// 2. immediately select the summary (which must include appt and to-do)
// causes a crash for me every time in this method unless we make
// the following check
if ( !mIncidence )return;
TQRect visRect = visibleRect();
// when scrolling horizontally in the side-by-side view, the repainted area is clipped
// to the newly visible area, which is a problem since the content changes when visRect
// changes, so repaint the full item in that case
if ( ev->rect() != visRect && visRect.isValid() && ev->rect().isValid() ) {
repaint( visRect );
return;
}
TQPainter p( this );
const int ft = 2; // frame thickness for layout, see paintFrame()
const int margin = 1 + ft; // frame + space between frame and content
// General idea is to always show the icons (even in the all-day events).
// This creates a consistent fealing for the user when the view mode
// changes and therefore the available width changes.
// Also look at #17984
if ( !alarmPxmp ) {
alarmPxmp = new TQPixmap( KOGlobals::self()->smallIcon("bell") );
recurPxmp = new TQPixmap( KOGlobals::self()->smallIcon("recur") );
readonlyPxmp = new TQPixmap( KOGlobals::self()->smallIcon("readonlyevent") );
replyPxmp = new TQPixmap( KOGlobals::self()->smallIcon("mail-reply-sender") );
groupPxmp = new TQPixmap( KOGlobals::self()->smallIcon("groupevent") );
groupPxmpTentative = new TQPixmap( KOGlobals::self()->smallIcon("groupeventtentative") );
organizerPxmp = new TQPixmap( KOGlobals::self()->smallIcon("organizer") );
}
TQColor bgColor;
if ( mIncidence->type() == "Todo" ) {
if ( static_cast<Todo*>(mIncidence)->isOverdue() )
bgColor = KOPrefs::instance()->todoOverdueColor();
else if ( static_cast<Todo*>(mIncidence)->dtDue().date() ==
TQDateTime::currentDateTime().date() )
bgColor = KOPrefs::instance()->todoDueTodayColor();
}
TQColor categoryColor;
TQStringList categories = mIncidence->categories();
TQString cat = categories.first();
if (cat.isEmpty())
categoryColor = KOPrefs::instance()->unsetCategoryColor();
else
categoryColor = *(KOPrefs::instance()->categoryColor(cat));
TQColor resourceColor = mResourceColor;
if ( !resourceColor.isValid() )
resourceColor = categoryColor;
TQColor frameColor;
if ( KOPrefs::instance()->agendaViewColors() == KOPrefs::ResourceOnly ||
KOPrefs::instance()->agendaViewColors() == KOPrefs::CategoryInsideResourceOutside ) {
frameColor = bgColor.isValid() ? bgColor : resourceColor;
} else {
frameColor = bgColor.isValid() ? bgColor : categoryColor;
}
if ( !bgColor.isValid() ) {
if ( KOPrefs::instance()->agendaViewColors() == KOPrefs::ResourceOnly ||
KOPrefs::instance()->agendaViewColors() == KOPrefs::ResourceInsideCategoryOutside ) {
bgColor = resourceColor;
} else {
bgColor = categoryColor;
}
}
if ( cat.isEmpty() &&
KOPrefs::instance()->agendaViewColors() == KOPrefs::ResourceInsideCategoryOutside ) {
frameColor = bgColor;
}
if ( cat.isEmpty() &&
KOPrefs::instance()->agendaViewColors() == KOPrefs::CategoryInsideResourceOutside ) {
bgColor = frameColor;
}
if ( mSelected ) {
frameColor = TQColor( 85 + frameColor.red() * 2/3,
85 + frameColor.green() * 2/3,
85 + frameColor.blue() * 2/3 );
} else {
frameColor = frameColor.dark( 115 );
}
TQColor textColor = getTextColor(bgColor);
p.setPen( textColor );
p.setBackgroundColor( bgColor );
p.setFont(KOPrefs::instance()->mAgendaViewFont);
TQFontMetrics fm = p.fontMetrics();
int singleLineHeight = fm.boundingRect( mLabelText ).height();
p.eraseRect( 0, 0, width(), height() );
paintFrame( &p, frameColor );
// calculate the height of the full version (case 4) to test whether it is
// possible
TQString shortH;
TQString longH;
if ( !isMultiItem() ) {
shortH = TDEGlobal::locale()->formatTime(mIncidence->dtStart().time());
if (mIncidence->type() != "Todo")
longH = i18n("%1 - %2").arg(shortH)
.arg(TDEGlobal::locale()->formatTime(mIncidence->dtEnd().time()));
else
longH = shortH;
} else if ( !mMultiItemInfo->mFirstMultiItem ) {
shortH = TDEGlobal::locale()->formatTime(mIncidence->dtStart().time());
longH = shortH;
} else {
shortH = TDEGlobal::locale()->formatTime(mIncidence->dtEnd().time());
longH = i18n("- %1").arg(shortH);
}
KWordWrap *ww = KWordWrap::formatText( fm,
TQRect(0, 0, width() - (2 * margin), -1),
0,
mLabelText );
int th = ww->boundingRect().height();
delete ww;
int hlHeight = TQMAX(fm.boundingRect(longH).height(),
TQMAX(alarmPxmp->height(), TQMAX(recurPxmp->height(),
TQMAX(readonlyPxmp->height(), TQMAX(replyPxmp->height(),
TQMAX(groupPxmp->height(), organizerPxmp->height()))))));
bool completelyRenderable = th < (height() - 2 * ft - 2 - hlHeight);
// case 1: do not draw text when not even a single line fits
// Don't do this any more, always try to print out the text. Even if
// it's just a few pixel, one can still guess the whole text from just four pixels' height!
if ( //( singleLineHeight > height()-4 ) || // ignore margin, be gentle.. Even ignore 2 pixel outside the item
( width() < 16 ) ) {
int x = margin;
paintTodoIcon( &p, x, ft );
return;
}
// case 2: draw a single line when no more space
if ( (2 * singleLineHeight) > (height() - 2 * margin) ) {
int x = margin, txtWidth;
if ( mIncidence->doesFloat() ) {
x += visRect.left();
paintIcons( &p, x, ft );
txtWidth = visRect.right() - margin - x;
}
else {
paintIcons( &p, x, ft );
txtWidth = width() - margin - x;
}
int y = ((height() - 2 * ft - singleLineHeight) / 2) + fm.ascent();
KWordWrap::drawFadeoutText( &p, x, y,
txtWidth, mLabelText );
return;
}
// case 3: enough for 2-5 lines, but not for the header.
// Also used for the middle days in multi-events
if ( ((!completelyRenderable) && ((height() - (2 * margin)) <= (5 * singleLineHeight)) ) ||
(isMultiItem() && mMultiItemInfo->mNextMultiItem && mMultiItemInfo->mFirstMultiItem) ) {
int x = margin, txtWidth;
if ( mIncidence->doesFloat() ) {
x += visRect.left();
paintIcons( &p, x, ft );
txtWidth = visRect.right() - margin - x;
}
else {
paintIcons( &p, x, ft );
txtWidth = width() - margin - x;
}
ww = KWordWrap::formatText( fm,
TQRect( 0, 0, txtWidth,
(height() - (2 * margin)) ),
0,
mLabelText );
//kdDebug() << "SIZES for " << mLabelText << ": " << width() << " :: " << txtWidth << endl;
ww->drawText( &p, x, margin, TQt::AlignHCenter | KWordWrap::FadeOut );
delete ww;
return;
}
// case 4: paint everything, with header:
// consists of (vertically) ft + headline&icons + ft + text + margin
int y = 2 * ft + hlHeight;
if ( completelyRenderable )
y += (height() - (2 * ft) - margin - hlHeight - th) / 2;
int x = margin, txtWidth, hTxtWidth, eventX;
if ( mIncidence->doesFloat() ) {
shortH = longH = "";
if ( (mIncidence->type() != "Todo") &&
(mIncidence->dtStart() != mIncidence->dtEnd()) ) { // multi days
shortH = longH =
i18n("%1 - %2")
.arg(TDEGlobal::locale()->formatDate(mIncidence->dtStart().date()))
.arg(TDEGlobal::locale()->formatDate(mIncidence->dtEnd().date()));
// paint headline
p.fillRect( 0, 0, width(), (ft/2) + margin + hlHeight,
TQBrush( frameColor ) );
}
x += visRect.left();
eventX = x;
txtWidth = visRect.right() - margin - x;
paintIcons( &p, x, ft );
hTxtWidth = visRect.right() - margin - x;
}
else {
// paint headline
p.fillRect( 0, 0, width(), (ft/2) + margin + hlHeight,
TQBrush( frameColor ) );
txtWidth = width() - margin - x;
eventX = x;
paintIcons( &p, x, ft );
hTxtWidth = width() - margin - x;
}
TQString headline;
int hw = fm.boundingRect( longH ).width();
if ( hw > hTxtWidth ) {
headline = shortH;
hw = fm.boundingRect( shortH ).width();
if ( hw < txtWidth )
x += (hTxtWidth - hw) / 2;
} else {
headline = longH;
x += (hTxtWidth - hw) / 2;
}
p.setBackgroundColor( frameColor );
p.setPen( getTextColor( frameColor ) );
KWordWrap::drawFadeoutText( &p, x, ft + fm.ascent(), hTxtWidth, headline );
// draw event text
ww = KWordWrap::formatText( fm,
TQRect( 0, 0, txtWidth, height() - margin - y ),
0,
mLabelText );
p.setBackgroundColor( bgColor );
p.setPen( textColor );
TQString ws = ww->wrappedString();
if ( ws.left( ws.length()-1 ).find( '\n' ) >= 0 )
ww->drawText( &p, eventX, y,
TQt::AlignAuto | KWordWrap::FadeOut );
else
ww->drawText( &p, eventX + (txtWidth-ww->boundingRect().width()-2*margin)/2,
y, TQt::AlignHCenter | KWordWrap::FadeOut );
delete ww;
}