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