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