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