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