/******************************************************************************* ** ** 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 #include #include #include #include #include #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 ) : TDEListViewItem( 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 ) : TDEListViewItem( 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(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( 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( 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(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 = TDEIO::convertSize( mMsgBase->msgSizeServer() ); } else { tmp = TDEIO::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 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(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(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( &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( &headers->paintInfo()->colTodo ); font = headers->todoFont(); weight = TQMAX( weight, font.weight() ); } if ( mMsgBase->isUnread() ) { color = const_cast( &headers->paintInfo()->colUnread ); font = headers->unreadFont(); weight = TQMAX( weight, font.weight() ); } if ( mMsgBase->isNew() ) { color = const_cast( &headers->paintInfo()->colNew ); font = headers->newFont(); weight = TQMAX( weight, font.weight() ); } if ( mMsgBase->isImportant() ) { color = const_cast( &headers->paintInfo()->colFlag ); font = headers->importantFont(); weight = TQMAX( weight, font.weight() ); } if ( column == headers->paintInfo()->dateCol ) { font = headers->dateFont(); } TQColor cdisabled = TDEGlobalSettings::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 ); TDEListViewItem::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(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(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(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(); }