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/kmail/headeritem.cpp

622 lines
20 KiB

/*******************************************************************************
**
** Filename : headeritem.cpp
** Created on : 28 November, 2004
** Copyright : (c) 2004 Till Adam
** Email : adam@kde.org
**
*******************************************************************************/
/*******************************************************************************
**
** 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.
**
** In addition, as a special exception, the copyright holders give
** permission to link the code of this program with any edition of
** the TQt library by Trolltech AS, Norway (or with modified versions
** of TQt that use the same license as TQt), and distribute linked
** combinations including the two. You must obey the GNU General
** Public License in all respects for all of the code used other than
** TQt. If you modify this file, you may extend this exception to
** your version of the file, but you are not obligated to do so. If
** you do not wish to do so, delete this exception statement from
** your version.
**
*******************************************************************************/
#include <klocale.h>
#include <tqapplication.h>
#include <tqregexp.h>
#include <tqbitmap.h>
#include <tqpainter.h>
#include <kio/netaccess.h>
#include "headeritem.h"
#include "kmheaders.h"
#include "kmfolder.h"
using namespace KMail;
// Constuction a new list view item with the given colors and pixmap
HeaderItem::HeaderItem( TQListView* parent, int msgId, const TQString& key )
: KListViewItem( parent ),
mMsgId( msgId ),
mKey( key ),
mAboutToBeDeleted( false ),
mSortCacheItem( 0 )
{
irefresh();
}
// Constuction a new list view item with the given parent, colors, & pixmap
HeaderItem::HeaderItem( TQListViewItem* parent, int msgId, const TQString& key )
: KListViewItem( parent ),
mMsgId( msgId ),
mKey( key ),
mAboutToBeDeleted( false ),
mSortCacheItem( 0 )
{
irefresh();
}
HeaderItem::~HeaderItem ()
{
delete mSortCacheItem;
}
// Update the msgId this item corresponds to.
void HeaderItem::setMsgId( int aMsgId )
{
mMsgId = aMsgId;
}
// Profiling note: About 30% of the time taken to initialize the
// listview is spent in this function. About 60% is spent in operator
// new and TQListViewItem::TQListViewItem.
void HeaderItem::irefresh()
{
KMHeaders *headers = static_cast<KMHeaders*>(listView());
NestingPolicy threadingPolicy = headers->getNestingPolicy();
if ((threadingPolicy == AlwaysOpen) ||
(threadingPolicy == DefaultOpen)) {
//Avoid opening items as TQListView is currently slow to do so.
setOpen(true);
return;
}
if (threadingPolicy == DefaultClosed)
return; //default to closed
// otherwise threadingPolicy == OpenUnread
if (parent() && parent()->isOpen()) {
setOpen(true);
return;
}
KMMsgBase *mMsgBase = headers->folder()->getMsgBase( mMsgId );
mSerNum = mMsgBase->getMsgSerNum();
if (mMsgBase->isNew() || mMsgBase->isUnread()
|| mMsgBase->isImportant() || mMsgBase->isTodo() || mMsgBase->isWatched() ) {
setOpen(true);
HeaderItem * topOfThread = this;
while(topOfThread->parent())
topOfThread = (HeaderItem*)topOfThread->parent();
topOfThread->setOpenRecursive(true);
}
}
// Return the msgId of the message associated with this item
int HeaderItem::msgId() const
{
return mMsgId;
}
TQString HeaderItem::to() const
{
KMHeaders * const headers = static_cast<KMHeaders*>( listView() );
KMMsgBase * const msgBase = headers->folder()->getMsgBase( mMsgId );
if ( msgBase ) {
return msgBase->to();
} else {
return TQString();
}
}
TQString HeaderItem::from() const
{
KMHeaders * const headers = static_cast<KMHeaders*>( listView() );
KMMsgBase * const msgBase = headers->folder()->getMsgBase( mMsgId );
if ( msgBase ) {
return msgBase->from();
} else {
return TQString();
}
}
// Return the serial number
TQ_UINT32 HeaderItem::msgSerNum() const
{
return mSerNum;
}
// Update this item to summarise a new folder and message
//Opens all children in the thread
void HeaderItem::setOpenRecursive( bool open )
{
if (open){
TQListViewItem * lvchild;
lvchild = firstChild();
while (lvchild){
((HeaderItem*)lvchild)->setOpenRecursive( true );
lvchild = lvchild->nextSibling();
}
setOpen( true );
} else {
setOpen( false );
}
}
TQString HeaderItem::text( int col) const
{
KMHeaders *headers = static_cast<KMHeaders*>(listView());
KMMsgBase *mMsgBase = headers->folder()->getMsgBase( mMsgId );
TQString tmp;
if ( !mMsgBase )
return TQString();
if ( col == headers->paintInfo()->senderCol ) {
if ( (headers->folder()->whoField().lower() == "to") && !headers->paintInfo()->showReceiver )
tmp = mMsgBase->toStrip();
else
tmp = mMsgBase->fromStrip();
if (tmp.isEmpty())
tmp = i18n("Unknown");
else
tmp = tmp.simplifyWhiteSpace();
} else if ( col == headers->paintInfo()->receiverCol ) {
tmp = mMsgBase->toStrip();
if (tmp.isEmpty())
tmp = i18n("Unknown");
else
tmp = tmp.simplifyWhiteSpace();
} else if(col == headers->paintInfo()->subCol) {
tmp = mMsgBase->subject();
if (tmp.isEmpty())
tmp = i18n("No Subject");
else
tmp.remove(TQRegExp("[\r\n]"));
} else if(col == headers->paintInfo()->dateCol) {
tmp = headers->mDate.dateString( mMsgBase->date() );
} else if(col == headers->paintInfo()->sizeCol
&& headers->paintInfo()->showSize) {
if ( mMsgBase->parent()->folderType() == KMFolderTypeImap ) {
tmp = KIO::convertSize( mMsgBase->msgSizeServer() );
} else {
tmp = KIO::convertSize( mMsgBase->msgSize() );
}
}
return tmp;
}
void HeaderItem::setup()
{
widthChanged();
const int ph = KMHeaders::pixNew->height();
TQListView *v = listView();
int h = TQMAX( v->fontMetrics().height(), ph ) + 2*v->itemMargin();
h = TQMAX( h, TQApplication::globalStrut().height());
if ( h % 2 > 0 )
h++;
setHeight( h );
}
typedef TQValueList<TQPixmap> PixmapList;
TQPixmap HeaderItem::pixmapMerge( PixmapList pixmaps ) const
{
int width = 0;
int height = 0;
for ( PixmapList::ConstIterator it = pixmaps.begin();
it != pixmaps.end(); ++it ) {
width += (*it).width();
height = TQMAX( height, (*it).height() );
}
TQPixmap res( width, height );
TQBitmap mask( width, height, true );
int x = 0;
for ( PixmapList::ConstIterator it = pixmaps.begin();
it != pixmaps.end(); ++it ) {
bitBlt( &res, x, (height - (*it).height()) / 2, &(*it) );
bitBlt( &mask, x, (height - (*it).height()) / 2, (*it).mask() );
x += (*it).width();
}
res.setMask( mask );
return res;
}
const TQPixmap *HeaderItem::cryptoIcon(KMMsgBase *msgBase) const
{
switch ( msgBase->encryptionState() )
{
case KMMsgFullyEncrypted : return KMHeaders::pixFullyEncrypted;
case KMMsgPartiallyEncrypted : return KMHeaders::pixPartiallyEncrypted;
case KMMsgEncryptionStateUnknown: return KMHeaders::pixUndefinedEncrypted;
case KMMsgEncryptionProblematic : return KMHeaders::pixEncryptionProblematic;
default : return 0;
}
}
const TQPixmap *HeaderItem::signatureIcon(KMMsgBase *msgBase) const
{
switch ( msgBase->signatureState() )
{
case KMMsgFullySigned : return KMHeaders::pixFullySigned;
case KMMsgPartiallySigned : return KMHeaders::pixPartiallySigned;
case KMMsgSignatureStateUnknown: return KMHeaders::pixUndefinedSigned;
case KMMsgSignatureProblematic : return KMHeaders::pixSignatureProblematic;
default : return 0;
}
}
const TQPixmap *HeaderItem::statusIcon(KMMsgBase *msgBase) const
{
// forwarded, replied have precedence over the other states
if ( msgBase->isForwarded() && !msgBase->isReplied() ) return KMHeaders::pixReadFwd;
if ( !msgBase->isForwarded() && msgBase->isReplied() ) return KMHeaders::pixReadReplied;
if ( msgBase->isForwarded() && msgBase->isReplied() ) return KMHeaders::pixReadFwdReplied;
// a queued or sent mail is usually also read
if ( msgBase->isQueued() ) return KMHeaders::pixQueued;
if ( msgBase->isSent() ) return KMHeaders::pixSent;
if ( msgBase->isNew() ) return KMHeaders::pixNew;
if ( msgBase->isRead() || msgBase->isOld() ) return KMHeaders::pixRead;
if ( msgBase->isUnread() ) return KMHeaders::pixUns;
if ( msgBase->isDeleted() ) return KMHeaders::pixDel;
return 0;
}
const TQPixmap *HeaderItem::pixmap(int col) const
{
KMHeaders *headers = static_cast<KMHeaders*>(listView());
KMMsgBase *msgBase = headers->folder()->getMsgBase( mMsgId );
if ( col == headers->paintInfo()->subCol ) {
PixmapList pixmaps;
if ( !headers->mPaintInfo.showSpamHam ) {
// Have the spam/ham and watched/ignored icons first, I guess.
if ( msgBase->isSpam() ) pixmaps << *KMHeaders::pixSpam;
if ( msgBase->isHam() ) pixmaps << *KMHeaders::pixHam;
}
if ( !headers->mPaintInfo.showWatchedIgnored ) {
if ( msgBase->isIgnored() ) pixmaps << *KMHeaders::pixIgnored;
if ( msgBase->isWatched() ) pixmaps << *KMHeaders::pixWatched;
}
if ( !headers->mPaintInfo.showStatus ) {
const TQPixmap *pix = statusIcon(msgBase);
if ( pix ) pixmaps << *pix;
}
// Only merge the attachment icon in if that is configured.
if ( headers->paintInfo()->showAttachmentIcon &&
!headers->paintInfo()->showAttachment &&
msgBase->attachmentState() == KMMsgHasAttachment )
pixmaps << *KMHeaders::pixAttachment;
// Only merge the invitation icon in if that is configured.
if ( headers->paintInfo()->showInvitationIcon &&
msgBase->invitationState() == KMMsgHasInvitation )
pixmaps << *KMHeaders::pixInvitation;
// Only merge the crypto icons in if that is configured.
if ( headers->paintInfo()->showCryptoIcons ) {
const TQPixmap *pix;
if ( !headers->paintInfo()->showCrypto )
if ( (pix = cryptoIcon(msgBase)) ) pixmaps << *pix;
if ( !headers->paintInfo()->showSigned )
if ( (pix = signatureIcon(msgBase)) ) pixmaps << *pix;
}
if ( !headers->mPaintInfo.showImportant )
if ( msgBase->isImportant() ) pixmaps << *KMHeaders::pixFlag;
if ( !headers->mPaintInfo.showTodo )
if ( msgBase->isTodo() ) pixmaps << *KMHeaders::pixTodo;
static TQPixmap mergedpix;
mergedpix = pixmapMerge( pixmaps );
return &mergedpix;
}
else if ( col == headers->paintInfo()->statusCol ) {
return statusIcon(msgBase);
}
else if ( col == headers->paintInfo()->attachmentCol ) {
if ( msgBase->attachmentState() == KMMsgHasAttachment )
return KMHeaders::pixAttachment;
}
else if ( col == headers->paintInfo()->invitationCol ) {
if ( msgBase->invitationState() == KMMsgHasInvitation )
return KMHeaders::pixInvitation;
}
else if ( col == headers->paintInfo()->importantCol ) {
if ( msgBase->isImportant() )
return KMHeaders::pixFlag;
}
else if ( col == headers->paintInfo()->todoCol ) {
if ( msgBase->isTodo() )
return KMHeaders::pixTodo;
}
else if ( col == headers->paintInfo()->spamHamCol ) {
if ( msgBase->isSpam() ) return KMHeaders::pixSpam;
if ( msgBase->isHam() ) return KMHeaders::pixHam;
}
else if ( col == headers->paintInfo()->watchedIgnoredCol ) {
if ( msgBase->isWatched() ) return KMHeaders::pixWatched;
if ( msgBase->isIgnored() ) return KMHeaders::pixIgnored;
}
else if ( col == headers->paintInfo()->signedCol ) {
return signatureIcon(msgBase);
}
else if ( col == headers->paintInfo()->cryptoCol ) {
return cryptoIcon(msgBase);
}
return 0;
}
void HeaderItem::paintCell( TQPainter * p, const TQColorGroup & cg,
int column, int width, int align )
{
KMHeaders *headers = static_cast<KMHeaders*>(listView());
if (headers->noRepaint) return;
if (!headers->folder()) return;
KMMsgBase *mMsgBase = headers->folder()->getMsgBase( mMsgId );
if (!mMsgBase) return;
TQColorGroup _cg( cg );
TQColor c = _cg.text();
TQColor *color = const_cast<TQColor *>( &headers->paintInfo()->colFore );
TQFont font = p->font();
int weight = font.weight();
// for color and font family "important" overrides "new" overrides "unread"
// overrides "todo" for the weight we use the maximal weight
if ( mMsgBase->isTodo() ) {
color = const_cast<TQColor*>( &headers->paintInfo()->colTodo );
font = headers->todoFont();
weight = TQMAX( weight, font.weight() );
}
if ( mMsgBase->isUnread() ) {
color = const_cast<TQColor*>( &headers->paintInfo()->colUnread );
font = headers->unreadFont();
weight = TQMAX( weight, font.weight() );
}
if ( mMsgBase->isNew() ) {
color = const_cast<TQColor*>( &headers->paintInfo()->colNew );
font = headers->newFont();
weight = TQMAX( weight, font.weight() );
}
if ( mMsgBase->isImportant() ) {
color = const_cast<TQColor*>( &headers->paintInfo()->colFlag );
font = headers->importantFont();
weight = TQMAX( weight, font.weight() );
}
if ( column == headers->paintInfo()->dateCol ) {
font = headers->dateFont();
}
TQColor cdisabled = KGlobalSettings::inactiveTextColor();
if ( headers->isMessageCut( msgSerNum() ) ) {
font.setItalic( true );
color = &cdisabled;
}
// set color and font
_cg.setColor( TQColorGroup::Text, *color );
font.setWeight( weight );
p->setFont( font );
KListViewItem::paintCell( p, _cg, column, width, align );
if (aboutToBeDeleted()) {
// strike through
p->drawLine( 0, height()/2, width, height()/2);
}
// reset color
_cg.setColor( TQColorGroup::Text, c );
}
TQString HeaderItem::generate_key( KMHeaders *headers,
KMMsgBase *msg,
const KPaintInfo *paintInfo,
int sortOrder )
{
// It appears, that TQListView in TQt-3.0 asks for the key
// in TQListView::clear(), which is called from
// readSortOrder()
if (!msg) return TQString();
int column = sortOrder & ((1 << 5) - 1);
TQString ret = TQChar( (char)sortOrder );
TQString sortArrival = TQString( "%1" ).arg( msg->getMsgSerNum(), 0, 36 );
while (sortArrival.length() < 7) sortArrival = '0' + sortArrival;
if (column == paintInfo->dateCol) {
if (paintInfo->orderOfArrival)
return ret + sortArrival;
else {
TQString d = TQString::number(msg->date());
while (d.length() <= 10) d = '0' + d;
return ret + d + sortArrival;
}
} else if (column == paintInfo->senderCol) {
TQString tmp;
if ( (headers->folder()->whoField().lower() == "to") && !headers->paintInfo()->showReceiver )
tmp = msg->toStrip();
else
tmp = msg->fromStrip();
return ret + tmp.lower() + ' ' + sortArrival;
} else if (column == paintInfo->receiverCol) {
TQString tmp = msg->toStrip();
return ret + tmp.lower() + ' ' + sortArrival;
} else if (column == paintInfo->subCol) {
TQString tmp;
tmp = ret;
if (paintInfo->status) {
tmp += msg->statusToSortRank() + ' ';
}
tmp += KMMessage::stripOffPrefixes( msg->subject().lower() ) + ' ' + sortArrival;
return tmp;
}
else if (column == paintInfo->sizeCol) {
TQString len;
if ( msg->parent()->folderType() == KMFolderTypeImap )
{
len = TQString::number( msg->msgSizeServer() );
} else {
len = TQString::number( msg->msgSize() );
}
while (len.length() < 9) len = '0' + len;
return ret + len + sortArrival;
}
else if (column == paintInfo->statusCol) {
TQString s;
if ( msg->isNew() ) s = "1";
else if ( msg->isUnread() ) s = "2";
else if (!msg->isForwarded() && msg->isReplied() ) s = "3";
else if ( msg->isForwarded() && msg->isReplied() ) s = "4";
else if ( msg->isForwarded() && !msg->isReplied() ) s = "5";
else if ( msg->isRead() || msg->isOld() ) s = "6";
else if ( msg->isQueued() ) s = "7";
else if ( msg->isSent() ) s = "8";
else if ( msg->isDeleted() ) s = "9";
return ret + s + sortArrival;
}
else if (column == paintInfo->attachmentCol) {
TQString s(msg->attachmentState() == KMMsgHasAttachment ? "1" : "0");
return ret + s + sortArrival;
}
else if (column == paintInfo->invitationCol) {
TQString s(msg->invitationState() == KMMsgHasInvitation ? "1" : "0");
return ret + s + sortArrival;
}
else if (column == paintInfo->importantCol) {
TQString s(msg->isImportant() ? "1" : "0");
return ret + s + sortArrival;
}
else if ( column == paintInfo->todoCol ) {
TQString s( msg->isTodo() ? "1": "0" );
return ret + s + sortArrival;
}
else if (column == paintInfo->spamHamCol) {
TQString s((msg->isSpam() || msg->isHam()) ? "1" : "0");
return ret + s + sortArrival;
}
else if (column == paintInfo->watchedIgnoredCol) {
TQString s((msg->isWatched() || msg->isIgnored()) ? "1" : "0");
return ret + s + sortArrival;
}
else if (column == paintInfo->signedCol) {
TQString s;
switch ( msg->signatureState() )
{
case KMMsgFullySigned : s = "1"; break;
case KMMsgPartiallySigned : s = "2"; break;
case KMMsgSignatureStateUnknown: s = "3"; break;
case KMMsgSignatureProblematic : s = "4"; break;
default : s = "5"; break;
}
return ret + s + sortArrival;
}
else if (column == paintInfo->cryptoCol) {
TQString s;
switch ( msg->encryptionState() )
{
case KMMsgFullyEncrypted : s = "1"; break;
case KMMsgPartiallyEncrypted : s = "2"; break;
case KMMsgEncryptionStateUnknown: s = "3"; break;
case KMMsgEncryptionProblematic : s = "4"; break;
default : s = "5"; break;
}
return ret + s + sortArrival;
}
return ret + "missing key"; //you forgot something!!
}
TQString HeaderItem::key( int column, bool /*ascending*/ ) const
{
KMHeaders *headers = static_cast<KMHeaders*>(listView());
int sortOrder = column;
if (headers->mPaintInfo.orderOfArrival)
sortOrder |= (1 << 6);
if (headers->mPaintInfo.status)
sortOrder |= (1 << 5);
//This code should stay pretty much like this, if you are adding new
//columns put them in generate_key
if(mKey.isEmpty() || mKey[0] != (char)sortOrder) {
KMHeaders *headers = static_cast<KMHeaders*>(listView());
KMMsgBase *msgBase = headers->folder()->getMsgBase( mMsgId );
return ((HeaderItem *)this)->mKey =
generate_key( headers, msgBase, headers->paintInfo(), sortOrder );
}
return mKey;
}
void HeaderItem::setTempKey( TQString key ) {
mKey = key;
}
int HeaderItem::compare( TQListViewItem *i, int col, bool ascending ) const
{
int res = 0;
KMHeaders *headers = static_cast<KMHeaders*>(listView());
if ( ( col == headers->paintInfo()->statusCol ) ||
( col == headers->paintInfo()->sizeCol ) ||
( col == headers->paintInfo()->attachmentCol ) ||
( col == headers->paintInfo()->invitationCol ) ||
( col == headers->paintInfo()->importantCol ) ||
( col == headers->paintInfo()->todoCol ) ||
( col == headers->paintInfo()->spamHamCol ) ||
( col == headers->paintInfo()->signedCol ) ||
( col == headers->paintInfo()->cryptoCol ) ||
( col == headers->paintInfo()->watchedIgnoredCol ) ) {
res = key( col, ascending ).compare( i->key( col, ascending ) );
} else if ( col == headers->paintInfo()->dateCol ) {
res = key( col, ascending ).compare( i->key( col, ascending ) );
if (i->parent() && !ascending)
res = -res;
} else if ( col == headers->paintInfo()->subCol ||
col == headers->paintInfo()->senderCol ||
col == headers->paintInfo()->receiverCol ) {
res = key( col, ascending ).localeAwareCompare( i->key( col, ascending ) );
}
return res;
}
TQListViewItem* HeaderItem::firstChildNonConst() /* Non const! */
{
enforceSortOrder(); // Try not to rely on TQListView implementation details
return firstChild();
}