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