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