summaryrefslogtreecommitdiffstats
path: root/kmail/headeritem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kmail/headeritem.cpp')
-rw-r--r--kmail/headeritem.cpp585
1 files changed, 585 insertions, 0 deletions
diff --git a/kmail/headeritem.cpp b/kmail/headeritem.cpp
new file mode 100644
index 00000000..ab9b8329
--- /dev/null
+++ b/kmail/headeritem.cpp
@@ -0,0 +1,585 @@
+/*******************************************************************************
+**
+** 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 Qt library by Trolltech AS, Norway (or with modified versions
+** of Qt that use the same license as Qt), 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
+** Qt. 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 <qapplication.h>
+#include <qregexp.h>
+#include <qbitmap.h>
+#include <qpainter.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( QListView* parent, int msgId, const QString& 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( QListViewItem* parent, int msgId, const QString& 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 QListViewItem::QListViewItem.
+void HeaderItem::irefresh()
+{
+ KMHeaders *headers = static_cast<KMHeaders*>(listView());
+ NestingPolicy threadingPolicy = headers->getNestingPolicy();
+ if ((threadingPolicy == AlwaysOpen) ||
+ (threadingPolicy == DefaultOpen)) {
+ //Avoid opening items as QListView 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;
+}
+
+// Return the serial number
+Q_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){
+ QListViewItem * lvchild;
+ lvchild = firstChild();
+ while (lvchild){
+ ((HeaderItem*)lvchild)->setOpenRecursive( true );
+ lvchild = lvchild->nextSibling();
+ }
+ setOpen( true );
+ } else {
+ setOpen( false );
+ }
+}
+
+QString HeaderItem::text( int col) const
+{
+ KMHeaders *headers = static_cast<KMHeaders*>(listView());
+ KMMsgBase *mMsgBase = headers->folder()->getMsgBase( mMsgId );
+ QString tmp;
+
+ if ( !mMsgBase )
+ return QString();
+
+ 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(QRegExp("[\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();
+ QListView *v = listView();
+ int h = QMAX( v->fontMetrics().height(), ph ) + 2*v->itemMargin();
+ h = QMAX( h, QApplication::globalStrut().height());
+ if ( h % 2 > 0 )
+ h++;
+ setHeight( h );
+}
+
+typedef QValueList<QPixmap> PixmapList;
+
+QPixmap 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 = QMAX( height, (*it).height() );
+ }
+
+ QPixmap res( width, height );
+ QBitmap 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 QPixmap *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 QPixmap *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 QPixmap *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 QPixmap *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 QPixmap *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 crypto icons in if that is configured.
+ if ( headers->paintInfo()->showCryptoIcons ) {
+ const QPixmap *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 QPixmap 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()->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( QPainter * p, const QColorGroup & 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;
+
+ QColorGroup _cg( cg );
+ QColor c = _cg.text();
+ QColor *color = const_cast<QColor *>( &headers->paintInfo()->colFore );
+ QFont 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<QColor*>( &headers->paintInfo()->colTodo );
+ font = headers->todoFont();
+ weight = QMAX( weight, font.weight() );
+ }
+ if ( mMsgBase->isUnread() ) {
+ color = const_cast<QColor*>( &headers->paintInfo()->colUnread );
+ font = headers->unreadFont();
+ weight = QMAX( weight, font.weight() );
+ }
+ if ( mMsgBase->isNew() ) {
+ color = const_cast<QColor*>( &headers->paintInfo()->colNew );
+ font = headers->newFont();
+ weight = QMAX( weight, font.weight() );
+ }
+
+ if ( mMsgBase->isImportant() ) {
+ color = const_cast<QColor*>( &headers->paintInfo()->colFlag );
+ font = headers->importantFont();
+ weight = QMAX( weight, font.weight() );
+ }
+ if ( column == headers->paintInfo()->dateCol ) {
+ font = headers->dateFont();
+ }
+
+ QColor cdisabled = KGlobalSettings::inactiveTextColor();
+ if ( headers->isMessageCut( msgSerNum() ) ) {
+ font.setItalic( true );
+ color = &cdisabled;
+ }
+
+ // set color and font
+ _cg.setColor( QColorGroup::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( QColorGroup::Text, c );
+}
+
+QString HeaderItem::generate_key( KMHeaders *headers,
+ KMMsgBase *msg,
+ const KPaintInfo *paintInfo,
+ int sortOrder )
+{
+ // It appears, that QListView in Qt-3.0 asks for the key
+ // in QListView::clear(), which is called from
+ // readSortOrder()
+ if (!msg) return QString::null;
+
+ int column = sortOrder & ((1 << 5) - 1);
+ QString ret = QChar( (char)sortOrder );
+ QString sortArrival = QString( "%1" ).arg( msg->getMsgSerNum(), 0, 36 );
+ while (sortArrival.length() < 7) sortArrival = '0' + sortArrival;
+
+ if (column == paintInfo->dateCol) {
+ if (paintInfo->orderOfArrival)
+ return ret + sortArrival;
+ else {
+ QString d = QString::number(msg->date());
+ while (d.length() <= 10) d = '0' + d;
+ return ret + d + sortArrival;
+ }
+ } else if (column == paintInfo->senderCol) {
+ QString 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) {
+ QString tmp = msg->toStrip();
+ return ret + tmp.lower() + ' ' + sortArrival;
+ } else if (column == paintInfo->subCol) {
+ QString tmp;
+ tmp = ret;
+ if (paintInfo->status) {
+ tmp += msg->statusToSortRank() + ' ';
+ }
+ tmp += KMMessage::stripOffPrefixes( msg->subject().lower() ) + ' ' + sortArrival;
+ return tmp;
+ }
+ else if (column == paintInfo->sizeCol) {
+ QString len;
+ if ( msg->parent()->folderType() == KMFolderTypeImap )
+ {
+ len = QString::number( msg->msgSizeServer() );
+ } else {
+ len = QString::number( msg->msgSize() );
+ }
+ while (len.length() < 9) len = '0' + len;
+ return ret + len + sortArrival;
+ }
+ else if (column == paintInfo->statusCol) {
+ QString 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) {
+ QString s(msg->attachmentState() == KMMsgHasAttachment ? "1" : "0");
+ return ret + s + sortArrival;
+ }
+ else if (column == paintInfo->importantCol) {
+ QString s(msg->isImportant() ? "1" : "0");
+ return ret + s + sortArrival;
+ }
+ else if ( column == paintInfo->todoCol ) {
+ QString s( msg->isTodo() ? "1": "0" );
+ return ret + s + sortArrival;
+ }
+ else if (column == paintInfo->spamHamCol) {
+ QString s((msg->isSpam() || msg->isHam()) ? "1" : "0");
+ return ret + s + sortArrival;
+ }
+ else if (column == paintInfo->watchedIgnoredCol) {
+ QString s((msg->isWatched() || msg->isIgnored()) ? "1" : "0");
+ return ret + s + sortArrival;
+ }
+ else if (column == paintInfo->signedCol) {
+ QString 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) {
+ QString 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!!
+}
+
+QString 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( QString key ) {
+ mKey = key;
+}
+
+int HeaderItem::compare( QListViewItem *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()->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;
+}
+
+QListViewItem* HeaderItem::firstChildNonConst() /* Non const! */
+{
+ enforceSortOrder(); // Try not to rely on QListView implementation details
+ return firstChild();
+}
+