00001
00002
00003
00004 #include <config.h>
00005
00006 #include "kmheaders.h"
00007
00008 #include "kcursorsaver.h"
00009 #include "kmcommands.h"
00010 #include "kmfolderimap.h"
00011 #include "kmmainwidget.h"
00012 #include "kmcomposewin.h"
00013 #include "kmfiltermgr.h"
00014 #include "undostack.h"
00015 #include "kmmsgdict.h"
00016 #include "kmkernel.h"
00017 #include "kmdebug.h"
00018 using KMail::FolderJob;
00019 #include "broadcaststatus.h"
00020 using KPIM::BroadcastStatus;
00021 #include "actionscheduler.h"
00022 using KMail::ActionScheduler;
00023 #include <maillistdrag.h>
00024 #include "globalsettings.h"
00025 using namespace KPIM;
00026
00027 #include <kapplication.h>
00028 #include <kaccelmanager.h>
00029 #include <kglobalsettings.h>
00030 #include <kmessagebox.h>
00031 #include <kiconloader.h>
00032 #include <kimageio.h>
00033 #include <kconfig.h>
00034 #include <klocale.h>
00035 #include <kdebug.h>
00036
00037 #include <qbuffer.h>
00038 #include <qfile.h>
00039 #include <qheader.h>
00040 #include <qptrstack.h>
00041 #include <qptrqueue.h>
00042 #include <qpainter.h>
00043 #include <qtextcodec.h>
00044 #include <qbitmap.h>
00045 #include <qstyle.h>
00046 #include <qlistview.h>
00047 #include <qregexp.h>
00048
00049 #include <mimelib/enum.h>
00050 #include <mimelib/field.h>
00051 #include <mimelib/mimepp.h>
00052
00053 #include <stdlib.h>
00054 #include <errno.h>
00055
00056 QPixmap* KMHeaders::pixNew = 0;
00057 QPixmap* KMHeaders::pixUns = 0;
00058 QPixmap* KMHeaders::pixDel = 0;
00059 QPixmap* KMHeaders::pixRead = 0;
00060 QPixmap* KMHeaders::pixRep = 0;
00061 QPixmap* KMHeaders::pixQueued = 0;
00062 QPixmap* KMHeaders::pixSent = 0;
00063 QPixmap* KMHeaders::pixFwd = 0;
00064 QPixmap* KMHeaders::pixFlag = 0;
00065 QPixmap* KMHeaders::pixWatched = 0;
00066 QPixmap* KMHeaders::pixIgnored = 0;
00067 QPixmap* KMHeaders::pixSpam = 0;
00068 QPixmap* KMHeaders::pixHam = 0;
00069 QPixmap* KMHeaders::pixFullySigned = 0;
00070 QPixmap* KMHeaders::pixPartiallySigned = 0;
00071 QPixmap* KMHeaders::pixUndefinedSigned = 0;
00072 QPixmap* KMHeaders::pixFullyEncrypted = 0;
00073 QPixmap* KMHeaders::pixPartiallyEncrypted = 0;
00074 QPixmap* KMHeaders::pixUndefinedEncrypted = 0;
00075 QPixmap* KMHeaders::pixEncryptionProblematic = 0;
00076 QPixmap* KMHeaders::pixSignatureProblematic = 0;
00077 QPixmap* KMHeaders::pixAttachment = 0;
00078 QPixmap* KMHeaders::pixTodo = 0;
00079 QPixmap* KMHeaders::pixReadFwd = 0;
00080 QPixmap* KMHeaders::pixReadReplied = 0;
00081 QPixmap* KMHeaders::pixReadFwdReplied = 0;
00082
00083 #define KMAIL_SORT_VERSION 1012
00084 #define KMAIL_SORT_FILE(x) x->indexLocation() + ".sorted"
00085 #define KMAIL_SORT_HEADER "## KMail Sort V%04d\n\t"
00086 #define KMAIL_MAGIC_HEADER_OFFSET 21 //strlen(KMAIL_SORT_HEADER)
00087 #define KMAIL_MAX_KEY_LEN 16384
00088 #define KMAIL_RESERVED 3
00089
00090
00091 class KMSortCacheItem {
00092 KMHeaderItem *mItem;
00093 KMSortCacheItem *mParent;
00094 int mId, mSortOffset;
00095 QString mKey;
00096
00097 QPtrList<KMSortCacheItem> mSortedChildren;
00098 int mUnsortedCount, mUnsortedSize;
00099 KMSortCacheItem **mUnsortedChildren;
00100 bool mImperfectlyThreaded;
00101
00102 public:
00103 KMSortCacheItem() : mItem(0), mParent(0), mId(-1), mSortOffset(-1),
00104 mUnsortedCount(0), mUnsortedSize(0), mUnsortedChildren(0),
00105 mImperfectlyThreaded (true) { }
00106 KMSortCacheItem(int i, QString k, int o=-1)
00107 : mItem(0), mParent(0), mId(i), mSortOffset(o), mKey(k),
00108 mUnsortedCount(0), mUnsortedSize(0), mUnsortedChildren(0),
00109 mImperfectlyThreaded (true) { }
00110 ~KMSortCacheItem() { if(mUnsortedChildren) free(mUnsortedChildren); }
00111
00112 KMSortCacheItem *parent() const { return mParent; }
00113 bool isImperfectlyThreaded() const
00114 { return mImperfectlyThreaded; }
00115 void setImperfectlyThreaded (bool val)
00116 { mImperfectlyThreaded = val; }
00117 bool hasChildren() const
00118 { return mSortedChildren.count() || mUnsortedCount; }
00119 const QPtrList<KMSortCacheItem> *sortedChildren() const
00120 { return &mSortedChildren; }
00121 KMSortCacheItem **unsortedChildren(int &count) const
00122 { count = mUnsortedCount; return mUnsortedChildren; }
00123 void addSortedChild(KMSortCacheItem *i) {
00124 i->mParent = this;
00125 mSortedChildren.append(i);
00126 }
00127 void addUnsortedChild(KMSortCacheItem *i) {
00128 i->mParent = this;
00129 if(!mUnsortedChildren)
00130 mUnsortedChildren = (KMSortCacheItem **)malloc((mUnsortedSize = 25) * sizeof(KMSortCacheItem *));
00131 else if(mUnsortedCount >= mUnsortedSize)
00132 mUnsortedChildren = (KMSortCacheItem **)realloc(mUnsortedChildren,
00133 (mUnsortedSize *= 2) * sizeof(KMSortCacheItem *));
00134 mUnsortedChildren[mUnsortedCount++] = i;
00135 }
00136
00137 KMHeaderItem *item() const { return mItem; }
00138 void setItem(KMHeaderItem *i) { Q_ASSERT(!mItem); mItem = i; }
00139
00140 const QString &key() const { return mKey; }
00141 void setKey(const QString &key) { mKey = key; }
00142
00143 int id() const { return mId; }
00144 void setId(int id) { mId = id; }
00145
00146 int offset() const { return mSortOffset; }
00147 void setOffset(int x) { mSortOffset = x; }
00148
00149 void updateSortFile( FILE *sortStream, KMFolder *folder,
00150 bool waiting_for_parent = false,
00151 bool update_discovered_count = false);
00152 };
00153
00154
00155
00156
00157
00158 class KMHeaderItem : public KListViewItem
00159 {
00160
00161 public:
00162 int mMsgId;
00163 QString mKey;
00164
00165
00166
00167 KMHeaderItem( QListView* parent, int msgId, const QString& key = QString::null )
00168 : KListViewItem( parent ),
00169 mMsgId( msgId ),
00170 mKey( key ),
00171 mAboutToBeDeleted( false ),
00172 mSortCacheItem( 0 )
00173 {
00174 irefresh();
00175 }
00176
00177
00178 KMHeaderItem( QListViewItem* parent, int msgId, const QString& key = QString::null )
00179 : KListViewItem( parent ),
00180 mMsgId( msgId ),
00181 mKey( key ),
00182 mAboutToBeDeleted( false ),
00183 mSortCacheItem( 0 )
00184 {
00185 irefresh();
00186 }
00187
00188 ~KMHeaderItem ()
00189 {
00190 delete mSortCacheItem;
00191 }
00192
00193
00194 void setMsgId( int aMsgId )
00195 {
00196 mMsgId = aMsgId;
00197 }
00198
00199
00200
00201
00202 void irefresh()
00203 {
00204 KMHeaders *headers = static_cast<KMHeaders*>(listView());
00205 NestingPolicy threadingPolicy = headers->getNestingPolicy();
00206 if ((threadingPolicy == AlwaysOpen) ||
00207 (threadingPolicy == DefaultOpen)) {
00208
00209 setOpen(true);
00210 return;
00211
00212 }
00213 if (threadingPolicy == DefaultClosed)
00214 return;
00215
00216
00217 if (parent() && parent()->isOpen()) {
00218 setOpen(true);
00219 return;
00220 }
00221
00222 KMMsgBase *mMsgBase = headers->folder()->getMsgBase( mMsgId );
00223 if (mMsgBase->isNew() || mMsgBase->isUnread()
00224 || mMsgBase->isImportant() || mMsgBase->isWatched() ) {
00225 setOpen(true);
00226 KMHeaderItem * topOfThread = this;
00227 while(topOfThread->parent())
00228 topOfThread = (KMHeaderItem*)topOfThread->parent();
00229 topOfThread->setOpenRecursive(true);
00230 }
00231 }
00232
00233
00234 int msgId() const
00235 {
00236 return mMsgId;
00237 }
00238
00239
00240 void reset( int aMsgId )
00241 {
00242 mMsgId = aMsgId;
00243 irefresh();
00244 }
00245
00246
00247 void setOpenRecursive( bool open )
00248 {
00249 if (open){
00250 QListViewItem * lvchild;
00251 lvchild = firstChild();
00252 while (lvchild){
00253 ((KMHeaderItem*)lvchild)->setOpenRecursive( true );
00254 lvchild = lvchild->nextSibling();
00255 }
00256 setOpen( true );
00257 } else {
00258 setOpen( false );
00259 }
00260 }
00261
00262 QString text( int col) const
00263 {
00264 KMHeaders *headers = static_cast<KMHeaders*>(listView());
00265 KMMsgBase *mMsgBase = headers->folder()->getMsgBase( mMsgId );
00266 if ( !mMsgBase ) return QString();
00267 QString tmp;
00268
00269
00270 if(col == headers->paintInfo()->flagCol) {
00271 if (headers->paintInfo()->flagCol >= 0)
00272 tmp = QString( QChar( (char)mMsgBase->status() ));
00273
00274 } else if(col == headers->paintInfo()->senderCol) {
00275 if ( (headers->folder()->whoField().lower() == "to") && !headers->paintInfo()->showReceiver )
00276 tmp = mMsgBase->toStrip();
00277 else
00278 tmp = mMsgBase->fromStrip();
00279 if (tmp.isEmpty())
00280 tmp = i18n("Unknown");
00281 else
00282 tmp = tmp.simplifyWhiteSpace();
00283
00284 } else if ( col == headers->paintInfo()->receiverCol ) {
00285 tmp = mMsgBase->toStrip();
00286 if (tmp.isEmpty())
00287 tmp = i18n("Unknown");
00288 else
00289 tmp = tmp.simplifyWhiteSpace();
00290
00291 } else if(col == headers->paintInfo()->subCol) {
00292 tmp = mMsgBase->subject();
00293 if (tmp.isEmpty())
00294 tmp = i18n("No Subject");
00295 else
00296 tmp.remove(QRegExp("[\r\n]"));
00297
00298 } else if(col == headers->paintInfo()->dateCol) {
00299 tmp = headers->mDate.dateString( mMsgBase->date() );
00300 } else if(col == headers->paintInfo()->sizeCol
00301 && headers->paintInfo()->showSize) {
00302 if ( mMsgBase->parent()->folderType() == KMFolderTypeImap ) {
00303 tmp = KIO::convertSize( mMsgBase->msgSizeServer() );
00304 } else {
00305 tmp = KIO::convertSize( mMsgBase->msgSize() );
00306 }
00307 }
00308 return tmp;
00309 }
00310
00311 void setup()
00312 {
00313 widthChanged();
00314 const int ph = KMHeaders::pixNew->height();
00315 QListView *v = listView();
00316 int h = QMAX( v->fontMetrics().height(), ph ) + 2*v->itemMargin();
00317 h = QMAX( h, QApplication::globalStrut().height());
00318 if ( h % 2 > 0 )
00319 h++;
00320 setHeight( h );
00321 }
00322
00323 typedef QValueList<QPixmap> PixmapList;
00324
00325 QPixmap pixmapMerge( PixmapList pixmaps ) const {
00326 int width = 0;
00327 int height = 0;
00328 for ( PixmapList::ConstIterator it = pixmaps.begin();
00329 it != pixmaps.end(); ++it ) {
00330 width += (*it).width();
00331 height = QMAX( height, (*it).height() );
00332 }
00333
00334 QPixmap res( width, height );
00335 QBitmap mask( width, height );
00336
00337 int x = 0;
00338 for ( PixmapList::ConstIterator it = pixmaps.begin();
00339 it != pixmaps.end(); ++it ) {
00340 bitBlt( &res, x, 0, &(*it) );
00341 bitBlt( &mask, x, 0, (*it).mask() );
00342 x += (*it).width();
00343 }
00344
00345 res.setMask( mask );
00346 return res;
00347 }
00348
00349
00350
00351 const QPixmap * cryptoIcon(KMMsgBase *msgBase) const
00352 {
00353 switch ( msgBase->encryptionState() )
00354 {
00355 case KMMsgFullyEncrypted : return KMHeaders::pixFullyEncrypted;
00356 case KMMsgPartiallyEncrypted : return KMHeaders::pixPartiallyEncrypted;
00357 case KMMsgEncryptionStateUnknown: return KMHeaders::pixUndefinedEncrypted;
00358 case KMMsgEncryptionProblematic : return KMHeaders::pixEncryptionProblematic;
00359 default : return 0;
00360 }
00361 }
00362
00363 const QPixmap * signatureIcon(KMMsgBase *msgBase) const
00364 {
00365 switch ( msgBase->signatureState() )
00366 {
00367 case KMMsgFullySigned : return KMHeaders::pixFullySigned;
00368 case KMMsgPartiallySigned : return KMHeaders::pixPartiallySigned;
00369 case KMMsgSignatureStateUnknown: return KMHeaders::pixUndefinedSigned;
00370 case KMMsgSignatureProblematic : return KMHeaders::pixSignatureProblematic;
00371 default : return 0;
00372 }
00373 }
00374
00375 const QPixmap * statusIcon(KMMsgBase *msgBase) const
00376 {
00377
00378 if ( msgBase->isForwarded() && !msgBase->isReplied() ) return KMHeaders::pixReadFwd;
00379 if ( !msgBase->isForwarded() && msgBase->isReplied() ) return KMHeaders::pixReadReplied;
00380 if ( msgBase->isForwarded() && msgBase->isReplied() ) return KMHeaders::pixReadFwdReplied;
00381
00382
00383 if ( msgBase->isQueued() ) return KMHeaders::pixQueued;
00384 if ( msgBase->isSent() ) return KMHeaders::pixSent;
00385
00386 if ( msgBase->isNew() ) return KMHeaders::pixNew;
00387 if ( msgBase->isRead() || msgBase->isOld() ) return KMHeaders::pixRead;
00388 if ( msgBase->isUnread() ) return KMHeaders::pixUns;
00389 if ( msgBase->isDeleted() ) return KMHeaders::pixDel;
00390
00391 return 0;
00392 }
00393
00394
00395 const QPixmap * pixmap(int col) const
00396 {
00397 KMHeaders *headers = static_cast<KMHeaders*>(listView());
00398 KMMsgBase *msgBase = headers->folder()->getMsgBase( mMsgId );
00399
00400 if ( col == headers->paintInfo()->subCol ) {
00401
00402 PixmapList pixmaps;
00403
00404 if ( !headers->mPaintInfo.showSpamHam ) {
00405
00406 if ( msgBase->isSpam() ) pixmaps << *KMHeaders::pixSpam;
00407 if ( msgBase->isHam() ) pixmaps << *KMHeaders::pixHam;
00408 }
00409
00410 if ( !headers->mPaintInfo.showWatchedIgnored ) {
00411 if ( msgBase->isIgnored() ) pixmaps << *KMHeaders::pixIgnored;
00412 if ( msgBase->isWatched() ) pixmaps << *KMHeaders::pixWatched;
00413 }
00414
00415 if ( !headers->mPaintInfo.showStatus ) {
00416 const QPixmap *pix = statusIcon(msgBase);
00417 if ( pix ) pixmaps << *pix;
00418 }
00419
00420
00421 if ( headers->paintInfo()->showAttachmentIcon &&
00422 !headers->paintInfo()->showAttachment &&
00423 msgBase->attachmentState() == KMMsgHasAttachment )
00424 pixmaps << *KMHeaders::pixAttachment;
00425
00426
00427 if ( headers->paintInfo()->showCryptoIcons ) {
00428 const QPixmap *pix;
00429
00430 if ( !headers->paintInfo()->showCrypto )
00431 if ( (pix = cryptoIcon(msgBase)) ) pixmaps << *pix;
00432
00433 if ( !headers->paintInfo()->showSigned )
00434 if ( (pix = signatureIcon(msgBase)) ) pixmaps << *pix;
00435 }
00436
00437 if ( !headers->mPaintInfo.showImportant )
00438 if ( msgBase->isImportant() ) pixmaps << *KMHeaders::pixFlag;
00439
00440 if ( !headers->mPaintInfo.showTodo )
00441 if ( msgBase->isTodo() ) pixmaps << *KMHeaders::pixTodo;
00442
00443 static QPixmap mergedpix;
00444 mergedpix = pixmapMerge( pixmaps );
00445 return &mergedpix;
00446 }
00447 else if ( col == headers->paintInfo()->statusCol ) {
00448 return statusIcon(msgBase);
00449 }
00450 else if ( col == headers->paintInfo()->attachmentCol ) {
00451 if ( msgBase->attachmentState() == KMMsgHasAttachment )
00452 return KMHeaders::pixAttachment;
00453 }
00454 else if ( col == headers->paintInfo()->importantCol ) {
00455 if ( msgBase->isImportant() )
00456 return KMHeaders::pixFlag;
00457 }
00458 else if ( col == headers->paintInfo()->todoCol ) {
00459 if ( msgBase->isTodo() )
00460 return KMHeaders::pixTodo;
00461 }
00462 else if ( col == headers->paintInfo()->spamHamCol ) {
00463 if ( msgBase->isSpam() ) return KMHeaders::pixSpam;
00464 if ( msgBase->isHam() ) return KMHeaders::pixHam;
00465 }
00466 else if ( col == headers->paintInfo()->watchedIgnoredCol ) {
00467 if ( msgBase->isWatched() ) return KMHeaders::pixWatched;
00468 if ( msgBase->isIgnored() ) return KMHeaders::pixIgnored;
00469 }
00470 else if ( col == headers->paintInfo()->signedCol ) {
00471 return signatureIcon(msgBase);
00472 }
00473 else if ( col == headers->paintInfo()->cryptoCol ) {
00474 return cryptoIcon(msgBase);
00475 }
00476 return 0;
00477 }
00478
00479
00480 void paintCell( QPainter * p, const QColorGroup & cg,
00481 int column, int width, int align )
00482 {
00483 KMHeaders *headers = static_cast<KMHeaders*>(listView());
00484 if (headers->noRepaint) return;
00485 if (!headers->folder()) return;
00486 QColorGroup _cg( cg );
00487 QColor c = _cg.text();
00488 QColor *color;
00489
00490 KMMsgBase *mMsgBase = headers->folder()->getMsgBase( mMsgId );
00491 if (!mMsgBase) return;
00492
00493 color = (QColor *)(&headers->paintInfo()->colFore);
00494
00495 if (mMsgBase->isUnread()) color = (QColor*)(&headers->paintInfo()->colUnread);
00496 if (mMsgBase->isNew()) color = (QColor*)(&headers->paintInfo()->colNew);
00497 if (mMsgBase->isImportant()) color = (QColor*)(&headers->paintInfo()->colFlag);
00498 if (mMsgBase->isTodo()) color = (QColor*)(&headers->paintInfo()->colTodo);
00499
00500 _cg.setColor( QColorGroup::Text, *color );
00501
00502 if( column == headers->paintInfo()->dateCol )
00503 p->setFont(headers->dateFont);
00504
00505 KListViewItem::paintCell( p, _cg, column, width, align );
00506
00507 if (aboutToBeDeleted()) {
00508
00509 p->drawLine( 0, height()/2, width, height()/2);
00510 }
00511 _cg.setColor( QColorGroup::Text, c );
00512 }
00513
00514 static QString generate_key( KMHeaders *headers, KMMsgBase *msg, const KPaintInfo *paintInfo, int sortOrder )
00515 {
00516
00517
00518
00519 if (!msg) return QString::null;
00520
00521 int column = sortOrder & ((1 << 5) - 1);
00522 QString ret = QChar( (char)sortOrder );
00523 QString sortArrival = QString( "%1" ).arg( msg->getMsgSerNum(), 0, 36 );
00524 while (sortArrival.length() < 7) sortArrival = '0' + sortArrival;
00525
00526 if (column == paintInfo->dateCol) {
00527 if (paintInfo->orderOfArrival)
00528 return ret + sortArrival;
00529 else {
00530 QString d = QString::number(msg->date());
00531 while (d.length() <= 10) d = '0' + d;
00532 return ret + d + sortArrival;
00533 }
00534 } else if (column == paintInfo->senderCol) {
00535 QString tmp;
00536 if ( (headers->folder()->whoField().lower() == "to") && !headers->paintInfo()->showReceiver )
00537 tmp = msg->toStrip();
00538 else
00539 tmp = msg->fromStrip();
00540 return ret + tmp.lower() + ' ' + sortArrival;
00541 } else if (column == paintInfo->receiverCol) {
00542 QString tmp = msg->toStrip();
00543 return ret + tmp.lower() + ' ' + sortArrival;
00544 } else if (column == paintInfo->subCol) {
00545 QString tmp;
00546 tmp = ret;
00547 if (paintInfo->status) {
00548 tmp += msg->statusToSortRank() + ' ';
00549 }
00550 tmp += KMMessage::stripOffPrefixes( msg->subject().lower() ) + ' ' + sortArrival;
00551 return tmp;
00552 }
00553 else if (column == paintInfo->sizeCol) {
00554 QString len;
00555 if ( msg->parent()->folderType() == KMFolderTypeImap )
00556 {
00557 len = QString::number( msg->msgSizeServer() );
00558 } else {
00559 len = QString::number( msg->msgSize() );
00560 }
00561 while (len.length() < 9) len = '0' + len;
00562 return ret + len + sortArrival;
00563 }
00564 else if (column == paintInfo->statusCol) {
00565 QString s;
00566 if ( msg->isNew() ) s = "1";
00567 else if ( msg->isUnread() ) s = "2";
00568 else if (!msg->isForwarded() && msg->isReplied() ) s = "3";
00569 else if ( msg->isForwarded() && msg->isReplied() ) s = "4";
00570 else if ( msg->isForwarded() && !msg->isReplied() ) s = "5";
00571 else if ( msg->isRead() || msg->isOld() ) s = "6";
00572 else if ( msg->isQueued() ) s = "7";
00573 else if ( msg->isSent() ) s = "8";
00574 else if ( msg->isDeleted() ) s = "9";
00575 return ret + s + sortArrival;
00576 }
00577 else if (column == paintInfo->attachmentCol) {
00578 QString s(msg->attachmentState() == KMMsgHasAttachment ? "1" : "0");
00579 return ret + s + sortArrival;
00580 }
00581 else if (column == paintInfo->importantCol) {
00582 QString s(msg->isImportant() ? "1" : "0");
00583 return ret + s + sortArrival;
00584 }
00585 else if ( column == paintInfo->todoCol ) {
00586 QString s( msg->isTodo() ? "1": "0" );
00587 return ret + s + sortArrival;
00588 }
00589 else if (column == paintInfo->spamHamCol) {
00590 QString s((msg->isSpam() || msg->isHam()) ? "1" : "0");
00591 return ret + s + sortArrival;
00592 }
00593 else if (column == paintInfo->watchedIgnoredCol) {
00594 QString s((msg->isWatched() || msg->isIgnored()) ? "1" : "0");
00595 return ret + s + sortArrival;
00596 }
00597 else if (column == paintInfo->signedCol) {
00598 QString s;
00599 switch ( msg->signatureState() )
00600 {
00601 case KMMsgFullySigned : s = "1"; break;
00602 case KMMsgPartiallySigned : s = "2"; break;
00603 case KMMsgSignatureStateUnknown: s = "3"; break;
00604 case KMMsgSignatureProblematic : s = "4"; break;
00605 default : s = "5"; break;
00606 }
00607 return ret + s + sortArrival;
00608 }
00609 else if (column == paintInfo->cryptoCol) {
00610 QString s;
00611 switch ( msg->encryptionState() )
00612 {
00613 case KMMsgFullyEncrypted : s = "1"; break;
00614 case KMMsgPartiallyEncrypted : s = "2"; break;
00615 case KMMsgEncryptionStateUnknown: s = "3"; break;
00616 case KMMsgEncryptionProblematic : s = "4"; break;
00617 default : s = "5"; break;
00618 }
00619 return ret + s + sortArrival;
00620 }
00621 return ret + "missing key";
00622 }
00623
00624 virtual QString key( int column, bool ) const
00625 {
00626 KMHeaders *headers = static_cast<KMHeaders*>(listView());
00627 int sortOrder = column;
00628 if (headers->mPaintInfo.orderOfArrival)
00629 sortOrder |= (1 << 6);
00630 if (headers->mPaintInfo.status)
00631 sortOrder |= (1 << 5);
00632
00633
00634 if(mKey.isEmpty() || mKey[0] != (char)sortOrder) {
00635 KMHeaders *headers = static_cast<KMHeaders*>(listView());
00636 KMMsgBase *msgBase = headers->folder()->getMsgBase( mMsgId );
00637 return ((KMHeaderItem *)this)->mKey =
00638 generate_key( headers, msgBase, headers->paintInfo(), sortOrder );
00639 }
00640 return mKey;
00641 }
00642
00643 void setTempKey( QString key ) {
00644 mKey = key;
00645 }
00646
00647 int compare( QListViewItem *i, int col, bool ascending ) const
00648 {
00649
00650 int res = 0;
00651 KMHeaders *headers = static_cast<KMHeaders*>(listView());
00652 if ( ( col == headers->paintInfo()->statusCol ) ||
00653 ( col == headers->paintInfo()->sizeCol ) ||
00654 ( col == headers->paintInfo()->attachmentCol ) ||
00655 ( col == headers->paintInfo()->importantCol ) ||
00656 ( col == headers->paintInfo()->todoCol ) ||
00657 ( col == headers->paintInfo()->spamHamCol ) ||
00658 ( col == headers->paintInfo()->signedCol ) ||
00659 ( col == headers->paintInfo()->cryptoCol ) ||
00660 ( col == headers->paintInfo()->watchedIgnoredCol ) ) {
00661 res = key( col, ascending ).compare( i->key( col, ascending ) );
00662 } else if ( col == headers->paintInfo()->dateCol ) {
00663 res = key( col, ascending ).compare( i->key( col, ascending ) );
00664 if (i->parent() && !ascending)
00665 res = -res;
00666 } else if ( col == headers->paintInfo()->subCol ||
00667 col == headers->paintInfo()->senderCol ||
00668 col == headers->paintInfo()->receiverCol ) {
00669 res = key( col, ascending ).localeAwareCompare( i->key( col, ascending ) );
00670 }
00671 return res;
00672 }
00673
00674 QListViewItem* firstChildNonConst() {
00675 enforceSortOrder();
00676 return firstChild();
00677 }
00678
00679 bool mAboutToBeDeleted;
00680 bool aboutToBeDeleted() const { return mAboutToBeDeleted; }
00681 void setAboutToBeDeleted( bool val ) { mAboutToBeDeleted = val; }
00682
00683 KMSortCacheItem *mSortCacheItem;
00684 void setSortCacheItem( KMSortCacheItem *item ) { mSortCacheItem = item; }
00685 KMSortCacheItem* sortCacheItem() const { return mSortCacheItem; }
00686 };
00687
00688
00689 KMHeaders::KMHeaders(KMMainWidget *aOwner, QWidget *parent,
00690 const char *name) :
00691 KListView(parent, name)
00692 {
00693 static bool pixmapsLoaded = false;
00694
00695 KImageIO::registerFormats();
00696 mOwner = aOwner;
00697 mFolder = 0;
00698 noRepaint = false;
00699 getMsgIndex = -1;
00700 mTopItem = 0;
00701 setSelectionMode( QListView::Extended );
00702 setAllColumnsShowFocus( true );
00703 mNested = false;
00704 nestingPolicy = OpenUnread;
00705 mNestedOverride = false;
00706 mSubjThreading = true;
00707 mMousePressed = false;
00708 mSortInfo.dirty = true;
00709 mSortInfo.fakeSort = 0;
00710 mSortInfo.removed = 0;
00711 mSortInfo.column = 0;
00712 mSortInfo.ascending = false;
00713 mReaderWindowActive = false;
00714 setStyleDependantFrameWidth();
00715
00716 header()->setClickEnabled(true);
00717 header()->installEventFilter(this);
00718 mPopup = new KPopupMenu(this);
00719 mPopup->insertTitle(i18n("View Columns"));
00720 mPopup->setCheckable(true);
00721 mPopup->insertItem(i18n("Status"), KPaintInfo::COL_STATUS);
00722 mPopup->insertItem(i18n("Important"), KPaintInfo::COL_IMPORTANT);
00723 mPopup->insertItem(i18n("Todo"), KPaintInfo::COL_TODO);
00724 mPopup->insertItem(i18n("Attachment"), KPaintInfo::COL_ATTACHMENT);
00725 mPopup->insertItem(i18n("Spam/Ham"), KPaintInfo::COL_SPAM_HAM);
00726 mPopup->insertItem(i18n("Watched/Ignored"), KPaintInfo::COL_WATCHED_IGNORED);
00727 mPopup->insertItem(i18n("Signature"), KPaintInfo::COL_SIGNED);
00728 mPopup->insertItem(i18n("Encryption"), KPaintInfo::COL_CRYPTO);
00729 mPopup->insertItem(i18n("Size"), KPaintInfo::COL_SIZE);
00730 mPopup->insertItem(i18n("Receiver"), KPaintInfo::COL_RECEIVER);
00731
00732 connect(mPopup, SIGNAL(activated(int)), this, SLOT(slotToggleColumn(int)));
00733
00734 mPaintInfo.flagCol = -1;
00735 mPaintInfo.subCol = mPaintInfo.flagCol + 1;
00736 mPaintInfo.senderCol = mPaintInfo.subCol + 1;
00737 mPaintInfo.dateCol = mPaintInfo.senderCol + 1;
00738 mPaintInfo.orderOfArrival = false;
00739 mPaintInfo.status = false;
00740 mSortCol = KMMsgList::sfDate;
00741 mSortDescending = false;
00742
00743 setShowSortIndicator(true);
00744 setFocusPolicy( WheelFocus );
00745
00746 if (!pixmapsLoaded)
00747 {
00748 pixmapsLoaded = true;
00749 pixNew = new QPixmap( UserIcon("kmmsgnew") );
00750 pixUns = new QPixmap( UserIcon("kmmsgunseen") );
00751 pixDel = new QPixmap( UserIcon("kmmsgdel") );
00752 pixRead = new QPixmap( UserIcon("kmmsgread") );
00753 pixRep = new QPixmap( UserIcon("kmmsgreplied") );
00754 pixQueued= new QPixmap( UserIcon("kmmsgqueued") );
00755 pixSent = new QPixmap( UserIcon("kmmsgsent") );
00756 pixFwd = new QPixmap( UserIcon("kmmsgforwarded") );
00757 pixFlag = new QPixmap( UserIcon("kmmsgflag") );
00758 pixWatched = new QPixmap( UserIcon("kmmsgwatched") );
00759 pixIgnored = new QPixmap( UserIcon("kmmsgignored") );
00760 pixSpam = new QPixmap( UserIcon("kmmsgspam") );
00761 pixHam = new QPixmap( UserIcon("kmmsgham") );
00762 pixFullySigned = new QPixmap( UserIcon( "kmmsgfullysigned" ) );
00763 pixPartiallySigned = new QPixmap( UserIcon( "kmmsgpartiallysigned" ) );
00764 pixUndefinedSigned = new QPixmap( UserIcon( "kmmsgundefinedsigned" ) );
00765 pixFullyEncrypted = new QPixmap( UserIcon( "kmmsgfullyencrypted" ) );
00766 pixPartiallyEncrypted = new QPixmap( UserIcon( "kmmsgpartiallyencrypted" ) );
00767 pixUndefinedEncrypted = new QPixmap( UserIcon( "kmmsgundefinedencrypted" ) );
00768 pixEncryptionProblematic = new QPixmap( UserIcon( "kmmsgencryptionproblematic" ) );
00769 pixSignatureProblematic = new QPixmap( UserIcon( "kmmsgsignatureproblematic" ) );
00770 pixAttachment = new QPixmap( UserIcon( "kmmsgattachment" ) );
00771 pixTodo = new QPixmap( UserIcon( "kmmsgtodo" ) );
00772 pixReadFwd = new QPixmap( UserIcon( "kmmsgread_fwd" ) );
00773 pixReadReplied = new QPixmap( UserIcon( "kmmsgread_replied" ) );
00774 pixReadFwdReplied = new QPixmap( UserIcon( "kmmsgread_fwd_replied" ) );
00775 }
00776
00777 header()->setStretchEnabled( false );
00778 header()->setResizeEnabled( false );
00779
00780 mPaintInfo.subCol = addColumn( i18n("Subject"), 310 );
00781 mPaintInfo.senderCol = addColumn( i18n("Sender"), 170 );
00782 mPaintInfo.dateCol = addColumn( i18n("Date"), 170 );
00783 mPaintInfo.sizeCol = addColumn( i18n("Size"), 0 );
00784 mPaintInfo.receiverCol = addColumn( i18n("Receiver"), 0 );
00785
00786 mPaintInfo.statusCol = addColumn( *pixNew , "", 0 );
00787 mPaintInfo.importantCol = addColumn( *pixFlag , "", 0 );
00788 mPaintInfo.todoCol = addColumn( *pixTodo , "", 0 );
00789 mPaintInfo.attachmentCol = addColumn( *pixAttachment , "", 0 );
00790 mPaintInfo.spamHamCol = addColumn( *pixSpam , "", 0 );
00791 mPaintInfo.watchedIgnoredCol = addColumn( *pixWatched , "", 0 );
00792 mPaintInfo.signedCol = addColumn( *pixFullySigned , "", 0 );
00793 mPaintInfo.cryptoCol = addColumn( *pixFullyEncrypted, "", 0 );
00794
00795 setResizeMode( QListView::NoColumn );
00796
00797
00798 header()->setResizeEnabled( true, mPaintInfo.subCol );
00799 header()->setResizeEnabled( true, mPaintInfo.senderCol );
00800 header()->setResizeEnabled( true, mPaintInfo.dateCol );
00801
00802 readConfig();
00803 restoreLayout(KMKernel::config(), "Header-Geometry");
00804
00805 connect( this, SIGNAL( contextMenuRequested( QListViewItem*, const QPoint &, int )),
00806 this, SLOT( rightButtonPressed( QListViewItem*, const QPoint &, int )));
00807 connect(this, SIGNAL(doubleClicked(QListViewItem*)),
00808 this,SLOT(selectMessage(QListViewItem*)));
00809 connect(this,SIGNAL(currentChanged(QListViewItem*)),
00810 this,SLOT(highlightMessage(QListViewItem*)));
00811 resetCurrentTime();
00812
00813 mSubjectLists.setAutoDelete( true );
00814 }
00815
00816
00817
00818 KMHeaders::~KMHeaders ()
00819 {
00820 if (mFolder)
00821 {
00822 writeFolderConfig();
00823 writeSortOrder();
00824 mFolder->close();
00825 }
00826 writeConfig();
00827 }
00828
00829
00830
00831 bool KMHeaders::eventFilter ( QObject *o, QEvent *e )
00832 {
00833 if ( e->type() == QEvent::MouseButtonPress &&
00834 static_cast<QMouseEvent*>(e)->button() == RightButton &&
00835 o->isA("QHeader") )
00836 {
00837
00838
00839 if ( mPaintInfo.showReceiver )
00840 mPopup->changeItem(KPaintInfo::COL_RECEIVER, i18n("Receiver"));
00841 else
00842 if ( mFolder && (mFolder->whoField().lower() == "to") )
00843 mPopup->changeItem(KPaintInfo::COL_RECEIVER, i18n("Sender"));
00844 else
00845 mPopup->changeItem(KPaintInfo::COL_RECEIVER, i18n("Receiver"));
00846
00847 mPopup->popup( static_cast<QMouseEvent*>(e)->globalPos() );
00848 return true;
00849 }
00850 return KListView::eventFilter(o, e);
00851 }
00852
00853
00854 void KMHeaders::slotToggleColumn(int id, int mode)
00855 {
00856 bool *show = 0;
00857 int *col = 0;
00858 int width = 0;
00859
00860 switch ( static_cast<KPaintInfo::ColumnIds>(id) )
00861 {
00862 case KPaintInfo::COL_SIZE:
00863 {
00864 show = &mPaintInfo.showSize;
00865 col = &mPaintInfo.sizeCol;
00866 width = 80;
00867 break;
00868 }
00869 case KPaintInfo::COL_ATTACHMENT:
00870 {
00871 show = &mPaintInfo.showAttachment;
00872 col = &mPaintInfo.attachmentCol;
00873 width = pixAttachment->width();
00874 break;
00875 }
00876 case KPaintInfo::COL_IMPORTANT:
00877 {
00878 show = &mPaintInfo.showImportant;
00879 col = &mPaintInfo.importantCol;
00880 width = pixFlag->width();
00881 break;
00882 }
00883 case KPaintInfo::COL_TODO:
00884 {
00885 show = &mPaintInfo.showTodo;
00886 col = &mPaintInfo.todoCol;
00887 width = pixTodo->width();
00888 break;
00889 }
00890 case KPaintInfo::COL_SPAM_HAM:
00891 {
00892 show = &mPaintInfo.showSpamHam;
00893 col = &mPaintInfo.spamHamCol;
00894 width = pixSpam->width();
00895 break;
00896 }
00897 case KPaintInfo::COL_WATCHED_IGNORED:
00898 {
00899 show = &mPaintInfo.showWatchedIgnored;
00900 col = &mPaintInfo.watchedIgnoredCol;
00901 width = pixWatched->width();
00902 break;
00903 }
00904 case KPaintInfo::COL_STATUS:
00905 {
00906 show = &mPaintInfo.showStatus;
00907 col = &mPaintInfo.statusCol;
00908 width = pixNew->width();
00909 break;
00910 }
00911 case KPaintInfo::COL_SIGNED:
00912 {
00913 show = &mPaintInfo.showSigned;
00914 col = &mPaintInfo.signedCol;
00915 width = pixFullySigned->width();
00916 break;
00917 }
00918 case KPaintInfo::COL_CRYPTO:
00919 {
00920 show = &mPaintInfo.showCrypto;
00921 col = &mPaintInfo.cryptoCol;
00922 width = pixFullyEncrypted->width();
00923 break;
00924 }
00925 case KPaintInfo::COL_RECEIVER:
00926 {
00927 show = &mPaintInfo.showReceiver;
00928 col = &mPaintInfo.receiverCol;
00929 width = 170;
00930 break;
00931 }
00932 case KPaintInfo::COL_SCORE: ;
00933
00934 }
00935
00936 assert(show);
00937
00938 if (mode == -1)
00939 *show = !*show;
00940 else
00941 *show = mode;
00942
00943 mPopup->setItemChecked(id, *show);
00944
00945 if (*show) {
00946 header()->setResizeEnabled(true, *col);
00947 setColumnWidth(*col, width);
00948 }
00949 else {
00950 header()->setResizeEnabled(false, *col);
00951 header()->setStretchEnabled(false, *col);
00952 hideColumn(*col);
00953 }
00954
00955
00956
00957 if ( static_cast<KPaintInfo::ColumnIds>(id) == KPaintInfo::COL_RECEIVER ) {
00958 QString colText = i18n( "Sender" );
00959 if ( mFolder && (mFolder->whoField().lower() == "to") && !mPaintInfo.showReceiver)
00960 colText = i18n( "Receiver" );
00961 setColumnText( mPaintInfo.senderCol, colText );
00962 }
00963
00964 if (mode == -1)
00965 writeConfig();
00966 }
00967
00968
00969
00970 void KMHeaders::paintEmptyArea( QPainter * p, const QRect & rect )
00971 {
00972 if (mPaintInfo.pixmapOn)
00973 p->drawTiledPixmap( rect.left(), rect.top(), rect.width(), rect.height(),
00974 mPaintInfo.pixmap,
00975 rect.left() + contentsX(),
00976 rect.top() + contentsY() );
00977 else
00978 p->fillRect( rect, colorGroup().base() );
00979 }
00980
00981 bool KMHeaders::event(QEvent *e)
00982 {
00983 bool result = KListView::event(e);
00984 if (e->type() == QEvent::ApplicationPaletteChange)
00985 {
00986 readColorConfig();
00987 }
00988 return result;
00989 }
00990
00991
00992
00993 void KMHeaders::readColorConfig (void)
00994 {
00995 KConfig* config = KMKernel::config();
00996
00997 KConfigGroupSaver saver(config, "Reader");
00998 QColor c1=QColor(kapp->palette().active().text());
00999 QColor c2=QColor("red");
01000 QColor c3=QColor("blue");
01001 QColor c4=QColor(kapp->palette().active().base());
01002 QColor c5=QColor(0,0x7F,0);
01003 QColor c6=KGlobalSettings::alternateBackgroundColor();
01004
01005 if (!config->readBoolEntry("defaultColors",true)) {
01006 mPaintInfo.colFore = config->readColorEntry("ForegroundColor",&c1);
01007 mPaintInfo.colBack = config->readColorEntry("BackgroundColor",&c4);
01008 QPalette newPal = kapp->palette();
01009 newPal.setColor( QColorGroup::Base, mPaintInfo.colBack );
01010 newPal.setColor( QColorGroup::Text, mPaintInfo.colFore );
01011 setPalette( newPal );
01012 mPaintInfo.colNew = config->readColorEntry("NewMessage",&c2);
01013 mPaintInfo.colUnread = config->readColorEntry("UnreadMessage",&c3);
01014 mPaintInfo.colFlag = config->readColorEntry("FlagMessage",&c5);
01015 c6 = config->readColorEntry("AltBackgroundColor",&c6);
01016 }
01017 else {
01018 mPaintInfo.colFore = c1;
01019 mPaintInfo.colBack = c4;
01020 QPalette newPal = kapp->palette();
01021 newPal.setColor( QColorGroup::Base, c4 );
01022 newPal.setColor( QColorGroup::Text, c1 );
01023 setPalette( newPal );
01024 mPaintInfo.colNew = c2;
01025 mPaintInfo.colUnread = c3;
01026 mPaintInfo.colFlag = c5;
01027 }
01028 setAlternateBackground(c6);
01029 }
01030
01031
01032 void KMHeaders::readConfig (void)
01033 {
01034 KConfig* config = KMKernel::config();
01035
01036
01037 {
01038 KConfigGroupSaver saver(config, "Pixmaps");
01039 QString pixmapFile = config->readEntry("Headers");
01040 mPaintInfo.pixmapOn = false;
01041 if (!pixmapFile.isEmpty()) {
01042 mPaintInfo.pixmapOn = true;
01043 mPaintInfo.pixmap = QPixmap( pixmapFile );
01044 }
01045 }
01046
01047
01048 {
01049 KConfigGroupSaver saver(config, "General");
01050 bool show = config->readBoolEntry("showMessageSize");
01051 slotToggleColumn(KPaintInfo::COL_SIZE, show);
01052
01053 show = config->readBoolEntry("showAttachmentColumn");
01054 slotToggleColumn(KPaintInfo::COL_ATTACHMENT, show);
01055
01056 show = config->readBoolEntry("showImportantColumn");
01057 slotToggleColumn(KPaintInfo::COL_IMPORTANT, show);
01058
01059 show = config->readBoolEntry("showTodoColumn");
01060 slotToggleColumn(KPaintInfo::COL_TODO, show);
01061
01062 show = config->readBoolEntry("showSpamHamColumn");
01063 slotToggleColumn(KPaintInfo::COL_SPAM_HAM, show);
01064
01065 show = config->readBoolEntry("showWatchedIgnoredColumn");
01066 slotToggleColumn(KPaintInfo::COL_WATCHED_IGNORED, show);
01067
01068 show = config->readBoolEntry("showStatusColumn");
01069 slotToggleColumn(KPaintInfo::COL_STATUS, show);
01070
01071 show = config->readBoolEntry("showSignedColumn");
01072 slotToggleColumn(KPaintInfo::COL_SIGNED, show);
01073
01074 show = config->readBoolEntry("showCryptoColumn");
01075 slotToggleColumn(KPaintInfo::COL_CRYPTO, show);
01076
01077 show = config->readBoolEntry("showReceiverColumn");
01078 slotToggleColumn(KPaintInfo::COL_RECEIVER, show);
01079
01080 mPaintInfo.showCryptoIcons = config->readBoolEntry( "showCryptoIcons", false );
01081 mPaintInfo.showAttachmentIcon = config->readBoolEntry( "showAttachmentIcon", true );
01082
01083 KMime::DateFormatter::FormatType t =
01084 (KMime::DateFormatter::FormatType) config->readNumEntry("dateFormat", KMime::DateFormatter::Fancy ) ;
01085 mDate.setCustomFormat( config->readEntry("customDateFormat") );
01086 mDate.setFormat( t );
01087 }
01088
01089 readColorConfig();
01090
01091
01092 {
01093 KConfigGroupSaver saver(config, "Fonts");
01094 if (!(config->readBoolEntry("defaultFonts",true)))
01095 {
01096 QFont listFont( KGlobalSettings::generalFont() );
01097 setFont(config->readFontEntry("list-font", &listFont));
01098 dateFont = KGlobalSettings::fixedFont();
01099 dateFont = config->readFontEntry("list-date-font", &dateFont);
01100 } else {
01101 dateFont = KGlobalSettings::generalFont();
01102 setFont(dateFont);
01103 }
01104 }
01105
01106
01107 {
01108 KConfigGroupSaver saver(config, "Geometry");
01109 mReaderWindowActive = config->readEntry( "readerWindowMode", "below" ) != "hide";
01110 }
01111 }
01112
01113
01114 void KMHeaders::reset(void)
01115 {
01116 int top = topItemIndex();
01117 int id = currentItemIndex();
01118 noRepaint = true;
01119 clear();
01120 QString colText = i18n( "Sender" );
01121 if ( mFolder && (mFolder->whoField().lower() == "to") && !mPaintInfo.showReceiver)
01122 colText = i18n( "Receiver" );
01123 setColumnText( mPaintInfo.senderCol, colText );
01124 noRepaint = false;
01125 mItems.resize(0);
01126 updateMessageList();
01127 setCurrentMsg(id);
01128 setTopItemByIndex(top);
01129 ensureCurrentItemVisible();
01130 }
01131
01132
01133 void KMHeaders::refreshNestedState(void)
01134 {
01135 bool oldState = isThreaded();
01136 NestingPolicy oldNestPolicy = nestingPolicy;
01137 KConfig* config = KMKernel::config();
01138 KConfigGroupSaver saver(config, "Geometry");
01139 mNested = config->readBoolEntry( "nestedMessages", false );
01140
01141 nestingPolicy = (NestingPolicy)config->readNumEntry( "nestingPolicy", OpenUnread );
01142 if ((nestingPolicy != oldNestPolicy) ||
01143 (oldState != isThreaded()))
01144 {
01145 setRootIsDecorated( nestingPolicy != AlwaysOpen && isThreaded() );
01146 reset();
01147 }
01148
01149 }
01150
01151
01152 void KMHeaders::readFolderConfig (void)
01153 {
01154 if (!mFolder) return;
01155 KConfig* config = KMKernel::config();
01156
01157 KConfigGroupSaver saver(config, "Folder-" + mFolder->idString());
01158 mNestedOverride = config->readBoolEntry( "threadMessagesOverride", false );
01159 mSortCol = config->readNumEntry("SortColumn", (int)KMMsgList::sfDate);
01160 mSortDescending = (mSortCol < 0);
01161 mSortCol = abs(mSortCol) - 1;
01162
01163 mTopItem = config->readNumEntry("Top", 0);
01164 mCurrentItem = config->readNumEntry("Current", 0);
01165 mCurrentItemSerNum = config->readNumEntry("CurrentSerialNum", 0);
01166
01167 mPaintInfo.orderOfArrival = config->readBoolEntry( "OrderOfArrival", true );
01168 mPaintInfo.status = config->readBoolEntry( "Status", false );
01169
01170 {
01171 KConfigGroupSaver saver(config, "Geometry");
01172 mNested = config->readBoolEntry( "nestedMessages", false );
01173 nestingPolicy = (NestingPolicy)config->readNumEntry( "nestingPolicy", OpenUnread );
01174 }
01175
01176 setRootIsDecorated( nestingPolicy != AlwaysOpen && isThreaded() );
01177 mSubjThreading = config->readBoolEntry( "threadMessagesBySubject", true );
01178 }
01179
01180
01181
01182 void KMHeaders::writeFolderConfig (void)
01183 {
01184 if (!mFolder) return;
01185 KConfig* config = KMKernel::config();
01186 int mSortColAdj = mSortCol + 1;
01187
01188 KConfigGroupSaver saver(config, "Folder-" + mFolder->idString());
01189 config->writeEntry("SortColumn", (mSortDescending ? -mSortColAdj : mSortColAdj));
01190 config->writeEntry("Top", topItemIndex());
01191 config->writeEntry("Current", currentItemIndex());
01192 KMHeaderItem* current = currentHeaderItem();
01193 ulong sernum = 0;
01194 if ( current && mFolder->getMsgBase( current->msgId() ) )
01195 sernum = mFolder->getMsgBase( current->msgId() )->getMsgSerNum();
01196 config->writeEntry("CurrentSerialNum", sernum);
01197
01198 config->writeEntry("OrderOfArrival", mPaintInfo.orderOfArrival);
01199 config->writeEntry("Status", mPaintInfo.status);
01200 }
01201
01202
01203
01204 void KMHeaders::writeConfig (void)
01205 {
01206 KConfig* config = KMKernel::config();
01207 saveLayout(config, "Header-Geometry");
01208 KConfigGroupSaver saver(config, "General");
01209 config->writeEntry("showMessageSize" , mPaintInfo.showSize);
01210 config->writeEntry("showAttachmentColumn" , mPaintInfo.showAttachment);
01211 config->writeEntry("showImportantColumn" , mPaintInfo.showImportant);
01212 config->writeEntry("showTodoColumn" , mPaintInfo.showTodo);
01213 config->writeEntry("showSpamHamColumn" , mPaintInfo.showSpamHam);
01214 config->writeEntry("showWatchedIgnoredColumn", mPaintInfo.showWatchedIgnored);
01215 config->writeEntry("showStatusColumn" , mPaintInfo.showStatus);
01216 config->writeEntry("showSignedColumn" , mPaintInfo.showSigned);
01217 config->writeEntry("showCryptoColumn" , mPaintInfo.showCrypto);
01218 config->writeEntry("showReceiverColumn" , mPaintInfo.showReceiver);
01219 }
01220
01221
01222 void KMHeaders::setFolder( KMFolder *aFolder, bool forceJumpToUnread )
01223 {
01224 CREATE_TIMER(set_folder);
01225 START_TIMER(set_folder);
01226
01227 int id;
01228 QString str;
01229
01230 mSortInfo.fakeSort = 0;
01231 if ( mFolder && static_cast<KMFolder*>(mFolder) == aFolder ) {
01232 int top = topItemIndex();
01233 id = currentItemIndex();
01234 writeFolderConfig();
01235 readFolderConfig();
01236 updateMessageList();
01237 setCurrentMsg(id);
01238 setTopItemByIndex(top);
01239 } else {
01240 if (mFolder) {
01241
01242
01243 highlightMessage(0, false);
01244
01245 disconnect(mFolder, SIGNAL(numUnreadMsgsChanged(KMFolder*)),
01246 this, SLOT(setFolderInfoStatus()));
01247
01248 mFolder->markNewAsUnread();
01249 writeFolderConfig();
01250 disconnect(mFolder, SIGNAL(msgHeaderChanged(KMFolder*,int)),
01251 this, SLOT(msgHeaderChanged(KMFolder*,int)));
01252 disconnect(mFolder, SIGNAL(msgAdded(int)),
01253 this, SLOT(msgAdded(int)));
01254 disconnect(mFolder, SIGNAL(msgRemoved(int,QString, QString)),
01255 this, SLOT(msgRemoved(int,QString, QString)));
01256 disconnect(mFolder, SIGNAL(changed()),
01257 this, SLOT(msgChanged()));
01258 disconnect(mFolder, SIGNAL(cleared()),
01259 this, SLOT(folderCleared()));
01260 disconnect(mFolder, SIGNAL(expunged( KMFolder* )),
01261 this, SLOT(folderCleared()));
01262 disconnect( mFolder, SIGNAL( statusMsg( const QString& ) ),
01263 BroadcastStatus::instance(), SLOT( setStatusMsg( const QString& ) ) );
01264 writeSortOrder();
01265 mFolder->close();
01266
01267
01268 if (mFolder->dirty()) mFolder->writeIndex();
01269 }
01270
01271 mSortInfo.removed = 0;
01272 mFolder = aFolder;
01273 mSortInfo.dirty = true;
01274 mOwner->editAction()->setEnabled(mFolder ?
01275 (kmkernel->folderIsDraftOrOutbox(mFolder)): false );
01276 mOwner->replyListAction()->setEnabled(mFolder ?
01277 mFolder->isMailingListEnabled() : false);
01278 if (mFolder)
01279 {
01280 connect(mFolder, SIGNAL(msgHeaderChanged(KMFolder*,int)),
01281 this, SLOT(msgHeaderChanged(KMFolder*,int)));
01282 connect(mFolder, SIGNAL(msgAdded(int)),
01283 this, SLOT(msgAdded(int)));
01284 connect(mFolder, SIGNAL(msgRemoved(int,QString, QString)),
01285 this, SLOT(msgRemoved(int,QString, QString)));
01286 connect(mFolder, SIGNAL(changed()),
01287 this, SLOT(msgChanged()));
01288 connect(mFolder, SIGNAL(cleared()),
01289 this, SLOT(folderCleared()));
01290 connect(mFolder, SIGNAL(expunged( KMFolder* )),
01291 this, SLOT(folderCleared()));
01292 connect(mFolder, SIGNAL(statusMsg(const QString&)),
01293 BroadcastStatus::instance(), SLOT( setStatusMsg( const QString& ) ) );
01294 connect(mFolder, SIGNAL(numUnreadMsgsChanged(KMFolder*)),
01295 this, SLOT(setFolderInfoStatus()));
01296
01297
01298
01299
01300 if (isThreaded()) {
01301 noRepaint = true;
01302 clear();
01303 noRepaint = false;
01304 mItems.resize( 0 );
01305 }
01306
01307 readFolderConfig();
01308
01309 CREATE_TIMER(kmfolder_open);
01310 START_TIMER(kmfolder_open);
01311 mFolder->open();
01312 END_TIMER(kmfolder_open);
01313 SHOW_TIMER(kmfolder_open);
01314
01315 if (isThreaded()) {
01316 noRepaint = true;
01317 clear();
01318 noRepaint = false;
01319 mItems.resize( 0 );
01320 }
01321 }
01322
01323 CREATE_TIMER(updateMsg);
01324 START_TIMER(updateMsg);
01325 updateMessageList(true, forceJumpToUnread);
01326 END_TIMER(updateMsg);
01327 SHOW_TIMER(updateMsg);
01328 makeHeaderVisible();
01329 }
01330
01331 setFolderInfoStatus();
01332
01333 QString colText = i18n( "Sender" );
01334 if (mFolder && (mFolder->whoField().lower() == "to") && !mPaintInfo.showReceiver)
01335 colText = i18n("Receiver");
01336 setColumnText( mPaintInfo.senderCol, colText);
01337
01338 colText = i18n( "Date" );
01339 if (mPaintInfo.orderOfArrival)
01340 colText = i18n( "Order of Arrival" );
01341 setColumnText( mPaintInfo.dateCol, colText);
01342
01343 colText = i18n( "Subject" );
01344 if (mPaintInfo.status)
01345 colText = colText + i18n( " (Status)" );
01346 setColumnText( mPaintInfo.subCol, colText);
01347
01348 END_TIMER(set_folder);
01349 SHOW_TIMER(set_folder);
01350 }
01351
01352
01353 void KMHeaders::msgChanged()
01354 {
01355
01356 if (mFolder->count() == 0) {
01357 clear();
01358 return;
01359 }
01360 int i = topItemIndex();
01361 int cur = currentItemIndex();
01362 if (!isUpdatesEnabled()) return;
01363 QString msgIdMD5;
01364 QListViewItem *item = currentItem();
01365 KMHeaderItem *hi = dynamic_cast<KMHeaderItem*>(item);
01366 if (item && hi) {
01367 KMMsgBase *mb = mFolder->getMsgBase(hi->msgId());
01368 if (mb)
01369 msgIdMD5 = mb->msgIdMD5();
01370 }
01371 if (!isUpdatesEnabled()) return;
01372
01373 disconnect(this,SIGNAL(currentChanged(QListViewItem*)),
01374 this,SLOT(highlightMessage(QListViewItem*)));
01375
01376 QValueList<int> curItems = selectedItems();
01377 updateMessageList();
01378
01379 setTopItemByIndex( i );
01380 setCurrentMsg( cur );
01381 setSelectedByIndex( curItems, true );
01382 connect(this,SIGNAL(currentChanged(QListViewItem*)),
01383 this,SLOT(highlightMessage(QListViewItem*)));
01384
01385
01386
01387
01388
01389
01390
01391
01392 item = currentItem();
01393 hi = dynamic_cast<KMHeaderItem*>(item);
01394 if (item && hi) {
01395 KMMsgBase *mb = mFolder->getMsgBase(hi->msgId());
01396 if (mb) {
01397 if (msgIdMD5.isEmpty() || (msgIdMD5 != mb->msgIdMD5()))
01398 emit selected(mFolder->getMsg(hi->msgId()));
01399 } else {
01400 emit selected(0);
01401 }
01402 } else
01403 emit selected(0);
01404 }
01405
01406
01407
01408 void KMHeaders::msgAdded(int id)
01409 {
01410 KMHeaderItem* hi = 0;
01411 if (!isUpdatesEnabled()) return;
01412
01413 CREATE_TIMER(msgAdded);
01414 START_TIMER(msgAdded);
01415
01416 assert( mFolder->getMsgBase( id ) );
01417
01418
01419 KMSortCacheItem *sci = new KMSortCacheItem;
01420 sci->setId(id);
01421 if (isThreaded()) {
01422
01423 if (mSortCacheItems.count() == (uint)mFolder->count()
01424 || mSortCacheItems.count() == 0) {
01425 kdDebug (5006) << "KMHeaders::msgAdded - Resizing id and subject trees of " << mFolder->label()
01426 << ": before=" << mSortCacheItems.count() << " ,after=" << (mFolder->count()*2) << endl;
01427 mSortCacheItems.resize(mFolder->count()*2);
01428 mSubjectLists.resize(mFolder->count()*2);
01429 }
01430 QString msgId = mFolder->getMsgBase(id)->msgIdMD5();
01431 if (msgId.isNull())
01432 msgId = "";
01433 QString replyToId = mFolder->getMsgBase(id)->replyToIdMD5();
01434
01435 KMSortCacheItem *parent = findParent( sci );
01436 if (!parent && mSubjThreading) {
01437 parent = findParentBySubject( sci );
01438 if (parent && sci->isImperfectlyThreaded()) {
01439
01440
01441
01442
01443 if (msgId == mFolder->getMsgBase(parent->item()->msgId())->replyToIdMD5()
01444 || msgId == mFolder->getMsgBase(parent->item()->msgId())->replyToAuxIdMD5())
01445 parent = NULL;
01446 }
01447 }
01448
01449 if (parent && mFolder->getMsgBase(parent->id())->isWatched())
01450 mFolder->getMsgBase(id)->setStatus( KMMsgStatusWatched );
01451 else if (parent && mFolder->getMsgBase(parent->id())->isIgnored()) {
01452 mFolder->getMsgBase(id)->setStatus( KMMsgStatusIgnored );
01453 mFolder->setStatus( id, KMMsgStatusRead );
01454 }
01455 if (parent)
01456 hi = new KMHeaderItem( parent->item(), id );
01457 else
01458 hi = new KMHeaderItem( this, id );
01459
01460
01461 hi->setSortCacheItem(sci);
01462 sci->setItem(hi);
01463
01464
01465 mItems.resize( mFolder->count() );
01466 mItems[id] = hi;
01467
01468 if ( !msgId.isEmpty() )
01469 mSortCacheItems.replace(msgId, sci);
01470
01471
01472 if (mSubjThreading && parent) {
01473 QString subjMD5 = mFolder->getMsgBase(id)->strippedSubjectMD5();
01474 if (subjMD5.isEmpty()) {
01475 mFolder->getMsgBase(id)->initStrippedSubjectMD5();
01476 subjMD5 = mFolder->getMsgBase(id)->strippedSubjectMD5();
01477 }
01478 if( !subjMD5.isEmpty()) {
01479 if ( !mSubjectLists.find(subjMD5) )
01480 mSubjectLists.insert(subjMD5, new QPtrList<KMSortCacheItem>());
01481
01482 int p=0;
01483 for (QPtrListIterator<KMSortCacheItem> it(*mSubjectLists[subjMD5]);
01484 it.current(); ++it) {
01485 KMMsgBase *mb = mFolder->getMsgBase((*it)->id());
01486 if ( mb->date() < mFolder->getMsgBase(id)->date())
01487 break;
01488 p++;
01489 }
01490 mSubjectLists[subjMD5]->insert( p, sci);
01491 }
01492 }
01493
01494
01495
01496
01497
01498
01499 disconnect( this, SIGNAL(currentChanged(QListViewItem*)),
01500 this, SLOT(highlightMessage(QListViewItem*)));
01501
01502 if ( !msgId.isEmpty() ) {
01503 QPtrListIterator<KMHeaderItem> it(mImperfectlyThreadedList);
01504 KMHeaderItem *cur;
01505 while ( (cur = it.current()) ) {
01506 ++it;
01507 int tryMe = cur->msgId();
01508
01509
01510
01511 bool perfectParent = true;
01512 KMMsgBase *otherMsg = mFolder->getMsgBase(tryMe);
01513 if ( !otherMsg ) {
01514 kdDebug(5006) << "otherMsg is NULL !!! tryMe: " << tryMe << endl;
01515 continue;
01516 }
01517 QString otherId = otherMsg->replyToIdMD5();
01518 if (msgId != otherId) {
01519 if (msgId != otherMsg->replyToAuxIdMD5())
01520 continue;
01521 else {
01522 if (!otherId.isEmpty() && mSortCacheItems.find(otherId))
01523 continue;
01524 else
01525
01526
01527 perfectParent = false;
01528 }
01529 }
01530 QListViewItem *newParent = mItems[id];
01531 QListViewItem *msg = mItems[tryMe];
01532
01533 if (msg->parent())
01534 msg->parent()->takeItem(msg);
01535 else
01536 takeItem(msg);
01537 newParent->insertItem(msg);
01538
01539 makeHeaderVisible();
01540
01541 if (perfectParent) {
01542 mImperfectlyThreadedList.removeRef (mItems[tryMe]);
01543
01544
01545 QString sortFile = KMAIL_SORT_FILE(mFolder);
01546 FILE *sortStream = fopen(QFile::encodeName(sortFile), "r+");
01547 if (sortStream) {
01548 mItems[tryMe]->sortCacheItem()->updateSortFile( sortStream, mFolder );
01549 fclose (sortStream);
01550 }
01551 }
01552 }
01553 }
01554
01555 if (hi && hi->sortCacheItem()->isImperfectlyThreaded())
01556 mImperfectlyThreadedList.append(hi);
01557 } else {
01558
01559 hi = new KMHeaderItem( this, id );
01560 mItems.resize( mFolder->count() );
01561 mItems[id] = hi;
01562
01563 hi->setSortCacheItem(sci);
01564 sci->setItem(hi);
01565 }
01566 if (mSortInfo.fakeSort) {
01567 QObject::disconnect(header(), SIGNAL(clicked(int)), this, SLOT(dirtySortOrder(int)));
01568 KListView::setSorting(mSortCol, !mSortDescending );
01569 mSortInfo.fakeSort = 0;
01570 }
01571 appendItemToSortFile(hi);
01572
01573 msgHeaderChanged(mFolder,id);
01574
01575 if ((childCount() == 1) && hi) {
01576 setSelected( hi, true );
01577 setCurrentItem( firstChild() );
01578 setSelectionAnchor( currentItem() );
01579 highlightMessage( currentItem() );
01580 }
01581
01582
01583 connect( this, SIGNAL(currentChanged(QListViewItem*)),
01584 this, SLOT(highlightMessage(QListViewItem*)));
01585
01586 emit msgAddedToListView( hi );
01587 END_TIMER(msgAdded);
01588 SHOW_TIMER(msgAdded);
01589 }
01590
01591
01592
01593 void KMHeaders::msgRemoved(int id, QString msgId, QString strippedSubjMD5)
01594 {
01595 if (!isUpdatesEnabled()) return;
01596
01597 if ((id < 0) || (id >= (int)mItems.size()))
01598 return;
01599
01600
01601
01602
01603
01604 disconnect( this, SIGNAL(currentChanged(QListViewItem*)),
01605 this, SLOT(highlightMessage(QListViewItem*)));
01606
01607 KMHeaderItem *removedItem = mItems[id];
01608 if (!removedItem) return;
01609 KMHeaderItem *curItem = currentHeaderItem();
01610
01611 for (int i = id; i < (int)mItems.size() - 1; ++i) {
01612 mItems[i] = mItems[i+1];
01613 mItems[i]->setMsgId( i );
01614 mItems[i]->sortCacheItem()->setId( i );
01615 }
01616
01617 mItems.resize( mItems.size() - 1 );
01618
01619 if (isThreaded() && mFolder->count()) {
01620 if ( !msgId.isEmpty() && mSortCacheItems[msgId] ) {
01621 if (mSortCacheItems[msgId] == removedItem->sortCacheItem())
01622 mSortCacheItems.remove(msgId);
01623 }
01624
01625
01626 if (!strippedSubjMD5.isEmpty() &&
01627 mSubjThreading && mSubjectLists[strippedSubjMD5])
01628 mSubjectLists[strippedSubjMD5]->remove(removedItem->sortCacheItem());
01629
01630
01631 QListViewItem *myParent = removedItem;
01632 QListViewItem *myChild = myParent->firstChild();
01633 QListViewItem *threadRoot = myParent;
01634 while (threadRoot->parent())
01635 threadRoot = threadRoot->parent();
01636 QString key = static_cast<KMHeaderItem*>(threadRoot)->key(mSortCol, !mSortDescending);
01637
01638 QPtrList<QListViewItem> childList;
01639 while (myChild) {
01640 KMHeaderItem *item = static_cast<KMHeaderItem*>(myChild);
01641
01642 if ( !item->aboutToBeDeleted() ) {
01643 childList.append(myChild);
01644 }
01645 myChild = myChild->nextSibling();
01646 if ( item->aboutToBeDeleted() ) {
01647 myParent->takeItem( item );
01648 insertItem( item );
01649 }
01650 item->setTempKey( key + item->key( mSortCol, !mSortDescending ));
01651 if (mSortInfo.fakeSort) {
01652 QObject::disconnect(header(), SIGNAL(clicked(int)), this, SLOT(dirtySortOrder(int)));
01653 KListView::setSorting(mSortCol, !mSortDescending );
01654 mSortInfo.fakeSort = 0;
01655 }
01656 }
01657
01658 for (QPtrListIterator<QListViewItem> it(childList); it.current() ; ++it ) {
01659 QListViewItem *lvi = *it;
01660 KMHeaderItem *item = static_cast<KMHeaderItem*>(lvi);
01661 KMSortCacheItem *sci = item->sortCacheItem();
01662 KMSortCacheItem *parent = findParent( sci );
01663 if ( !parent && mSubjThreading )
01664 parent = findParentBySubject( sci );
01665 myParent->takeItem(lvi);
01666 if ( parent && parent->item() != item && parent->item() != removedItem )
01667 parent->item()->insertItem(lvi);
01668 else
01669 insertItem(lvi);
01670
01671 if (!parent || (sci->isImperfectlyThreaded()
01672 && !mImperfectlyThreadedList.containsRef(item)))
01673 mImperfectlyThreadedList.append(item);
01674 if (parent && !sci->isImperfectlyThreaded()
01675 && mImperfectlyThreadedList.containsRef(item))
01676 mImperfectlyThreadedList.removeRef(item);
01677 }
01678 }
01679
01680 if (!mFolder->count())
01681 folderCleared();
01682
01683 mImperfectlyThreadedList.removeRef(removedItem);
01684 delete removedItem;
01685
01686 if ( curItem ) {
01687 if ( curItem != removedItem ) {
01688 setCurrentItem( curItem );
01689 setSelectionAnchor( currentItem() );
01690 } else {
01691
01692
01693
01694
01695
01696 emit maybeDeleting();
01697 int contentX, contentY;
01698 KMHeaderItem *nextItem = prepareMove( &contentX, &contentY );
01699 finalizeMove( nextItem, contentX, contentY );
01700 }
01701 }
01702
01703 connect( this, SIGNAL(currentChanged(QListViewItem*)),
01704 this, SLOT(highlightMessage(QListViewItem*)));
01705 }
01706
01707
01708
01709 void KMHeaders::msgHeaderChanged(KMFolder*, int msgId)
01710 {
01711 if (msgId<0 || msgId >= (int)mItems.size() || !isUpdatesEnabled()) return;
01712 KMHeaderItem *item = mItems[msgId];
01713 if (item) {
01714 item->irefresh();
01715 item->repaint();
01716 }
01717 }
01718
01719
01720
01721 void KMHeaders::setMsgStatus (KMMsgStatus status, bool toggle)
01722 {
01723 SerNumList serNums;
01724 for (QListViewItemIterator it(this); it.current(); ++it)
01725 if ( it.current()->isSelected() && it.current()->isVisible() ) {
01726 KMHeaderItem *item = static_cast<KMHeaderItem*>(it.current());
01727 KMMsgBase *msgBase = mFolder->getMsgBase(item->msgId());
01728 serNums.append( msgBase->getMsgSerNum() );
01729 }
01730 if (serNums.empty())
01731 return;
01732
01733 KMCommand *command = new KMSetStatusCommand( status, serNums, toggle );
01734 command->start();
01735 }
01736
01737
01738 QPtrList<QListViewItem> KMHeaders::currentThread() const
01739 {
01740 if (!mFolder) return QPtrList<QListViewItem>();
01741
01742
01743 QListViewItem *curItem = currentItem();
01744 if (!curItem) return QPtrList<QListViewItem>();
01745
01746
01747 QListViewItem *topOfThread = curItem;
01748 while ( topOfThread->parent() )
01749 topOfThread = topOfThread->parent();
01750
01751
01752 QPtrList<QListViewItem> list;
01753 QListViewItem *topOfNextThread = topOfThread->nextSibling();
01754 for ( QListViewItemIterator it( topOfThread ) ;
01755 it.current() && it.current() != topOfNextThread ; ++it )
01756 list.append( it.current() );
01757 return list;
01758 }
01759
01760 void KMHeaders::setThreadStatus(KMMsgStatus status, bool toggle)
01761 {
01762 QPtrList<QListViewItem> curThread = currentThread();
01763 QPtrListIterator<QListViewItem> it( curThread );
01764 SerNumList serNums;
01765
01766 for ( it.toFirst() ; it.current() ; ++it ) {
01767 int id = static_cast<KMHeaderItem*>(*it)->msgId();
01768 KMMsgBase *msgBase = mFolder->getMsgBase( id );
01769 serNums.append( msgBase->getMsgSerNum() );
01770 }
01771
01772 if (serNums.empty())
01773 return;
01774
01775 KMCommand *command = new KMSetStatusCommand( status, serNums, toggle );
01776 command->start();
01777 }
01778
01779
01780 int KMHeaders::slotFilterMsg(KMMessage *msg)
01781 {
01782 if ( !msg ) return 2;
01783 msg->setTransferInProgress(false);
01784 int filterResult = kmkernel->filterMgr()->process(msg,KMFilterMgr::Explicit);
01785 if (filterResult == 2) {
01786
01787 kmkernel->emergencyExit( i18n("Unable to process messages: " ) + QString::fromLocal8Bit(strerror(errno)));
01788 return 2;
01789 }
01790 if (msg->parent()) {
01791 int idx = -1;
01792 KMFolder * p = 0;
01793 kmkernel->msgDict()->getLocation( msg, &p, &idx );
01794 assert( p == msg->parent() ); assert( idx >= 0 );
01795 p->unGetMsg( idx );
01796 }
01797
01798 return filterResult;
01799 }
01800
01801
01802 void KMHeaders::slotExpandOrCollapseThread( bool expand )
01803 {
01804 if ( !isThreaded() ) return;
01805
01806 QListViewItem *item = currentItem();
01807 if ( !item ) return;
01808 clearSelection();
01809 item->setSelected( true );
01810 while ( item->parent() )
01811 item = item->parent();
01812 KMHeaderItem * hdrItem = static_cast<KMHeaderItem*>(item);
01813 hdrItem->setOpenRecursive( expand );
01814 if ( !expand )
01815 setCurrentMsg( hdrItem->msgId() );
01816 ensureItemVisible( currentItem() );
01817 }
01818
01819 void KMHeaders::slotExpandOrCollapseAllThreads( bool expand )
01820 {
01821 if ( !isThreaded() ) return;
01822
01823 QListViewItem * item = currentItem();
01824 if( item ) {
01825 clearSelection();
01826 item->setSelected( true );
01827 }
01828
01829 for ( QListViewItem *item = firstChild() ;
01830 item ; item = item->nextSibling() )
01831 static_cast<KMHeaderItem*>(item)->setOpenRecursive( expand );
01832 if ( !expand ) {
01833 QListViewItem * item = currentItem();
01834 if( item ) {
01835 while ( item->parent() )
01836 item = item->parent();
01837 setCurrentMsg( static_cast<KMHeaderItem*>(item)->msgId() );
01838 }
01839 }
01840 ensureItemVisible( currentItem() );
01841 }
01842
01843
01844 void KMHeaders::setStyleDependantFrameWidth()
01845 {
01846
01847 int frameWidth;
01848 if( style().isA("KeramikStyle") )
01849 frameWidth = style().pixelMetric( QStyle::PM_DefaultFrameWidth ) - 1;
01850 else
01851 frameWidth = style().pixelMetric( QStyle::PM_DefaultFrameWidth );
01852 if ( frameWidth < 0 )
01853 frameWidth = 0;
01854 if ( frameWidth != lineWidth() )
01855 setLineWidth( frameWidth );
01856 }
01857
01858
01859 void KMHeaders::styleChange( QStyle& oldStyle )
01860 {
01861 setStyleDependantFrameWidth();
01862 KListView::styleChange( oldStyle );
01863 }
01864
01865
01866 void KMHeaders::setFolderInfoStatus ()
01867 {
01868 if ( !mFolder ) return;
01869 QString str = ( static_cast<KMFolder*>(mFolder) == kmkernel->outboxFolder() )
01870 ? i18n( "1 unsent", "%n unsent", mFolder->countUnread() )
01871 : i18n( "1 unread", "%n unread", mFolder->countUnread() );
01872 str = i18n( "1 message, %1.", "%n messages, %1.", mFolder->count() )
01873 .arg( str );
01874 if ( mFolder->isReadOnly() )
01875 str = i18n("%1 = n messages, m unread.", "%1 Folder is read-only.").arg( str );
01876 BroadcastStatus::instance()->setStatusMsg(str);
01877 }
01878
01879
01880 void KMHeaders::applyFiltersOnMsg()
01881 {
01882 #if 0 // uses action scheduler
01883 KMFilterMgr::FilterSet set = KMFilterMgr::All;
01884 QPtrList<KMFilter> filters;
01885 filters = *( kmkernel->filterMgr() );
01886 ActionScheduler *scheduler = new ActionScheduler( set, filters, this );
01887 scheduler->setAutoDestruct( true );
01888
01889 int contentX, contentY;
01890 KMHeaderItem *nextItem = prepareMove( &contentX, &contentY );
01891 QPtrList<KMMsgBase> msgList = *selectedMsgs(true);
01892 finalizeMove( nextItem, contentX, contentY );
01893
01894 for (KMMsgBase *msg = msgList.first(); msg; msg = msgList.next())
01895 scheduler->execFilters( msg );
01896 #else
01897 int contentX, contentY;
01898 KMHeaderItem *nextItem = prepareMove( &contentX, &contentY );
01899
01900 KMMessageList* msgList = selectedMsgs();
01901 if (msgList->isEmpty())
01902 return;
01903 finalizeMove( nextItem, contentX, contentY );
01904
01905 CREATE_TIMER(filter);
01906 START_TIMER(filter);
01907
01908 for (KMMsgBase* msgBase=msgList->first(); msgBase; msgBase=msgList->next()) {
01909 int idx = msgBase->parent()->find(msgBase);
01910 assert(idx != -1);
01911 KMMessage * msg = msgBase->parent()->getMsg(idx);
01912 if (msg->transferInProgress()) continue;
01913 msg->setTransferInProgress(true);
01914 if ( !msg->isComplete() )
01915 {
01916 FolderJob *job = mFolder->createJob(msg);
01917 connect(job, SIGNAL(messageRetrieved(KMMessage*)),
01918 SLOT(slotFilterMsg(KMMessage*)));
01919 job->start();
01920 } else {
01921 if (slotFilterMsg(msg) == 2) break;
01922 }
01923 }
01924 END_TIMER(filter);
01925 SHOW_TIMER(filter);
01926 #endif
01927 }
01928
01929
01930
01931 void KMHeaders::setMsgRead (int msgId)
01932 {
01933 KMMsgBase *msgBase = mFolder->getMsgBase( msgId );
01934 if (!msgBase)
01935 return;
01936
01937 SerNumList serNums;
01938 if (msgBase->isNew() || msgBase->isUnread()) {
01939 serNums.append( msgBase->getMsgSerNum() );
01940 }
01941
01942 KMCommand *command = new KMSetStatusCommand( KMMsgStatusRead, serNums );
01943 command->start();
01944 }
01945
01946
01947
01948 void KMHeaders::deleteMsg ()
01949 {
01950
01951 if (!mFolder)
01952 return;
01953
01954 int contentX, contentY;
01955 KMHeaderItem *nextItem = prepareMove( &contentX, &contentY );
01956 KMMessageList msgList = *selectedMsgs(true);
01957 finalizeMove( nextItem, contentX, contentY );
01958
01959 KMCommand *command = new KMDeleteMsgCommand( mFolder, msgList );
01960 connect( command, SIGNAL( completed( KMCommand * ) ),
01961 this, SLOT( slotMoveCompleted( KMCommand * ) ) );
01962 command->start();
01963
01964 BroadcastStatus::instance()->setStatusMsg("");
01965
01966 }
01967
01968
01969
01970 void KMHeaders::moveSelectedToFolder( int menuId )
01971 {
01972 if (mMenuToFolder[menuId])
01973 moveMsgToFolder( mMenuToFolder[menuId] );
01974 }
01975
01976
01977 KMHeaderItem* KMHeaders::prepareMove( int *contentX, int *contentY )
01978 {
01979 KMHeaderItem *ret = 0;
01980 emit maybeDeleting();
01981
01982 disconnect( this, SIGNAL(currentChanged(QListViewItem*)),
01983 this, SLOT(highlightMessage(QListViewItem*)));
01984
01985 QListViewItem *curItem;
01986 KMHeaderItem *item;
01987 curItem = currentItem();
01988 while (curItem && curItem->isSelected() && curItem->itemBelow())
01989 curItem = curItem->itemBelow();
01990 while (curItem && curItem->isSelected() && curItem->itemAbove())
01991 curItem = curItem->itemAbove();
01992 item = static_cast<KMHeaderItem*>(curItem);
01993
01994 *contentX = contentsX();
01995 *contentY = contentsY();
01996
01997 if (item && !item->isSelected())
01998 ret = item;
01999
02000 return ret;
02001 }
02002
02003
02004 void KMHeaders::finalizeMove( KMHeaderItem *item, int contentX, int contentY )
02005 {
02006 emit selected( 0 );
02007
02008 if ( item ) {
02009 clearSelection();
02010 setCurrentItem( item );
02011 setSelected( item, true );
02012 setSelectionAnchor( currentItem() );
02013 mPrevCurrent = 0;
02014 highlightMessage( item, false);
02015 }
02016
02017 setContentsPos( contentX, contentY );
02018 makeHeaderVisible();
02019 connect( this, SIGNAL(currentChanged(QListViewItem*)),
02020 this, SLOT(highlightMessage(QListViewItem*)));
02021 }
02022
02023
02024
02025 void KMHeaders::moveMsgToFolder ( KMFolder* destFolder, bool askForConfirmation )
02026 {
02027 if ( destFolder == mFolder ) return;
02028
02029 KMMessageList msgList = *selectedMsgs();
02030 if ( msgList.isEmpty() ) return;
02031 if ( !destFolder && askForConfirmation &&
02032 KMessageBox::warningContinueCancel(this,
02033 i18n("<qt>Do you really want to delete the selected message?<br>"
02034 "Once deleted, it cannot be restored.</qt>",
02035 "<qt>Do you really want to delete the %n selected messages?<br>"
02036 "Once deleted, they cannot be restored.</qt>", msgList.count() ),
02037 i18n("Delete Messages"), KGuiItem(i18n("De&lete"),"editdelete"), "NoConfirmDelete") == KMessageBox::Cancel )
02038 return;
02039
02040
02041 int contentX, contentY;
02042 KMHeaderItem *nextItem = prepareMove( &contentX, &contentY );
02043 msgList = *selectedMsgs(true);
02044 finalizeMove( nextItem, contentX, contentY );
02045
02046 KMCommand *command = new KMMoveCommand( destFolder, msgList );
02047 connect( command, SIGNAL( completed( KMCommand * ) ),
02048 this, SLOT( slotMoveCompleted( KMCommand * ) ) );
02049 command->start();
02050
02051 }
02052
02053 void KMHeaders::slotMoveCompleted( KMCommand *command )
02054 {
02055 kdDebug(5006) << k_funcinfo << command->result() << endl;
02056 bool deleted = static_cast<KMMoveCommand *>( command )->destFolder() == 0;
02057 if ( command->result() == KMCommand::OK ) {
02058
02059 makeHeaderVisible();
02060 #if 0 // enable after the message-freeze
02061 BroadcastStatus::instance()->setStatusMsg(
02062 deleted ? i18nTODO("Messages deleted successfully.") : i18nTODO("Messages moved successfully") );
02063 #else
02064 if ( !deleted ) BroadcastStatus::instance()->setStatusMsg( i18n( "Messages moved successfully" ) );
02065 #endif
02066 } else {
02067
02068
02069
02070
02071
02072
02073 for (QListViewItemIterator it(this); it.current(); it++) {
02074 KMHeaderItem *item = static_cast<KMHeaderItem*>(it.current());
02075 if ( item->aboutToBeDeleted() ) {
02076 item->setAboutToBeDeleted ( false );
02077 item->setSelectable ( true );
02078 KMMsgBase *msgBase = mFolder->getMsgBase(item->msgId());
02079 if ( msgBase->isMessage() ) {
02080 KMMessage *msg = static_cast<KMMessage *>(msgBase);
02081 if ( msg ) msg->setTransferInProgress( false, true );
02082 }
02083 }
02084 }
02085 triggerUpdate();
02086 #if 0 // enable after the message-freeze
02087 if ( command->result() == KMCommand::Failed )
02088 BroadcastStatus::instance()->setStatusMsg(
02089 deleted ? i18nTODO("Deleting messages failed.") : i18nTODO("Moving messages failed.") );
02090 else
02091 BroadcastStatus::instance()->setStatusMsg(
02092 deleted ? i18nTODO("Deleting messages canceled.") : i18nTODO("Moving messages canceled.") );
02093 #else
02094 if ( !deleted ) {
02095 if ( command->result() == KMCommand::Failed )
02096 BroadcastStatus::instance()->setStatusMsg( i18n("Moving messages failed.") );
02097 else
02098 BroadcastStatus::instance()->setStatusMsg( i18n("Moving messages canceled.") );
02099 }
02100 #endif
02101 }
02102 }
02103
02104 bool KMHeaders::canUndo() const
02105 {
02106 return ( kmkernel->undoStack()->size() > 0 );
02107 }
02108
02109
02110 void KMHeaders::undo()
02111 {
02112 kmkernel->undoStack()->undo();
02113 }
02114
02115
02116 void KMHeaders::copySelectedToFolder(int menuId )
02117 {
02118 if (mMenuToFolder[menuId])
02119 copyMsgToFolder( mMenuToFolder[menuId] );
02120 }
02121
02122
02123
02124 void KMHeaders::copyMsgToFolder(KMFolder* destFolder, KMMessage* aMsg)
02125 {
02126 if ( !destFolder )
02127 return;
02128
02129 KMCommand * command = 0;
02130 if (aMsg)
02131 command = new KMCopyCommand( destFolder, aMsg );
02132 else {
02133 KMMessageList msgList = *selectedMsgs();
02134 command = new KMCopyCommand( destFolder, msgList );
02135 }
02136
02137 command->start();
02138 }
02139
02140
02141
02142 void KMHeaders::setCurrentMsg(int cur)
02143 {
02144 if (!mFolder) return;
02145 if (cur >= mFolder->count()) cur = mFolder->count() - 1;
02146 if ((cur >= 0) && (cur < (int)mItems.size())) {
02147 clearSelection();
02148 setCurrentItem( mItems[cur] );
02149 setSelected( mItems[cur], true );
02150 setSelectionAnchor( currentItem() );
02151 }
02152 makeHeaderVisible();
02153 setFolderInfoStatus();
02154 }
02155
02156
02157 void KMHeaders::setSelected( QListViewItem *item, bool selected )
02158 {
02159 if ( !item )
02160 return;
02161
02162 if ( item->isVisible() )
02163 KListView::setSelected( item, selected );
02164
02165
02166
02167 if ( isThreaded() && !item->isOpen() && item->firstChild() ) {
02168 QListViewItem *nextRoot = item->itemBelow();
02169 QListViewItemIterator it( item->firstChild() );
02170 for( ; (*it) != nextRoot; ++it ) {
02171 if ( (*it)->isVisible() )
02172 (*it)->setSelected( selected );
02173 }
02174 }
02175 }
02176
02177 void KMHeaders::setSelectedByIndex( QValueList<int> items, bool selected )
02178 {
02179 for ( QValueList<int>::Iterator it = items.begin(); it != items.end(); ++it )
02180 {
02181 if ( ((*it) >= 0) && ((*it) < (int)mItems.size()) )
02182 {
02183 setSelected( mItems[(*it)], selected );
02184 }
02185 }
02186 }
02187
02188 void KMHeaders::clearSelectableAndAboutToBeDeleted( Q_UINT32 serNum )
02189 {
02190
02191 for (QListViewItemIterator it(this); it.current(); it++) {
02192 KMHeaderItem *item = static_cast<KMHeaderItem*>(it.current());
02193 if ( item->aboutToBeDeleted() ) {
02194 KMMsgBase *msgBase = mFolder->getMsgBase( item->msgId() );
02195 if ( serNum == msgBase->getMsgSerNum() ) {
02196 item->setAboutToBeDeleted ( false );
02197 item->setSelectable ( true );
02198 }
02199 }
02200 }
02201 triggerUpdate();
02202 }
02203
02204
02205 KMMessageList* KMHeaders::selectedMsgs(bool toBeDeleted)
02206 {
02207 mSelMsgBaseList.clear();
02208 for (QListViewItemIterator it(this); it.current(); it++) {
02209 if ( it.current()->isSelected() && it.current()->isVisible() ) {
02210 KMHeaderItem *item = static_cast<KMHeaderItem*>(it.current());
02211 if ( !item->aboutToBeDeleted() ) {
02212 if (toBeDeleted) {
02213
02214 item->setAboutToBeDeleted ( true );
02215 item->setSelectable ( false );
02216 }
02217 KMMsgBase *msgBase = mFolder->getMsgBase(item->msgId());
02218 mSelMsgBaseList.append(msgBase);
02219 }
02220 }
02221 }
02222 return &mSelMsgBaseList;
02223 }
02224
02225
02226 QValueList<int> KMHeaders::selectedItems()
02227 {
02228 QValueList<int> items;
02229 for ( QListViewItemIterator it(this); it.current(); it++ )
02230 {
02231 if ( it.current()->isSelected() && it.current()->isVisible() )
02232 {
02233 KMHeaderItem* item = static_cast<KMHeaderItem*>( it.current() );
02234 items.append( item->msgId() );
02235 }
02236 }
02237 return items;
02238 }
02239
02240
02241 int KMHeaders::firstSelectedMsg() const
02242 {
02243 int selectedMsg = -1;
02244 QListViewItem *item;
02245 for (item = firstChild(); item; item = item->itemBelow())
02246 if (item->isSelected()) {
02247 selectedMsg = (static_cast<KMHeaderItem*>(item))->msgId();
02248 break;
02249 }
02250 return selectedMsg;
02251 }
02252
02253
02254 void KMHeaders::nextMessage()
02255 {
02256 QListViewItem *lvi = currentItem();
02257 if (lvi && lvi->itemBelow()) {
02258 clearSelection();
02259 setSelected( lvi, false );
02260 selectNextMessage();
02261 setSelectionAnchor( currentItem() );
02262 ensureCurrentItemVisible();
02263 }
02264 }
02265
02266 void KMHeaders::selectNextMessage()
02267 {
02268 QListViewItem *lvi = currentItem();
02269 if( lvi ) {
02270 QListViewItem *below = lvi->itemBelow();
02271 QListViewItem *temp = lvi;
02272 if (lvi && below ) {
02273 while (temp) {
02274 temp->firstChild();
02275 temp = temp->parent();
02276 }
02277 lvi->repaint();
02278
02279 (below->isSelected() ? setSelected(lvi, false) : setSelected(below, true));
02280 setCurrentItem(below);
02281 makeHeaderVisible();
02282 setFolderInfoStatus();
02283 }
02284 }
02285 }
02286
02287
02288 void KMHeaders::prevMessage()
02289 {
02290 QListViewItem *lvi = currentItem();
02291 if (lvi && lvi->itemAbove()) {
02292 clearSelection();
02293 setSelected( lvi, false );
02294 selectPrevMessage();
02295 setSelectionAnchor( currentItem() );
02296 ensureCurrentItemVisible();
02297 }
02298 }
02299
02300 void KMHeaders::selectPrevMessage()
02301 {
02302 QListViewItem *lvi = currentItem();
02303 if( lvi ) {
02304 QListViewItem *above = lvi->itemAbove();
02305 QListViewItem *temp = lvi;
02306
02307 if (lvi && above) {
02308 while (temp) {
02309 temp->firstChild();
02310 temp = temp->parent();
02311 }
02312 lvi->repaint();
02313
02314 (above->isSelected() ? setSelected(lvi, false) : setSelected(above, true));
02315 setCurrentItem(above);
02316 makeHeaderVisible();
02317 setFolderInfoStatus();
02318 }
02319 }
02320 }
02321
02322
02323 void KMHeaders::findUnreadAux( KMHeaderItem*& item,
02324 bool & foundUnreadMessage,
02325 bool onlyNew,
02326 bool aDirNext )
02327 {
02328 KMMsgBase* msgBase = 0;
02329 KMHeaderItem *lastUnread = 0;
02330
02331 if (aDirNext)
02332 {
02333 while (item) {
02334 msgBase = mFolder->getMsgBase(item->msgId());
02335 if (!msgBase) continue;
02336 if (msgBase->isUnread() || msgBase->isNew())
02337 foundUnreadMessage = true;
02338
02339 if (!onlyNew && (msgBase->isUnread() || msgBase->isNew())) break;
02340 if (onlyNew && msgBase->isNew()) break;
02341 item = static_cast<KMHeaderItem*>(item->itemBelow());
02342 }
02343 } else {
02344 KMHeaderItem *newItem = static_cast<KMHeaderItem*>(firstChild());
02345 while (newItem)
02346 {
02347 msgBase = mFolder->getMsgBase(newItem->msgId());
02348 if (!msgBase) continue;
02349 if (msgBase->isUnread() || msgBase->isNew())
02350 foundUnreadMessage = true;
02351 if (!onlyNew && (msgBase->isUnread() || msgBase->isNew())
02352 || onlyNew && msgBase->isNew())
02353 lastUnread = newItem;
02354 if (newItem == item) break;
02355 newItem = static_cast<KMHeaderItem*>(newItem->itemBelow());
02356 }
02357 item = lastUnread;
02358 }
02359 }
02360
02361
02362 int KMHeaders::findUnread(bool aDirNext, int aStartAt, bool onlyNew, bool acceptCurrent)
02363 {
02364 KMHeaderItem *item, *pitem;
02365 bool foundUnreadMessage = false;
02366
02367 if (!mFolder) return -1;
02368 if (!(mFolder->count()) > 0) return -1;
02369
02370 if ((aStartAt >= 0) && (aStartAt < (int)mItems.size()))
02371 item = mItems[aStartAt];
02372 else {
02373 item = currentHeaderItem();
02374 if (!item) {
02375 if (aDirNext)
02376 item = static_cast<KMHeaderItem*>(firstChild());
02377 else
02378 item = static_cast<KMHeaderItem*>(lastChild());
02379 }
02380 if (!item)
02381 return -1;
02382
02383 if ( !acceptCurrent )
02384 if (aDirNext)
02385 item = static_cast<KMHeaderItem*>(item->itemBelow());
02386 else
02387 item = static_cast<KMHeaderItem*>(item->itemAbove());
02388 }
02389
02390 pitem = item;
02391
02392 findUnreadAux( item, foundUnreadMessage, onlyNew, aDirNext );
02393
02394
02395
02396
02397
02398
02399 if (item) {
02400 QListViewItem *next = item;
02401 while (next->parent())
02402 next = next->parent();
02403 next = static_cast<KMHeaderItem*>(next)->firstChildNonConst();
02404 while (next && (next != item))
02405 if (static_cast<KMHeaderItem*>(next)->firstChildNonConst())
02406 next = next->firstChild();
02407 else if (next->nextSibling())
02408 next = next->nextSibling();
02409 else {
02410 while (next && (next != item)) {
02411 next = next->parent();
02412 if (next == item)
02413 break;
02414 if (next && next->nextSibling()) {
02415 next = next->nextSibling();
02416 break;
02417 }
02418 }
02419 }
02420 }
02421
02422 item = pitem;
02423
02424 findUnreadAux( item, foundUnreadMessage, onlyNew, aDirNext );
02425 if (item)
02426 return item->msgId();
02427
02428
02429
02430 int unread = mFolder->countUnread();
02431 if (((unread == 0) && foundUnreadMessage) ||
02432 ((unread > 0) && !foundUnreadMessage)) {
02433 mFolder->correctUnreadMsgsCount();
02434 }
02435 return -1;
02436 }
02437
02438
02439 bool KMHeaders::nextUnreadMessage(bool acceptCurrent)
02440 {
02441 if ( !mFolder || !mFolder->countUnread() ) return false;
02442 int i = findUnread(true, -1, false, acceptCurrent);
02443 if ( i < 0 && GlobalSettings::self()->loopOnGotoUnread() !=
02444 GlobalSettings::EnumLoopOnGotoUnread::DontLoop )
02445 {
02446 KMHeaderItem * first = static_cast<KMHeaderItem*>(firstChild());
02447 if ( first )
02448 i = findUnread(true, first->msgId(), false, acceptCurrent);
02449 }
02450 if ( i < 0 )
02451 return false;
02452 setCurrentMsg(i);
02453 ensureCurrentItemVisible();
02454 return true;
02455 }
02456
02457 void KMHeaders::ensureCurrentItemVisible()
02458 {
02459 int i = currentItemIndex();
02460 if ((i >= 0) && (i < (int)mItems.size()))
02461 center( contentsX(), itemPos(mItems[i]), 0, 9.0 );
02462 }
02463
02464
02465 bool KMHeaders::prevUnreadMessage()
02466 {
02467 if ( !mFolder || !mFolder->countUnread() ) return false;
02468 int i = findUnread(false);
02469 if ( i < 0 && GlobalSettings::self()->loopOnGotoUnread() !=
02470 GlobalSettings::EnumLoopOnGotoUnread::DontLoop )
02471 {
02472 KMHeaderItem * last = static_cast<KMHeaderItem*>(lastItem());
02473 if ( last )
02474 i = findUnread(false, last->msgId() );
02475 }
02476 if ( i < 0 )
02477 return false;
02478 setCurrentMsg(i);
02479 ensureCurrentItemVisible();
02480 return true;
02481 }
02482
02483
02484
02485 void KMHeaders::slotNoDrag()
02486 {
02487 mMousePressed = false;
02488 }
02489
02490
02491
02492 void KMHeaders::makeHeaderVisible()
02493 {
02494 if (currentItem())
02495 ensureItemVisible( currentItem() );
02496 }
02497
02498
02499 void KMHeaders::highlightMessage(QListViewItem* lvi, bool markitread)
02500 {
02501
02502 if (lvi && !lvi->isSelectable()) return;
02503
02504 KMHeaderItem *item = static_cast<KMHeaderItem*>(lvi);
02505 if (lvi != mPrevCurrent) {
02506 if (mPrevCurrent && mFolder)
02507 {
02508 KMMessage *prevMsg = mFolder->getMsg(mPrevCurrent->msgId());
02509 if (prevMsg && mReaderWindowActive)
02510 {
02511 mFolder->ignoreJobsForMessage(prevMsg);
02512 if (!prevMsg->transferInProgress())
02513 mFolder->unGetMsg(mPrevCurrent->msgId());
02514 }
02515 }
02516 mPrevCurrent = item;
02517 }
02518
02519 if (!item)
02520 {
02521 emit selected( 0 ); return;
02522 }
02523
02524 int idx = item->msgId();
02525 if (mReaderWindowActive)
02526 {
02527 KMMessage *msg = mFolder->getMsg(idx);
02528 if (!msg || msg->transferInProgress())
02529 {
02530 emit selected( 0 );
02531 mPrevCurrent = 0;
02532 return;
02533 }
02534 }
02535
02536 BroadcastStatus::instance()->setStatusMsg("");
02537 if (markitread && idx >= 0) setMsgRead(idx);
02538 mItems[idx]->irefresh();
02539 mItems[idx]->repaint();
02540 emit selected(mFolder->getMsg(idx));
02541 setFolderInfoStatus();
02542 }
02543
02544 void KMHeaders::resetCurrentTime()
02545 {
02546 mDate.reset();
02547 QTimer::singleShot( 1000, this, SLOT( resetCurrentTime() ) );
02548 }
02549
02550
02551 void KMHeaders::selectMessage(QListViewItem* lvi)
02552 {
02553 KMHeaderItem *item = static_cast<KMHeaderItem*>(lvi);
02554 if (!item)
02555 return;
02556
02557 int idx = item->msgId();
02558 KMMessage *msg = mFolder->getMsg(idx);
02559 if (!msg->transferInProgress())
02560 {
02561 emit activated(mFolder->getMsg(idx));
02562 }
02563
02564
02565
02566 }
02567
02568
02569
02570 void KMHeaders::updateMessageList( bool set_selection, bool forceJumpToUnread )
02571 {
02572 mPrevCurrent = 0;
02573 noRepaint = true;
02574 clear();
02575 noRepaint = false;
02576 KListView::setSorting( mSortCol, !mSortDescending );
02577 if (!mFolder) {
02578 mItems.resize(0);
02579 repaint();
02580 return;
02581 }
02582 readSortOrder( set_selection, forceJumpToUnread );
02583 emit messageListUpdated();
02584 }
02585
02586
02587
02588
02589
02590
02591
02592
02593
02594
02595
02596
02597
02598
02599
02600
02601
02602
02603 void KMHeaders::keyPressEvent( QKeyEvent * e )
02604 {
02605 bool cntrl = (e->state() & ControlButton );
02606 bool shft = (e->state() & ShiftButton );
02607 QListViewItem *cur = currentItem();
02608
02609 if (!e || !firstChild())
02610 return;
02611
02612
02613 if (!cur) {
02614 setCurrentItem( firstChild() );
02615 setSelectionAnchor( currentItem() );
02616 return;
02617 }
02618
02619
02620 if (cur->isSelectable() && e->ascii() == ' ' ) {
02621 setSelected( cur, !cur->isSelected() );
02622 highlightMessage( cur, false);
02623 return;
02624 }
02625
02626 if (cntrl) {
02627 if (!shft)
02628 disconnect(this,SIGNAL(currentChanged(QListViewItem*)),
02629 this,SLOT(highlightMessage(QListViewItem*)));
02630 switch (e->key()) {
02631 case Key_Down:
02632 case Key_Up:
02633 case Key_Home:
02634 case Key_End:
02635 case Key_Next:
02636 case Key_Prior:
02637 case Key_Escape:
02638 KListView::keyPressEvent( e );
02639 }
02640 if (!shft)
02641 connect(this,SIGNAL(currentChanged(QListViewItem*)),
02642 this,SLOT(highlightMessage(QListViewItem*)));
02643 }
02644 }
02645
02646
02647
02648 void KMHeaders::rightButtonPressed( QListViewItem *lvi, const QPoint &, int )
02649 {
02650 if (!lvi)
02651 return;
02652
02653 if (!(lvi->isSelected())) {
02654 clearSelection();
02655 }
02656 setSelected( lvi, true );
02657 slotRMB();
02658 }
02659
02660
02661 void KMHeaders::contentsMousePressEvent(QMouseEvent* e)
02662 {
02663 mPressPos = e->pos();
02664 QListViewItem *lvi = itemAt( contentsToViewport( e->pos() ));
02665 bool wasSelected = false;
02666 bool rootDecoClicked = false;
02667 if (lvi) {
02668 wasSelected = lvi->isSelected();
02669 rootDecoClicked =
02670 ( mPressPos.x() <= header()->cellPos( header()->mapToActual( 0 ) ) +
02671 treeStepSize() * ( lvi->depth() + ( rootIsDecorated() ? 1 : 0 ) ) + itemMargin() )
02672 && ( mPressPos.x() >= header()->cellPos( header()->mapToActual( 0 ) ) );
02673
02674 if ( rootDecoClicked ) {
02675
02676
02677
02678
02679 if ( !lvi->isOpen() && lvi->firstChild() ) {
02680 QListViewItem *nextRoot = lvi->itemBelow();
02681 QListViewItemIterator it( lvi->firstChild() );
02682 for( ; (*it) != nextRoot; ++it )
02683 (*it)->setSelected( false );
02684 }
02685 }
02686 }
02687
02688
02689 KListView::contentsMousePressEvent(e);
02690
02691
02692
02693 if ( e->state() & ShiftButton ) {
02694 QListViewItemIterator it( this, QListViewItemIterator::Invisible );
02695 while ( it.current() ) {
02696 it.current()->setSelected( false );
02697 ++it;
02698 }
02699 }
02700
02701 if ( rootDecoClicked ) {
02702
02703 if ( lvi && !lvi->isOpen() && lvi->isSelected() )
02704 setSelected( lvi, true );
02705 }
02706
02707 if ( lvi && !rootDecoClicked ) {
02708 if ( lvi != currentItem() )
02709 highlightMessage( lvi );
02710
02711
02712
02713
02714 if ( !( e->state() & ControlButton ) && !wasSelected )
02715 setSelected( lvi, true );
02716
02717 if ( e->state() & ControlButton )
02718 setSelected( lvi, !wasSelected );
02719
02720 if ((e->button() == LeftButton) )
02721 mMousePressed = true;
02722 }
02723 }
02724
02725
02726 void KMHeaders::contentsMouseReleaseEvent(QMouseEvent* e)
02727 {
02728 if (e->button() != RightButton)
02729 KListView::contentsMouseReleaseEvent(e);
02730
02731 mMousePressed = false;
02732 }
02733
02734
02735 void KMHeaders::contentsMouseMoveEvent( QMouseEvent* e )
02736 {
02737 if (mMousePressed &&
02738 (e->pos() - mPressPos).manhattanLength() > KGlobalSettings::dndEventDelay()) {
02739 mMousePressed = false;
02740 QListViewItem *item = itemAt( contentsToViewport(mPressPos) );
02741 if ( item ) {
02742 MailList mailList;
02743 unsigned int count = 0;
02744 for( QListViewItemIterator it(this); it.current(); it++ )
02745 if( it.current()->isSelected() ) {
02746 KMHeaderItem *item = static_cast<KMHeaderItem*>(it.current());
02747 KMMsgBase *msg = mFolder->getMsgBase(item->msgId());
02748 MailSummary mailSummary( msg->getMsgSerNum(), msg->msgIdMD5(),
02749 msg->subject(), msg->fromStrip(),
02750 msg->toStrip(), msg->date() );
02751 mailList.append( mailSummary );
02752 ++count;
02753 }
02754 MailListDrag *d = new MailListDrag( mailList, viewport() );
02755
02756
02757 QPixmap pixmap;
02758 if( count == 1 )
02759 pixmap = QPixmap( DesktopIcon("message", KIcon::SizeSmall) );
02760 else
02761 pixmap = QPixmap( DesktopIcon("kmultiple", KIcon::SizeSmall) );
02762
02763
02764 if( !pixmap.isNull() ) {
02765 QPoint hotspot( pixmap.width() / 2, pixmap.height() / 2 );
02766 d->setPixmap( pixmap, hotspot );
02767 }
02768 d->drag();
02769 }
02770 }
02771 }
02772
02773 void KMHeaders::highlightMessage(QListViewItem* i)
02774 {
02775 highlightMessage( i, false );
02776 }
02777
02778
02779 void KMHeaders::slotRMB()
02780 {
02781 if (!topLevelWidget()) return;
02782
02783 QPopupMenu *menu = new QPopupMenu(this);
02784
02785 mMenuToFolder.clear();
02786
02787 mOwner->updateMessageMenu();
02788
02789 bool out_folder = kmkernel->folderIsDraftOrOutbox(mFolder);
02790 if ( out_folder )
02791 mOwner->editAction()->plug(menu);
02792 else {
02793
02794 mOwner->replyAction()->plug(menu);
02795 mOwner->replyAllAction()->plug(menu);
02796 mOwner->replyAuthorAction()->plug( menu );
02797 mOwner->replyListAction()->plug(menu);
02798 mOwner->forwardMenu()->plug(menu);
02799 mOwner->bounceAction()->plug(menu);
02800 mOwner->sendAgainAction()->plug(menu);
02801 }
02802 menu->insertSeparator();
02803
02804 QPopupMenu *msgCopyMenu = new QPopupMenu(menu);
02805 KMCopyCommand::folderToPopupMenu( false, this, &mMenuToFolder, msgCopyMenu );
02806 menu->insertItem(i18n("&Copy To"), msgCopyMenu);
02807
02808 if ( mFolder->isReadOnly() ) {
02809 int id = menu->insertItem( i18n("&Move To") );
02810 menu->setItemEnabled( id, false );
02811 } else {
02812 QPopupMenu *msgMoveMenu = new QPopupMenu(menu);
02813 KMMoveCommand::folderToPopupMenu( true, this, &mMenuToFolder, msgMoveMenu );
02814 menu->insertItem(i18n("&Move To"), msgMoveMenu);
02815 }
02816
02817 if ( !out_folder ) {
02818 mOwner->statusMenu()->plug( menu );
02819 if ( mOwner->threadStatusMenu()->isEnabled() ) {
02820 mOwner->threadStatusMenu()->plug( menu );
02821 }
02822 }
02823
02824 if (mOwner->watchThreadAction()->isEnabled() ) {
02825 menu->insertSeparator();
02826 mOwner->watchThreadAction()->plug(menu);
02827 mOwner->ignoreThreadAction()->plug(menu);
02828 }
02829 menu->insertSeparator();
02830 mOwner->trashAction()->plug(menu);
02831 mOwner->deleteAction()->plug(menu);
02832
02833 menu->insertSeparator();
02834 mOwner->saveAsAction()->plug(menu);
02835 mOwner->saveAttachmentsAction()->plug(menu);
02836 mOwner->printAction()->plug(menu);
02837
02838 if ( !out_folder ) {
02839 menu->insertSeparator();
02840 mOwner->action("apply_filters")->plug(menu);
02841 mOwner->filterMenu()->plug( menu );
02842 }
02843
02844 mOwner->action("apply_filter_actions")->plug(menu);
02845
02846 KAcceleratorManager::manage(menu);
02847 kmkernel->setContextMenuShown( true );
02848 menu->exec(QCursor::pos(), 0);
02849 kmkernel->setContextMenuShown( false );
02850 delete menu;
02851 }
02852
02853
02854 KMMessage* KMHeaders::currentMsg()
02855 {
02856 KMHeaderItem *hi = currentHeaderItem();
02857 if (!hi)
02858 return 0;
02859 else
02860 return mFolder->getMsg(hi->msgId());
02861 }
02862
02863
02864 KMHeaderItem* KMHeaders::currentHeaderItem()
02865 {
02866 return static_cast<KMHeaderItem*>(currentItem());
02867 }
02868
02869
02870 int KMHeaders::currentItemIndex()
02871 {
02872 KMHeaderItem* item = currentHeaderItem();
02873 if (item)
02874 return item->msgId();
02875 else
02876 return -1;
02877 }
02878
02879
02880 void KMHeaders::setCurrentItemByIndex(int msgIdx)
02881 {
02882 if ((msgIdx >= 0) && (msgIdx < (int)mItems.size())) {
02883 clearSelection();
02884 bool unchanged = (currentItem() == mItems[msgIdx]);
02885 setCurrentItem( mItems[msgIdx] );
02886 setSelected( mItems[msgIdx], true );
02887 setSelectionAnchor( currentItem() );
02888 if (unchanged)
02889 highlightMessage( mItems[msgIdx], false);
02890 }
02891 }
02892
02893
02894 int KMHeaders::topItemIndex()
02895 {
02896 KMHeaderItem *item = static_cast<KMHeaderItem*>(itemAt(QPoint(1,1)));
02897 if (item)
02898 return item->msgId();
02899 else
02900 return -1;
02901 }
02902
02903
02904
02905 void KMHeaders::showNewMail()
02906 {
02907 if (mSortCol != mPaintInfo.dateCol)
02908 return;
02909 for( int i = 0; i < (int)mItems.size(); ++i)
02910 if (mFolder->getMsgBase(i)->isNew()) {
02911 if (!mSortDescending)
02912 setTopItemByIndex( currentItemIndex() );
02913 break;
02914 }
02915 }
02916
02917
02918 void KMHeaders::setTopItemByIndex( int aMsgIdx)
02919 {
02920 int msgIdx = aMsgIdx;
02921 if (msgIdx < 0)
02922 msgIdx = 0;
02923 else if (msgIdx >= (int)mItems.size())
02924 msgIdx = mItems.size() - 1;
02925 if ((msgIdx >= 0) && (msgIdx < (int)mItems.size()))
02926 setContentsPos( 0, itemPos( mItems[msgIdx] ));
02927 }
02928
02929
02930 void KMHeaders::setNestedOverride( bool override )
02931 {
02932 mSortInfo.dirty = true;
02933 mNestedOverride = override;
02934 setRootIsDecorated( nestingPolicy != AlwaysOpen
02935 && isThreaded() );
02936 QString sortFile = mFolder->indexLocation() + ".sorted";
02937 unlink(QFile::encodeName(sortFile));
02938 reset();
02939 }
02940
02941
02942 void KMHeaders::setSubjectThreading( bool aSubjThreading )
02943 {
02944 mSortInfo.dirty = true;
02945 mSubjThreading = aSubjThreading;
02946 QString sortFile = mFolder->indexLocation() + ".sorted";
02947 unlink(QFile::encodeName(sortFile));
02948 reset();
02949 }
02950
02951
02952 void KMHeaders::setOpen( QListViewItem *item, bool open )
02953 {
02954 if ((nestingPolicy != AlwaysOpen)|| open)
02955 ((KMHeaderItem*)item)->setOpenRecursive( open );
02956 }
02957
02958
02959 const KMMsgBase* KMHeaders::getMsgBaseForItem( const QListViewItem *item ) const
02960 {
02961 const KMHeaderItem *hi = static_cast<const KMHeaderItem *> ( item );
02962 return mFolder->getMsgBase( hi->msgId() );
02963 }
02964
02965
02966 void KMHeaders::setSorting( int column, bool ascending )
02967 {
02968 kdDebug(5006) << k_funcinfo << column << " " << ascending << endl;
02969
02970 if (column != -1) {
02971
02972
02973
02974 if(mSortInfo.dirty || column != mSortInfo.column || ascending != mSortInfo.ascending) {
02975 QObject::disconnect(header(), SIGNAL(clicked(int)), this, SLOT(dirtySortOrder(int)));
02976 mSortInfo.dirty = true;
02977 }
02978
02979 mSortCol = column;
02980 mSortDescending = !ascending;
02981
02982 if (!ascending && (column == mPaintInfo.dateCol))
02983 mPaintInfo.orderOfArrival = !mPaintInfo.orderOfArrival;
02984
02985 if (!ascending && (column == mPaintInfo.subCol))
02986 mPaintInfo.status = !mPaintInfo.status;
02987
02988 QString colText = i18n( "Date" );
02989 if (mPaintInfo.orderOfArrival)
02990 colText = i18n( "Order of Arrival" );
02991 setColumnText( mPaintInfo.dateCol, colText);
02992
02993 colText = i18n( "Subject" );
02994 if (mPaintInfo.status)
02995 colText = colText + i18n( " (Status)" );
02996 setColumnText( mPaintInfo.subCol, colText);
02997 }
02998 KListView::setSorting( column, ascending );
02999 ensureCurrentItemVisible();
03000
03001
03002 if ( mFolder ) {
03003 writeFolderConfig();
03004 writeSortOrder();
03005 }
03006 }
03007
03008
03009 static void internalWriteItem(FILE *sortStream, KMFolder *folder, int msgid,
03010 int parent_id, QString key,
03011 bool update_discover=true)
03012 {
03013 unsigned long msgSerNum;
03014 unsigned long parentSerNum;
03015 msgSerNum = kmkernel->msgDict()->getMsgSerNum( folder, msgid );
03016 if (parent_id >= 0)
03017 parentSerNum = kmkernel->msgDict()->getMsgSerNum( folder, parent_id ) + KMAIL_RESERVED;
03018 else
03019 parentSerNum = (unsigned long)(parent_id + KMAIL_RESERVED);
03020
03021 fwrite(&msgSerNum, sizeof(msgSerNum), 1, sortStream);
03022 fwrite(&parentSerNum, sizeof(parentSerNum), 1, sortStream);
03023 Q_INT32 len = key.length() * sizeof(QChar);
03024 fwrite(&len, sizeof(len), 1, sortStream);
03025 if (len)
03026 fwrite(key.unicode(), QMIN(len, KMAIL_MAX_KEY_LEN), 1, sortStream);
03027
03028 if (update_discover) {
03029
03030 Q_INT32 discovered_count = 0;
03031 fseek(sortStream, KMAIL_MAGIC_HEADER_OFFSET + 20, SEEK_SET);
03032 fread(&discovered_count, sizeof(discovered_count), 1, sortStream);
03033 discovered_count++;
03034 fseek(sortStream, KMAIL_MAGIC_HEADER_OFFSET + 20, SEEK_SET);
03035 fwrite(&discovered_count, sizeof(discovered_count), 1, sortStream);
03036 }
03037 }
03038
03039 void KMHeaders::folderCleared()
03040 {
03041 mSortCacheItems.clear();
03042 mSubjectLists.clear();
03043 mImperfectlyThreadedList.clear();
03044 mPrevCurrent = 0;
03045 emit selected(0);
03046 }
03047
03048 bool KMHeaders::writeSortOrder()
03049 {
03050 QString sortFile = KMAIL_SORT_FILE(mFolder);
03051
03052 if (!mSortInfo.dirty) {
03053 struct stat stat_tmp;
03054 if(stat(QFile::encodeName(sortFile), &stat_tmp) == -1) {
03055 mSortInfo.dirty = true;
03056 }
03057 }
03058 if (mSortInfo.dirty) {
03059 if (!mFolder->count()) {
03060
03061 unlink(QFile::encodeName(sortFile));
03062 return true;
03063 }
03064 QString tempName = sortFile + ".temp";
03065 unlink(QFile::encodeName(tempName));
03066 FILE *sortStream = fopen(QFile::encodeName(tempName), "w");
03067 if (!sortStream)
03068 return false;
03069
03070 mSortInfo.ascending = !mSortDescending;
03071 mSortInfo.dirty = false;
03072 mSortInfo.column = mSortCol;
03073 fprintf(sortStream, KMAIL_SORT_HEADER, KMAIL_SORT_VERSION);
03074
03075 Q_INT32 byteOrder = 0x12345678;
03076 Q_INT32 column = mSortCol;
03077 Q_INT32 ascending= !mSortDescending;
03078 Q_INT32 threaded = isThreaded();
03079 Q_INT32 appended=0;
03080 Q_INT32 discovered_count = 0;
03081 Q_INT32 sorted_count=0;
03082 fwrite(&byteOrder, sizeof(byteOrder), 1, sortStream);
03083 fwrite(&column, sizeof(column), 1, sortStream);
03084 fwrite(&ascending, sizeof(ascending), 1, sortStream);
03085 fwrite(&threaded, sizeof(threaded), 1, sortStream);
03086 fwrite(&appended, sizeof(appended), 1, sortStream);
03087 fwrite(&discovered_count, sizeof(discovered_count), 1, sortStream);
03088 fwrite(&sorted_count, sizeof(sorted_count), 1, sortStream);
03089
03090 QPtrStack<KMHeaderItem> items;
03091 {
03092 QPtrStack<QListViewItem> s;
03093 for (QListViewItem * i = firstChild(); i; ) {
03094 items.push((KMHeaderItem *)i);
03095 if ( i->firstChild() ) {
03096 s.push( i );
03097 i = i->firstChild();
03098 } else if( i->nextSibling()) {
03099 i = i->nextSibling();
03100 } else {
03101 for(i=0; !i && s.count(); i = s.pop()->nextSibling());
03102 }
03103 }
03104 }
03105
03106 KMMsgBase *kmb;
03107 while(KMHeaderItem *i = items.pop()) {
03108 int parent_id = -1;
03109 if (threaded) {
03110 kmb = mFolder->getMsgBase( i->mMsgId );
03111 assert(kmb);
03112
03113 QString replymd5 = kmb->replyToIdMD5();
03114 QString replyToAuxId = kmb->replyToAuxIdMD5();
03115 KMSortCacheItem *p = NULL;
03116 if(!replymd5.isEmpty())
03117 p = mSortCacheItems[replymd5];
03118
03119 if (p)
03120 parent_id = p->id();
03121
03122
03123
03124
03125
03126 if (replymd5.isEmpty()
03127 && replyToAuxId.isEmpty()
03128 && !kmb->subjectIsPrefixed() )
03129 parent_id = -2;
03130
03131
03132
03133 }
03134 internalWriteItem(sortStream, mFolder, i->mMsgId, parent_id,
03135 i->key(mSortCol, !mSortDescending), false);
03136
03137 sorted_count++;
03138 }
03139
03140
03141 fseek(sortStream, KMAIL_MAGIC_HEADER_OFFSET, SEEK_SET);
03142 fwrite(&byteOrder, sizeof(byteOrder), 1, sortStream);
03143 fwrite(&column, sizeof(column), 1, sortStream);
03144 fwrite(&ascending, sizeof(ascending), 1, sortStream);
03145 fwrite(&threaded, sizeof(threaded), 1, sortStream);
03146 fwrite(&appended, sizeof(appended), 1, sortStream);
03147 fwrite(&discovered_count, sizeof(discovered_count), 1, sortStream);
03148 fwrite(&sorted_count, sizeof(sorted_count), 1, sortStream);
03149 if (sortStream && ferror(sortStream)) {
03150 fclose(sortStream);
03151 unlink(QFile::encodeName(sortFile));
03152 kdWarning(5006) << "Error: Failure modifying " << sortFile << " (No space left on device?)" << endl;
03153 kdWarning(5006) << __FILE__ << ":" << __LINE__ << endl;
03154 kmkernel->emergencyExit( i18n("Failure modifying %1\n(No space left on device?)").arg( sortFile ));
03155 }
03156 fclose(sortStream);
03157 ::rename(QFile::encodeName(tempName), QFile::encodeName(sortFile));
03158 }
03159
03160 return true;
03161 }
03162
03163 void KMHeaders::appendItemToSortFile(KMHeaderItem *khi)
03164 {
03165 QString sortFile = KMAIL_SORT_FILE(mFolder);
03166 if(FILE *sortStream = fopen(QFile::encodeName(sortFile), "r+")) {
03167 int parent_id = -1;
03168
03169 if (isThreaded()) {
03170 KMSortCacheItem *sci = khi->sortCacheItem();
03171 KMMsgBase *kmb = mFolder->getMsgBase( khi->mMsgId );
03172 if(sci->parent() && !sci->isImperfectlyThreaded())
03173 parent_id = sci->parent()->id();
03174 else if(kmb->replyToIdMD5().isEmpty()
03175 && kmb->replyToAuxIdMD5().isEmpty()
03176 && !kmb->subjectIsPrefixed())
03177 parent_id = -2;
03178 }
03179
03180 internalWriteItem(sortStream, mFolder, khi->mMsgId, parent_id,
03181 khi->key(mSortCol, !mSortDescending), false);
03182
03183
03184 Q_INT32 appended = 1;
03185 fseek(sortStream, KMAIL_MAGIC_HEADER_OFFSET + 16, SEEK_SET);
03186 fwrite(&appended, sizeof(appended), 1, sortStream);
03187 fseek(sortStream, KMAIL_MAGIC_HEADER_OFFSET + 16, SEEK_SET);
03188
03189 if (sortStream && ferror(sortStream)) {
03190 fclose(sortStream);
03191 unlink(QFile::encodeName(sortFile));
03192 kdWarning(5006) << "Error: Failure modifying " << sortFile << " (No space left on device?)" << endl;
03193 kdWarning(5006) << __FILE__ << ":" << __LINE__ << endl;
03194 kmkernel->emergencyExit( i18n("Failure modifying %1\n(No space left on device?)").arg( sortFile ));
03195 }
03196 fclose(sortStream);
03197 } else {
03198 mSortInfo.dirty = true;
03199 }
03200 }
03201
03202 void KMHeaders::dirtySortOrder(int column)
03203 {
03204 mSortInfo.dirty = true;
03205 QObject::disconnect(header(), SIGNAL(clicked(int)), this, SLOT(dirtySortOrder(int)));
03206 setSorting(column, mSortInfo.column == column ? !mSortInfo.ascending : true);
03207 }
03208 void KMSortCacheItem::updateSortFile( FILE *sortStream, KMFolder *folder,
03209 bool waiting_for_parent, bool update_discover)
03210 {
03211 if(mSortOffset == -1) {
03212 fseek(sortStream, 0, SEEK_END);
03213 mSortOffset = ftell(sortStream);
03214 } else {
03215 fseek(sortStream, mSortOffset, SEEK_SET);
03216 }
03217
03218 int parent_id = -1;
03219 if(!waiting_for_parent) {
03220 if(mParent && !isImperfectlyThreaded())
03221 parent_id = mParent->id();
03222 }
03223 internalWriteItem(sortStream, folder, mId, parent_id, mKey, update_discover);
03224 }
03225
03226 static bool compare_ascending = false;
03227 static bool compare_toplevel = true;
03228 static int compare_KMSortCacheItem(const void *s1, const void *s2)
03229 {
03230 if ( !s1 || !s2 )
03231 return 0;
03232 KMSortCacheItem **b1 = (KMSortCacheItem **)s1;
03233 KMSortCacheItem **b2 = (KMSortCacheItem **)s2;
03234 int ret = (*b1)->key().compare((*b2)->key());
03235 if(compare_ascending || !compare_toplevel)
03236 ret = -ret;
03237 return ret;
03238 }
03239
03240
03241 void KMHeaders::buildThreadingTree( QMemArray<KMSortCacheItem *> sortCache )
03242 {
03243 mSortCacheItems.clear();
03244 mSortCacheItems.resize( mFolder->count() * 2 );
03245
03246
03247 for(int x = 0; x < mFolder->count(); x++) {
03248 KMMsgBase *mi = mFolder->getMsgBase(x);
03249 QString md5 = mi->msgIdMD5();
03250 if(!md5.isEmpty())
03251 mSortCacheItems.replace(md5, sortCache[x]);
03252 }
03253 }
03254
03255
03256 void KMHeaders::buildSubjectThreadingTree( QMemArray<KMSortCacheItem *> sortCache )
03257 {
03258 mSubjectLists.clear();
03259 mSubjectLists.resize( mFolder->count() * 2 );
03260
03261 for(int x = 0; x < mFolder->count(); x++) {
03262
03263 if ( sortCache[x]->parent()
03264 && sortCache[x]->parent()->id() != -666 ) continue;
03265 KMMsgBase *mi = mFolder->getMsgBase(x);
03266 QString subjMD5 = mi->strippedSubjectMD5();
03267 if (subjMD5.isEmpty()) {
03268 mFolder->getMsgBase(x)->initStrippedSubjectMD5();
03269 subjMD5 = mFolder->getMsgBase(x)->strippedSubjectMD5();
03270 }
03271 if( subjMD5.isEmpty() ) continue;
03272
03273
03274
03275 if (!mSubjectLists.find(subjMD5))
03276 mSubjectLists.insert(subjMD5, new QPtrList<KMSortCacheItem>());
03277
03278
03279
03280
03281 int p=0;
03282 for (QPtrListIterator<KMSortCacheItem> it(*mSubjectLists[subjMD5]);
03283 it.current(); ++it) {
03284 KMMsgBase *mb = mFolder->getMsgBase((*it)->id());
03285 if ( mb->date() < mi->date())
03286 break;
03287 p++;
03288 }
03289 mSubjectLists[subjMD5]->insert( p, sortCache[x]);
03290 }
03291 }
03292
03293
03294 KMSortCacheItem* KMHeaders::findParent(KMSortCacheItem *item)
03295 {
03296 KMSortCacheItem *parent = NULL;
03297 if (!item) return parent;
03298 KMMsgBase *msg = mFolder->getMsgBase(item->id());
03299 QString replyToIdMD5 = msg->replyToIdMD5();
03300 item->setImperfectlyThreaded(true);
03301
03302
03303 if(!replyToIdMD5.isEmpty()) {
03304 parent = mSortCacheItems[replyToIdMD5];
03305 if (parent)
03306 item->setImperfectlyThreaded(false);
03307 }
03308 if (!parent) {
03309
03310
03311
03312
03313
03314
03315 QString ref = msg->replyToAuxIdMD5();
03316 if (!ref.isEmpty())
03317 parent = mSortCacheItems[ref];
03318 }
03319 return parent;
03320 }
03321
03322 KMSortCacheItem* KMHeaders::findParentBySubject(KMSortCacheItem *item)
03323 {
03324 KMSortCacheItem *parent = NULL;
03325 if (!item) return parent;
03326
03327 KMMsgBase *msg = mFolder->getMsgBase(item->id());
03328
03329
03330
03331
03332 if (!msg->subjectIsPrefixed())
03333 return parent;
03334
03335 QString replyToIdMD5 = msg->replyToIdMD5();
03336 QString subjMD5 = msg->strippedSubjectMD5();
03337 if (!subjMD5.isEmpty() && mSubjectLists[subjMD5]) {
03338
03339
03340 for (QPtrListIterator<KMSortCacheItem> it2(*mSubjectLists[subjMD5]);
03341 it2.current(); ++it2) {
03342 KMMsgBase *mb = mFolder->getMsgBase((*it2)->id());
03343 if ( !mb ) return parent;
03344
03345 if ( item == (*it2) ) continue;
03346 int delta = msg->date() - mb->date();
03347
03348
03349 if (delta > 0 ) {
03350
03351 if (delta < 3628899)
03352 parent = (*it2);
03353 break;
03354 }
03355 }
03356 }
03357 return parent;
03358 }
03359
03360 bool KMHeaders::readSortOrder( bool set_selection, bool forceJumpToUnread )
03361 {
03362
03363 Q_INT32 column, ascending, threaded, discovered_count, sorted_count, appended;
03364 Q_INT32 deleted_count = 0;
03365 bool unread_exists = false;
03366 bool jumpToUnread = GlobalSettings::self()->jumpToUnread() ||
03367 forceJumpToUnread;
03368 QMemArray<KMSortCacheItem *> sortCache(mFolder->count());
03369 KMSortCacheItem root;
03370 root.setId(-666);
03371 bool error = false;
03372
03373
03374 QPtrList<KMSortCacheItem> unparented;
03375 mImperfectlyThreadedList.clear();
03376
03377
03378 mItems.fill( 0, mFolder->count() );
03379 sortCache.fill( 0 );
03380
03381 QString sortFile = KMAIL_SORT_FILE(mFolder);
03382 FILE *sortStream = fopen(QFile::encodeName(sortFile), "r+");
03383 mSortInfo.fakeSort = 0;
03384
03385 if(sortStream) {
03386 mSortInfo.fakeSort = 1;
03387 int version = 0;
03388 if (fscanf(sortStream, KMAIL_SORT_HEADER, &version) != 1)
03389 version = -1;
03390 if(version == KMAIL_SORT_VERSION) {
03391 Q_INT32 byteOrder = 0;
03392 fread(&byteOrder, sizeof(byteOrder), 1, sortStream);
03393 if (byteOrder == 0x12345678)
03394 {
03395 fread(&column, sizeof(column), 1, sortStream);
03396 fread(&ascending, sizeof(ascending), 1, sortStream);
03397 fread(&threaded, sizeof(threaded), 1, sortStream);
03398 fread(&appended, sizeof(appended), 1, sortStream);
03399 fread(&discovered_count, sizeof(discovered_count), 1, sortStream);
03400 fread(&sorted_count, sizeof(sorted_count), 1, sortStream);
03401
03402
03403 KListView::setSorting(-1);
03404 header()->setSortIndicator(column, ascending);
03405 QObject::connect(header(), SIGNAL(clicked(int)), this, SLOT(dirtySortOrder(int)));
03406
03407 mSortInfo.dirty = false;
03408 mSortInfo.column = (short)column;
03409 mSortInfo.ascending = (compare_ascending = ascending);
03410
03411 KMSortCacheItem *item;
03412 unsigned long serNum, parentSerNum;
03413 int id, len, parent, x;
03414 QChar *tmp_qchar = 0;
03415 int tmp_qchar_len = 0;
03416 const int mFolderCount = mFolder->count();
03417 QString key;
03418
03419 CREATE_TIMER(parse);
03420 START_TIMER(parse);
03421 for(x = 0; !feof(sortStream) && !error; x++) {
03422 off_t offset = ftell(sortStream);
03423 KMFolder *folder;
03424
03425 if(!fread(&serNum, sizeof(serNum), 1, sortStream) ||
03426 !fread(&parentSerNum, sizeof(parentSerNum), 1, sortStream) ||
03427 !fread(&len, sizeof(len), 1, sortStream)) {
03428 break;
03429 }
03430 if ((len < 0) || (len > KMAIL_MAX_KEY_LEN)) {
03431 kdDebug(5006) << "Whoa.2! len " << len << " " << __FILE__ << ":" << __LINE__ << endl;
03432 error = true;
03433 continue;
03434 }
03435 if(len) {
03436 if(len > tmp_qchar_len) {
03437 tmp_qchar = (QChar *)realloc(tmp_qchar, len);
03438 tmp_qchar_len = len;
03439 }
03440 if(!fread(tmp_qchar, len, 1, sortStream))
03441 break;
03442 key = QString(tmp_qchar, len / 2);
03443 } else {
03444 key = QString("");
03445 }
03446
03447 kmkernel->msgDict()->getLocation(serNum, &folder, &id);
03448 if (folder != mFolder) {
03449 ++deleted_count;
03450 continue;
03451 }
03452 if (parentSerNum < KMAIL_RESERVED) {
03453 parent = (int)parentSerNum - KMAIL_RESERVED;
03454 } else {
03455 kmkernel->msgDict()->getLocation(parentSerNum - KMAIL_RESERVED, &folder, &parent);
03456 if (folder != mFolder)
03457 parent = -1;
03458 }
03459 if ((id < 0) || (id >= mFolderCount) ||
03460 (parent < -2) || (parent >= mFolderCount)) {
03461 kdDebug(5006) << "Whoa.1! " << __FILE__ << ":" << __LINE__ << endl;
03462 error = true;
03463 continue;
03464 }
03465
03466 if ((item=sortCache[id])) {
03467 if (item->id() != -1) {
03468 kdDebug(5006) << "Whoa.3! " << __FILE__ << ":" << __LINE__ << endl;
03469 error = true;
03470 continue;
03471 }
03472 item->setKey(key);
03473 item->setId(id);
03474 item->setOffset(offset);
03475 } else {
03476 item = sortCache[id] = new KMSortCacheItem(id, key, offset);
03477 }
03478 if (threaded && parent != -2) {
03479 if(parent == -1) {
03480 unparented.append(item);
03481 root.addUnsortedChild(item);
03482 } else {
03483 if( ! sortCache[parent] )
03484 sortCache[parent] = new KMSortCacheItem;
03485 sortCache[parent]->addUnsortedChild(item);
03486 }
03487 } else {
03488 if(x < sorted_count )
03489 root.addSortedChild(item);
03490 else {
03491 root.addUnsortedChild(item);
03492 }
03493 }
03494 }
03495 if (error || (x != sorted_count + discovered_count)) {
03496 kdDebug(5006) << endl << "Whoa: x " << x << ", sorted_count " << sorted_count << ", discovered_count " << discovered_count << ", count " << mFolder->count() << endl << endl;
03497 fclose(sortStream);
03498 sortStream = 0;
03499 }
03500
03501 if(tmp_qchar)
03502 free(tmp_qchar);
03503 END_TIMER(parse);
03504 SHOW_TIMER(parse);
03505 }
03506 else {
03507 fclose(sortStream);
03508 sortStream = 0;
03509 }
03510 } else {
03511 fclose(sortStream);
03512 sortStream = 0;
03513 }
03514 }
03515
03516 if (!sortStream) {
03517 mSortInfo.dirty = true;
03518 mSortInfo.column = column = mSortCol;
03519 mSortInfo.ascending = ascending = !mSortDescending;
03520 threaded = (isThreaded());
03521 sorted_count = discovered_count = appended = 0;
03522 KListView::setSorting( mSortCol, !mSortDescending );
03523 }
03524
03525 if((sorted_count + discovered_count - deleted_count) < mFolder->count()) {
03526 CREATE_TIMER(holes);
03527 START_TIMER(holes);
03528 KMMsgBase *msg = 0;
03529 for(int x = 0; x < mFolder->count(); x++) {
03530 if((!sortCache[x] || (sortCache[x]->id() < 0)) && (msg=mFolder->getMsgBase(x))) {
03531 int sortOrder = column;
03532 if (mPaintInfo.orderOfArrival)
03533 sortOrder |= (1 << 6);
03534 if (mPaintInfo.status)
03535 sortOrder |= (1 << 5);
03536 sortCache[x] = new KMSortCacheItem(
03537 x, KMHeaderItem::generate_key( this, msg, &mPaintInfo, sortOrder ));
03538 if(threaded)
03539 unparented.append(sortCache[x]);
03540 else
03541 root.addUnsortedChild(sortCache[x]);
03542 if(sortStream)
03543 sortCache[x]->updateSortFile(sortStream, mFolder, true, true);
03544 discovered_count++;
03545 appended = 1;
03546 }
03547 }
03548 END_TIMER(holes);
03549 SHOW_TIMER(holes);
03550 }
03551
03552
03553
03554 if (threaded) buildThreadingTree( sortCache );
03555 QPtrList<KMSortCacheItem> toBeSubjThreaded;
03556
03557 if (threaded && !unparented.isEmpty()) {
03558 CREATE_TIMER(reparent);
03559 START_TIMER(reparent);
03560
03561 for(QPtrListIterator<KMSortCacheItem> it(unparented); it.current(); ++it) {
03562 KMSortCacheItem *item = (*it);
03563 KMSortCacheItem *parent = findParent( item );
03564
03565 if ( parent && (parent != (*it)) ) {
03566 parent->addUnsortedChild((*it));
03567 if(sortStream)
03568 (*it)->updateSortFile(sortStream, mFolder);
03569 } else {
03570
03571
03572 if (mSubjThreading)
03573 toBeSubjThreaded.append((*it));
03574 else
03575 root.addUnsortedChild((*it));
03576 }
03577 }
03578
03579 if (mSubjThreading) {
03580 buildSubjectThreadingTree( sortCache );
03581 for(QPtrListIterator<KMSortCacheItem> it(toBeSubjThreaded); it.current(); ++it) {
03582 KMSortCacheItem *item = (*it);
03583 KMSortCacheItem *parent = findParentBySubject( item );
03584
03585 if ( parent ) {
03586 parent->addUnsortedChild((*it));
03587 if(sortStream)
03588 (*it)->updateSortFile(sortStream, mFolder);
03589 } else {
03590
03591 root.addUnsortedChild((*it));
03592 }
03593 }
03594 }
03595 END_TIMER(reparent);
03596 SHOW_TIMER(reparent);
03597 }
03598
03599 CREATE_TIMER(header_creation);
03600 START_TIMER(header_creation);
03601 KMHeaderItem *khi;
03602 KMSortCacheItem *i, *new_kci;
03603 QPtrQueue<KMSortCacheItem> s;
03604 s.enqueue(&root);
03605 compare_toplevel = true;
03606 do {
03607 i = s.dequeue();
03608 const QPtrList<KMSortCacheItem> *sorted = i->sortedChildren();
03609 int unsorted_count, unsorted_off=0;
03610 KMSortCacheItem **unsorted = i->unsortedChildren(unsorted_count);
03611 if(unsorted)
03612 qsort(unsorted, unsorted_count, sizeof(KMSortCacheItem *),
03613 compare_KMSortCacheItem);
03614
03615
03616
03617
03618
03619 for(QPtrListIterator<KMSortCacheItem> it(*sorted);
03620 (unsorted && unsorted_off < unsorted_count) || it.current(); ) {
03621
03622
03623
03624
03625
03626 if( it.current() &&
03627 ( !unsorted || unsorted_off >= unsorted_count
03628 ||
03629 ( ( !ascending || (ascending && !compare_toplevel) )
03630 && (*it)->key() < unsorted[unsorted_off]->key() )
03631 ||
03632 ( ascending && (*it)->key() >= unsorted[unsorted_off]->key() )
03633 )
03634 )
03635 {
03636 new_kci = (*it);
03637 ++it;
03638 } else {
03639
03640 new_kci = unsorted[unsorted_off++];
03641 }
03642 if(new_kci->item() || new_kci->parent() != i)
03643 continue;
03644
03645 if(threaded && i->item()) {
03646
03647
03648 if (mFolder->getMsgBase(i->id())->isWatched())
03649 mFolder->getMsgBase(new_kci->id())->setStatus(KMMsgStatusWatched);
03650 if (mFolder->getMsgBase(i->id())->isIgnored()) {
03651 mFolder->getMsgBase(new_kci->id())->setStatus(KMMsgStatusIgnored);
03652 mFolder->setStatus(new_kci->id(), KMMsgStatusRead);
03653 }
03654 khi = new KMHeaderItem(i->item(), new_kci->id(), new_kci->key());
03655 } else {
03656 khi = new KMHeaderItem(this, new_kci->id(), new_kci->key());
03657 }
03658 new_kci->setItem(mItems[new_kci->id()] = khi);
03659 if(new_kci->hasChildren())
03660 s.enqueue(new_kci);
03661
03662
03663 if ( mFolder->getMsgBase(new_kci->id())->isNew() ||
03664 ( jumpToUnread &&
03665 mFolder->getMsgBase(new_kci->id())->isUnread() ) ) {
03666 unread_exists = true;
03667 }
03668 }
03669
03670
03671
03672 if (mSortCol == paintInfo()->dateCol)
03673 compare_toplevel = false;
03674 } while(!s.isEmpty());
03675
03676 for(int x = 0; x < mFolder->count(); x++) {
03677 if (!sortCache[x]) {
03678 continue;
03679 }
03680
03681 if (!sortCache[x]->item()) {
03682 kdDebug(5006) << "KMHeaders::readSortOrder - msg could not be threaded. "
03683 << endl << "Please talk to your threading counselor asap. " << endl;
03684 khi = new KMHeaderItem(this, sortCache[x]->id(), sortCache[x]->key());
03685 sortCache[x]->setItem(mItems[sortCache[x]->id()] = khi);
03686 }
03687
03688
03689
03690 if (threaded && sortCache[x]->isImperfectlyThreaded()) {
03691 mImperfectlyThreadedList.append(sortCache[x]->item());
03692 }
03693
03694
03695 sortCache[x]->item()->setSortCacheItem(sortCache[x]);
03696 }
03697
03698 if (getNestingPolicy()<2)
03699 for (KMHeaderItem *khi=static_cast<KMHeaderItem*>(firstChild()); khi!=0;khi=static_cast<KMHeaderItem*>(khi->nextSibling()))
03700 khi->setOpen(true);
03701
03702 END_TIMER(header_creation);
03703 SHOW_TIMER(header_creation);
03704
03705 if(sortStream) {
03706
03707 if( discovered_count * discovered_count > sorted_count - deleted_count ) {
03708 mSortInfo.dirty = true;
03709 } else {
03710
03711 appended = 0;
03712 fseek(sortStream, KMAIL_MAGIC_HEADER_OFFSET + 16, SEEK_SET);
03713 fwrite(&appended, sizeof(appended), 1, sortStream);
03714 }
03715 }
03716
03717
03718 CREATE_TIMER(selection);
03719 START_TIMER(selection);
03720 if(set_selection) {
03721 int first_unread = -1;
03722 if (unread_exists) {
03723 KMHeaderItem *item = static_cast<KMHeaderItem*>(firstChild());
03724 while (item) {
03725 if ( mFolder->getMsgBase( item->msgId() )->isNew() ||
03726 ( jumpToUnread &&
03727 mFolder->getMsgBase( item->msgId() )->isUnread() ) ) {
03728 first_unread = item->msgId();
03729 break;
03730 }
03731 item = static_cast<KMHeaderItem*>(item->itemBelow());
03732 }
03733 }
03734
03735 if(first_unread == -1 ) {
03736 setTopItemByIndex(mTopItem);
03737 if ( mCurrentItem >= 0 )
03738 setCurrentItemByIndex( mCurrentItem );
03739 else if ( mCurrentItemSerNum > 0 )
03740 setCurrentItemBySerialNum( mCurrentItemSerNum );
03741 else
03742 setCurrentItemByIndex( 0 );
03743 } else {
03744 setCurrentItemByIndex(first_unread);
03745 makeHeaderVisible();
03746 center( contentsX(), itemPos(mItems[first_unread]), 0, 9.0 );
03747 }
03748 } else {
03749
03750 if (mCurrentItem <= 0) {
03751 setTopItemByIndex(mTopItem);
03752 setCurrentItemByIndex(0);
03753 }
03754 }
03755 END_TIMER(selection);
03756 SHOW_TIMER(selection);
03757 if (error || (sortStream && ferror(sortStream))) {
03758 if ( sortStream )
03759 fclose(sortStream);
03760 unlink(QFile::encodeName(sortFile));
03761 kdWarning(5006) << "Error: Failure modifying " << sortFile << " (No space left on device?)" << endl;
03762 kdWarning(5006) << __FILE__ << ":" << __LINE__ << endl;
03763
03764 }
03765 if(sortStream)
03766 fclose(sortStream);
03767
03768 return true;
03769 }
03770
03771
03772 void KMHeaders::setCurrentItemBySerialNum( unsigned long serialNum )
03773 {
03774
03775
03776
03777 for (int i = 0; i < (int)mItems.size() - 1; ++i) {
03778 KMMsgBase *mMsgBase = mFolder->getMsgBase( i );
03779 if ( mMsgBase->getMsgSerNum() == serialNum ) {
03780 bool unchanged = (currentItem() == mItems[i]);
03781 setCurrentItem( mItems[i] );
03782 setSelected( mItems[i], true );
03783 setSelectionAnchor( currentItem() );
03784 if ( unchanged )
03785 highlightMessage( currentItem(), false );
03786 ensureCurrentItemVisible();
03787 return;
03788 }
03789 }
03790
03791 kdDebug(5006) << "KMHeaders::setCurrentItem item with serial number " << serialNum << " NOT FOUND" << endl;
03792 }
03793
03794 #include "kmheaders.moc"