kmail

headeritem.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 **
00003 ** Filename   : headeritem.cpp
00004 ** Created on : 28 November, 2004
00005 ** Copyright  : (c) 2004 Till Adam
00006 ** Email      : adam@kde.org
00007 **
00008 *******************************************************************************/
00009 
00010 /*******************************************************************************
00011 **
00012 **   This program is free software; you can redistribute it and/or modify
00013 **   it under the terms of the GNU General Public License as published by
00014 **   the Free Software Foundation; either version 2 of the License, or
00015 **   (at your option) any later version.
00016 **
00017 **   In addition, as a special exception, the copyright holders give
00018 **   permission to link the code of this program with any edition of
00019 **   the Qt library by Trolltech AS, Norway (or with modified versions
00020 **   of Qt that use the same license as Qt), and distribute linked
00021 **   combinations including the two.  You must obey the GNU General
00022 **   Public License in all respects for all of the code used other than
00023 **   Qt.  If you modify this file, you may extend this exception to
00024 **   your version of the file, but you are not obligated to do so.  If
00025 **   you do not wish to do so, delete this exception statement from
00026 **   your version.
00027 **
00028 *******************************************************************************/
00029 #include <klocale.h>
00030 #include <qapplication.h>
00031 #include <qregexp.h>
00032 #include <qbitmap.h>
00033 #include <qpainter.h>
00034 
00035 #include <kio/netaccess.h>
00036 
00037 #include "headeritem.h"
00038 #include "kmheaders.h"
00039 
00040 #include "kmfolder.h"
00041 
00042 using namespace KMail;
00043 
00044 // Constuction a new list view item with the given colors and pixmap
00045 HeaderItem::HeaderItem( QListView* parent, int msgId, const QString& key )
00046   : KListViewItem( parent ),
00047   mMsgId( msgId ),
00048   mKey( key ),
00049   mAboutToBeDeleted( false ),
00050   mSortCacheItem( 0 )
00051 {
00052   irefresh();
00053 }
00054 
00055 // Constuction a new list view item with the given parent, colors, & pixmap
00056 HeaderItem::HeaderItem( QListViewItem* parent, int msgId, const QString& key )
00057   : KListViewItem( parent ),
00058   mMsgId( msgId ),
00059   mKey( key ),
00060   mAboutToBeDeleted( false ),
00061   mSortCacheItem( 0 )
00062 {
00063   irefresh();
00064 }
00065 
00066 HeaderItem::~HeaderItem ()
00067 {
00068   delete mSortCacheItem;
00069 }
00070 
00071 // Update the msgId this item corresponds to.
00072 void HeaderItem::setMsgId( int aMsgId )
00073 {
00074   mMsgId = aMsgId;
00075 }
00076 
00077 // Profiling note: About 30% of the time taken to initialize the
00078 // listview is spent in this function. About 60% is spent in operator
00079 // new and QListViewItem::QListViewItem.
00080 void HeaderItem::irefresh()
00081 {
00082   KMHeaders *headers = static_cast<KMHeaders*>(listView());
00083   NestingPolicy threadingPolicy = headers->getNestingPolicy();
00084   if ((threadingPolicy == AlwaysOpen) ||
00085       (threadingPolicy == DefaultOpen)) {
00086     //Avoid opening items as QListView is currently slow to do so.
00087     setOpen(true);
00088     return;
00089 
00090   }
00091   if (threadingPolicy == DefaultClosed)
00092     return; //default to closed
00093 
00094   // otherwise threadingPolicy == OpenUnread
00095   if (parent() && parent()->isOpen()) {
00096     setOpen(true);
00097     return;
00098   }
00099 
00100   KMMsgBase *mMsgBase = headers->folder()->getMsgBase( mMsgId );
00101   mSerNum = mMsgBase->getMsgSerNum();
00102   if (mMsgBase->isNew() || mMsgBase->isUnread()
00103       || mMsgBase->isImportant() || mMsgBase->isTodo() || mMsgBase->isWatched() ) {
00104     setOpen(true);
00105     HeaderItem * topOfThread = this;
00106     while(topOfThread->parent())
00107       topOfThread = (HeaderItem*)topOfThread->parent();
00108     topOfThread->setOpenRecursive(true);
00109   }
00110 }
00111 
00112 // Return the msgId of the message associated with this item
00113 int HeaderItem::msgId() const
00114 {
00115   return mMsgId;
00116 }
00117 
00118 // Return the serial number
00119 Q_UINT32 HeaderItem::msgSerNum() const
00120 {
00121   return mSerNum;
00122 }
00123 
00124 // Update this item to summarise a new folder and message
00125 
00126 //Opens all children in the thread
00127 void HeaderItem::setOpenRecursive( bool open )
00128 {
00129   if (open){
00130     QListViewItem * lvchild;
00131     lvchild = firstChild();
00132     while (lvchild){
00133       ((HeaderItem*)lvchild)->setOpenRecursive( true );
00134       lvchild = lvchild->nextSibling();
00135     }
00136     setOpen( true );
00137   } else {
00138     setOpen( false );
00139   }
00140 }
00141 
00142 QString HeaderItem::text( int col) const
00143 {
00144   KMHeaders *headers = static_cast<KMHeaders*>(listView());
00145   KMMsgBase *mMsgBase = headers->folder()->getMsgBase( mMsgId );
00146   QString tmp;
00147 
00148   if ( !mMsgBase )
00149     return QString();
00150 
00151   if ( col == headers->paintInfo()->senderCol ) {
00152     if ( (headers->folder()->whoField().lower() == "to") && !headers->paintInfo()->showReceiver )
00153       tmp = mMsgBase->toStrip();
00154     else
00155       tmp = mMsgBase->fromStrip();
00156     if (tmp.isEmpty())
00157       tmp = i18n("Unknown");
00158     else
00159       tmp = tmp.simplifyWhiteSpace();
00160 
00161   } else if ( col == headers->paintInfo()->receiverCol ) {
00162     tmp = mMsgBase->toStrip();
00163     if (tmp.isEmpty())
00164       tmp = i18n("Unknown");
00165     else
00166       tmp = tmp.simplifyWhiteSpace();
00167 
00168   } else if(col == headers->paintInfo()->subCol) {
00169     tmp = mMsgBase->subject();
00170     if (tmp.isEmpty())
00171       tmp = i18n("No Subject");
00172     else
00173       tmp.remove(QRegExp("[\r\n]"));
00174 
00175   } else if(col == headers->paintInfo()->dateCol) {
00176     tmp = headers->mDate.dateString( mMsgBase->date() );
00177   } else if(col == headers->paintInfo()->sizeCol
00178       && headers->paintInfo()->showSize) {
00179     if ( mMsgBase->parent()->folderType() == KMFolderTypeImap ) {
00180       tmp = KIO::convertSize( mMsgBase->msgSizeServer() );
00181     } else {
00182       tmp = KIO::convertSize( mMsgBase->msgSize() );
00183     }
00184   }
00185   return tmp;
00186 }
00187 
00188 void HeaderItem::setup()
00189 {
00190   widthChanged();
00191   const int ph = KMHeaders::pixNew->height();
00192   QListView *v = listView();
00193   int h = QMAX( v->fontMetrics().height(), ph ) + 2*v->itemMargin();
00194   h = QMAX( h, QApplication::globalStrut().height());
00195   if ( h % 2 > 0 )
00196     h++;
00197   setHeight( h );
00198 }
00199 
00200 typedef QValueList<QPixmap> PixmapList;
00201 
00202 QPixmap HeaderItem::pixmapMerge( PixmapList pixmaps ) const
00203 {
00204   int width = 0;
00205   int height = 0;
00206   for ( PixmapList::ConstIterator it = pixmaps.begin();
00207       it != pixmaps.end(); ++it ) {
00208     width += (*it).width();
00209     height = QMAX( height, (*it).height() );
00210   }
00211 
00212   QPixmap res( width, height );
00213   QBitmap mask( width, height, true );
00214 
00215   int x = 0;
00216   for ( PixmapList::ConstIterator it = pixmaps.begin();
00217       it != pixmaps.end(); ++it ) {
00218     bitBlt( &res, x, (height - (*it).height()) / 2, &(*it) );
00219     bitBlt( &mask, x, (height - (*it).height()) / 2, (*it).mask() );
00220     x += (*it).width();
00221   }
00222 
00223   res.setMask( mask );
00224   return res;
00225 }
00226 
00227 const QPixmap *HeaderItem::cryptoIcon(KMMsgBase *msgBase) const
00228 {
00229   switch ( msgBase->encryptionState() )
00230   {
00231     case KMMsgFullyEncrypted        : return KMHeaders::pixFullyEncrypted;
00232     case KMMsgPartiallyEncrypted    : return KMHeaders::pixPartiallyEncrypted;
00233     case KMMsgEncryptionStateUnknown: return KMHeaders::pixUndefinedEncrypted;
00234     case KMMsgEncryptionProblematic : return KMHeaders::pixEncryptionProblematic;
00235     default                         : return 0;
00236   }
00237 }
00238 
00239 const QPixmap *HeaderItem::signatureIcon(KMMsgBase *msgBase) const
00240 {
00241   switch ( msgBase->signatureState() )
00242   {
00243     case KMMsgFullySigned          : return KMHeaders::pixFullySigned;
00244     case KMMsgPartiallySigned      : return KMHeaders::pixPartiallySigned;
00245     case KMMsgSignatureStateUnknown: return KMHeaders::pixUndefinedSigned;
00246     case KMMsgSignatureProblematic : return KMHeaders::pixSignatureProblematic;
00247     default                        : return 0;
00248   }
00249 }
00250 
00251 const QPixmap *HeaderItem::statusIcon(KMMsgBase *msgBase) const
00252 {
00253   // forwarded, replied have precedence over the other states
00254   if (  msgBase->isForwarded() && !msgBase->isReplied() ) return KMHeaders::pixReadFwd;
00255   if ( !msgBase->isForwarded() &&  msgBase->isReplied() ) return KMHeaders::pixReadReplied;
00256   if (  msgBase->isForwarded() &&  msgBase->isReplied() ) return KMHeaders::pixReadFwdReplied;
00257 
00258   // a queued or sent mail is usually also read
00259   if ( msgBase->isQueued() ) return KMHeaders::pixQueued;
00260   if ( msgBase->isSent()   ) return KMHeaders::pixSent;
00261 
00262   if ( msgBase->isNew()                      ) return KMHeaders::pixNew;
00263   if ( msgBase->isRead() || msgBase->isOld() ) return KMHeaders::pixRead;
00264   if ( msgBase->isUnread()                   ) return KMHeaders::pixUns;
00265   if ( msgBase->isDeleted()                  ) return KMHeaders::pixDel;
00266 
00267   return 0;
00268 }
00269 
00270 const QPixmap *HeaderItem::pixmap(int col) const
00271 {
00272   KMHeaders *headers = static_cast<KMHeaders*>(listView());
00273   KMMsgBase *msgBase = headers->folder()->getMsgBase( mMsgId );
00274 
00275   if ( col == headers->paintInfo()->subCol ) {
00276 
00277     PixmapList pixmaps;
00278 
00279     if ( !headers->mPaintInfo.showSpamHam ) {
00280       // Have the spam/ham and watched/ignored icons first, I guess.
00281       if ( msgBase->isSpam() ) pixmaps << *KMHeaders::pixSpam;
00282       if ( msgBase->isHam()  ) pixmaps << *KMHeaders::pixHam;
00283     }
00284 
00285     if ( !headers->mPaintInfo.showWatchedIgnored ) {
00286       if ( msgBase->isIgnored() ) pixmaps << *KMHeaders::pixIgnored;
00287       if ( msgBase->isWatched() ) pixmaps << *KMHeaders::pixWatched;
00288     }
00289 
00290     if ( !headers->mPaintInfo.showStatus ) {
00291       const QPixmap *pix = statusIcon(msgBase);
00292       if ( pix ) pixmaps << *pix;
00293     }
00294 
00295     // Only merge the attachment icon in if that is configured.
00296     if ( headers->paintInfo()->showAttachmentIcon &&
00297         !headers->paintInfo()->showAttachment &&
00298         msgBase->attachmentState() == KMMsgHasAttachment )
00299       pixmaps << *KMHeaders::pixAttachment;
00300 
00301     // Only merge the crypto icons in if that is configured.
00302     if ( headers->paintInfo()->showCryptoIcons ) {
00303       const QPixmap *pix;
00304 
00305       if ( !headers->paintInfo()->showCrypto )
00306         if ( (pix = cryptoIcon(msgBase))    ) pixmaps << *pix;
00307 
00308       if ( !headers->paintInfo()->showSigned )
00309         if ( (pix = signatureIcon(msgBase)) ) pixmaps << *pix;
00310     }
00311 
00312     if ( !headers->mPaintInfo.showImportant )
00313       if ( msgBase->isImportant() ) pixmaps << *KMHeaders::pixFlag;
00314 
00315     if ( !headers->mPaintInfo.showTodo )
00316       if ( msgBase->isTodo() ) pixmaps << *KMHeaders::pixTodo;
00317 
00318     static QPixmap mergedpix;
00319     mergedpix = pixmapMerge( pixmaps );
00320     return &mergedpix;
00321   }
00322   else if ( col == headers->paintInfo()->statusCol ) {
00323     return statusIcon(msgBase);
00324   }
00325   else if ( col == headers->paintInfo()->attachmentCol ) {
00326     if ( msgBase->attachmentState() == KMMsgHasAttachment )
00327       return KMHeaders::pixAttachment;
00328   }
00329   else if ( col == headers->paintInfo()->importantCol ) {
00330     if ( msgBase->isImportant() )
00331       return KMHeaders::pixFlag;
00332   }
00333   else if ( col == headers->paintInfo()->todoCol ) {
00334     if ( msgBase->isTodo() )
00335       return KMHeaders::pixTodo;
00336   }
00337   else if ( col == headers->paintInfo()->spamHamCol ) {
00338     if ( msgBase->isSpam() ) return KMHeaders::pixSpam;
00339     if ( msgBase->isHam()  ) return KMHeaders::pixHam;
00340   }
00341   else if ( col == headers->paintInfo()->watchedIgnoredCol ) {
00342     if ( msgBase->isWatched() ) return KMHeaders::pixWatched;
00343     if ( msgBase->isIgnored() ) return KMHeaders::pixIgnored;
00344   }
00345   else if ( col == headers->paintInfo()->signedCol ) {
00346     return signatureIcon(msgBase);
00347   }
00348   else if ( col == headers->paintInfo()->cryptoCol ) {
00349     return cryptoIcon(msgBase);
00350   }
00351   return 0;
00352 }
00353 
00354 void HeaderItem::paintCell( QPainter * p, const QColorGroup & cg,
00355     int column, int width, int align )
00356 {
00357   KMHeaders *headers = static_cast<KMHeaders*>(listView());
00358   if (headers->noRepaint) return;
00359   if (!headers->folder()) return;
00360   KMMsgBase *mMsgBase = headers->folder()->getMsgBase( mMsgId );
00361   if (!mMsgBase) return;
00362 
00363   QColorGroup _cg( cg );
00364   QColor c = _cg.text();
00365   QColor *color = const_cast<QColor *>( &headers->paintInfo()->colFore );
00366   QFont font = p->font();
00367   int weight = font.weight();
00368 
00369   // for color and font family "important" overrides "new" overrides "unread"
00370   // overrides "todo" for the weight we use the maximal weight
00371   if ( mMsgBase->isTodo() ) {
00372     color = const_cast<QColor*>( &headers->paintInfo()->colTodo );
00373     font = headers->todoFont();
00374     weight = QMAX( weight, font.weight() );
00375   }
00376   if ( mMsgBase->isUnread() ) {
00377     color = const_cast<QColor*>( &headers->paintInfo()->colUnread );
00378     font = headers->unreadFont();
00379     weight = QMAX( weight, font.weight() );
00380   }
00381   if ( mMsgBase->isNew() ) {
00382     color = const_cast<QColor*>( &headers->paintInfo()->colNew );
00383     font = headers->newFont();
00384     weight = QMAX( weight, font.weight() );
00385   }
00386 
00387   if ( mMsgBase->isImportant() ) {
00388     color = const_cast<QColor*>( &headers->paintInfo()->colFlag );
00389     font = headers->importantFont();
00390     weight = QMAX( weight, font.weight() );
00391   }
00392   if ( column == headers->paintInfo()->dateCol ) {
00393     font = headers->dateFont();
00394   }
00395 
00396   QColor cdisabled = KGlobalSettings::inactiveTextColor();
00397   if ( headers->isMessageCut( msgSerNum() ) ) {
00398     font.setItalic( true );
00399     color = &cdisabled;
00400   }
00401 
00402   // set color and font
00403   _cg.setColor( QColorGroup::Text, *color );
00404   font.setWeight( weight );
00405   p->setFont( font );
00406 
00407   KListViewItem::paintCell( p, _cg, column, width, align );
00408 
00409   if (aboutToBeDeleted()) {
00410     // strike through
00411     p->drawLine( 0, height()/2, width, height()/2);
00412   }
00413 
00414   // reset color
00415   _cg.setColor( QColorGroup::Text, c );
00416 }
00417 
00418 QString HeaderItem::generate_key( KMHeaders *headers,
00419     KMMsgBase *msg,
00420     const KPaintInfo *paintInfo,
00421     int sortOrder )
00422 {
00423   // It appears, that QListView in Qt-3.0 asks for the key
00424   // in QListView::clear(), which is called from
00425   // readSortOrder()
00426   if (!msg) return QString::null;
00427 
00428   int column = sortOrder & ((1 << 5) - 1);
00429   QString ret = QChar( (char)sortOrder );
00430   QString sortArrival = QString( "%1" ).arg( msg->getMsgSerNum(), 0, 36 );
00431   while (sortArrival.length() < 7) sortArrival = '0' + sortArrival;
00432 
00433   if (column == paintInfo->dateCol) {
00434     if (paintInfo->orderOfArrival)
00435       return ret + sortArrival;
00436     else {
00437       QString d = QString::number(msg->date());
00438       while (d.length() <= 10) d = '0' + d;
00439       return ret + d + sortArrival;
00440     }
00441   } else if (column == paintInfo->senderCol) {
00442     QString tmp;
00443     if ( (headers->folder()->whoField().lower() == "to") && !headers->paintInfo()->showReceiver )
00444       tmp = msg->toStrip();
00445     else
00446       tmp = msg->fromStrip();
00447     return ret + tmp.lower() + ' ' + sortArrival;
00448   } else if (column == paintInfo->receiverCol) {
00449     QString tmp = msg->toStrip();
00450     return ret + tmp.lower() + ' ' + sortArrival;
00451   } else if (column == paintInfo->subCol) {
00452     QString tmp;
00453     tmp = ret;
00454     if (paintInfo->status) {
00455       tmp += msg->statusToSortRank() + ' ';
00456     }
00457     tmp += KMMessage::stripOffPrefixes( msg->subject().lower() ) + ' ' + sortArrival;
00458     return tmp;
00459   }
00460   else if (column == paintInfo->sizeCol) {
00461     QString len;
00462     if ( msg->parent()->folderType() == KMFolderTypeImap )
00463     {
00464       len = QString::number( msg->msgSizeServer() );
00465     } else {
00466       len = QString::number( msg->msgSize() );
00467     }
00468     while (len.length() < 9) len = '0' + len;
00469     return ret + len + sortArrival;
00470   }
00471   else if (column == paintInfo->statusCol) {
00472     QString s;
00473     if      ( msg->isNew()                            ) s = "1";
00474     else if ( msg->isUnread()                         ) s = "2";
00475     else if (!msg->isForwarded() &&  msg->isReplied() ) s = "3";
00476     else if ( msg->isForwarded() &&  msg->isReplied() ) s = "4";
00477     else if ( msg->isForwarded() && !msg->isReplied() ) s = "5";
00478     else if ( msg->isRead() || msg->isOld()           ) s = "6";
00479     else if ( msg->isQueued()                         ) s = "7";
00480     else if ( msg->isSent()                           ) s = "8";
00481     else if ( msg->isDeleted()                        ) s = "9";
00482     return ret + s + sortArrival;
00483   }
00484   else if (column == paintInfo->attachmentCol) {
00485     QString s(msg->attachmentState() == KMMsgHasAttachment ? "1" : "0");
00486     return ret + s + sortArrival;
00487   }
00488   else if (column == paintInfo->importantCol) {
00489     QString s(msg->isImportant() ? "1" : "0");
00490     return ret + s + sortArrival;
00491   }
00492   else if ( column == paintInfo->todoCol ) {
00493     QString s( msg->isTodo() ? "1": "0" );
00494     return ret + s + sortArrival;
00495   }
00496   else if (column == paintInfo->spamHamCol) {
00497     QString s((msg->isSpam() || msg->isHam()) ? "1" : "0");
00498     return ret + s + sortArrival;
00499   }
00500   else if (column == paintInfo->watchedIgnoredCol) {
00501     QString s((msg->isWatched() || msg->isIgnored()) ? "1" : "0");
00502     return ret + s + sortArrival;
00503   }
00504   else if (column == paintInfo->signedCol) {
00505     QString s;
00506     switch ( msg->signatureState() )
00507     {
00508       case KMMsgFullySigned          : s = "1"; break;
00509       case KMMsgPartiallySigned      : s = "2"; break;
00510       case KMMsgSignatureStateUnknown: s = "3"; break;
00511       case KMMsgSignatureProblematic : s = "4"; break;
00512       default                        : s = "5"; break;
00513     }
00514     return ret + s + sortArrival;
00515   }
00516   else if (column == paintInfo->cryptoCol) {
00517     QString s;
00518     switch ( msg->encryptionState() )
00519     {
00520       case KMMsgFullyEncrypted        : s = "1"; break;
00521       case KMMsgPartiallyEncrypted    : s = "2"; break;
00522       case KMMsgEncryptionStateUnknown: s = "3"; break;
00523       case KMMsgEncryptionProblematic : s = "4"; break;
00524       default                         : s = "5"; break;
00525     }
00526     return ret + s + sortArrival;
00527   }
00528   return ret + "missing key"; //you forgot something!!
00529 }
00530 
00531 QString HeaderItem::key( int column, bool /*ascending*/ ) const
00532 {
00533   KMHeaders *headers = static_cast<KMHeaders*>(listView());
00534   int sortOrder = column;
00535   if (headers->mPaintInfo.orderOfArrival)
00536     sortOrder |= (1 << 6);
00537   if (headers->mPaintInfo.status)
00538     sortOrder |= (1 << 5);
00539   //This code should stay pretty much like this, if you are adding new
00540   //columns put them in generate_key
00541   if(mKey.isEmpty() || mKey[0] != (char)sortOrder) {
00542     KMHeaders *headers = static_cast<KMHeaders*>(listView());
00543     KMMsgBase *msgBase = headers->folder()->getMsgBase( mMsgId );
00544     return ((HeaderItem *)this)->mKey =
00545       generate_key( headers, msgBase, headers->paintInfo(), sortOrder );
00546   }
00547   return mKey;
00548 }
00549 
00550 void HeaderItem::setTempKey( QString key ) {
00551   mKey = key;
00552 }
00553 
00554 int HeaderItem::compare( QListViewItem *i, int col, bool ascending ) const
00555 {
00556   int res = 0;
00557   KMHeaders *headers = static_cast<KMHeaders*>(listView());
00558   if ( ( col == headers->paintInfo()->statusCol         ) ||
00559       ( col == headers->paintInfo()->sizeCol           ) ||
00560       ( col == headers->paintInfo()->attachmentCol     ) ||
00561       ( col == headers->paintInfo()->importantCol      ) ||
00562       ( col == headers->paintInfo()->todoCol           ) ||
00563       ( col == headers->paintInfo()->spamHamCol        ) ||
00564       ( col == headers->paintInfo()->signedCol         ) ||
00565       ( col == headers->paintInfo()->cryptoCol         ) ||
00566       ( col == headers->paintInfo()->watchedIgnoredCol ) ) {
00567     res = key( col, ascending ).compare( i->key( col, ascending ) );
00568   } else if ( col == headers->paintInfo()->dateCol ) {
00569     res = key( col, ascending ).compare( i->key( col, ascending ) );
00570     if (i->parent() && !ascending)
00571       res = -res;
00572   } else if ( col == headers->paintInfo()->subCol ||
00573       col == headers->paintInfo()->senderCol ||
00574       col == headers->paintInfo()->receiverCol ) {
00575     res = key( col, ascending ).localeAwareCompare( i->key( col, ascending ) );
00576   }
00577   return res;
00578 }
00579 
00580 QListViewItem* HeaderItem::firstChildNonConst() /* Non const! */
00581 {
00582   enforceSortOrder(); // Try not to rely on QListView implementation details
00583   return firstChild();
00584 }
00585