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