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