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