kmail

kmheaders.cpp

00001 // -*- mode: C++; c-file-style: "gnu" -*-
00002 // kmheaders.cpp
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   //qInitImageIO();
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; // 2 == date
00120   mSortDescending = false;
00121   mSortInfo.ascending = false;
00122   mReaderWindowActive = false;
00123   mRoot = new SortCacheItem;
00124   mRoot->setId(-666); //mark of the root!
00125   setStyleDependantFrameWidth();
00126   // popup-menu
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   // only the non-optional columns shall be resizeable
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     // if we currently only show one of either sender/receiver column
00243     // modify the popup text in the way, that it displays the text of the other of the two
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: ; // only used by KNode
00365     // don't use default, so that the compiler tells us you forgot to code here for a new column
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   // if we change the visibility of the receiver column,
00390   // the sender column has to show either the sender or the receiver
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 // Support for backing pixmap
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   // Custom/System colors
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   // Backing pixmap support
00474   { // area for config group "Pixmaps"
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   { // area for config group "General"
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   // Custom/System fonts
00532   { // area for config group "General"
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   // Behavior
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   // TDEListView::restoreLayout() will call setSorting(), which is reimplemented by us.
00563   // We don't want to change the sort order, so we set a flag here that is checked in
00564   // setSorting().
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 /* inited to  date column */);
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   { //area for config group "Geometry"
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(); // do not change the selection
00694     setCurrentMsg(id);
00695     setTopItemByIndex(top);
00696   } else {
00697     if (mFolder) {
00698     // WABA: Make sure that no KMReaderWin is still using a msg
00699     // from this folder, since it's msg's are about to be deleted.
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       // System folders remain open but we also should write the index from
00727       // time to time
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       // Not very nice, but if we go from nested to non-nested
00761       // in the folderConfig below then we need to do this otherwise
00762       // updateMessageList would do something unspeakable
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) { // Folder cleared
00820     mItems.resize(0);
00821     clear();
00822     return;
00823   }
00824   if (!isUpdatesEnabled()) return;
00825 
00826   // Remember selected messages, current message and some scrollbar data, as we have to restore it
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     // get the msgIdMD5 to compare it later
00842     KMMsgBase *mb = mFolder->getMsgBase(hi->msgId());
00843     if (mb)
00844       msgIdMD5 = mb->msgIdMD5();
00845   }
00846 //  if (!isUpdatesEnabled()) return;
00847   // prevent IMAP messages from scrolling to top
00848   disconnect(this,TQT_SIGNAL(currentChanged(TQListViewItem*)),
00849              this,TQT_SLOT(highlightMessage(TQListViewItem*)));
00850 
00851   updateMessageList(); // do not change the selection
00852 
00853   // Restore scrollbar state and selected and current messages
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   // if the current message has changed then emit
00874   // the selected signal to force an update
00875 
00876   // Normally the serial number of the message would be
00877   // used to do this, but because we don't yet have
00878   // guaranteed serial numbers for IMAP messages fall back
00879   // to using the MD5 checksum of the msgId.
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 ) ); // otherwise using count() is wrong
00905 
00906   /* Create a new SortCacheItem to be used for threading. */
00907   SortCacheItem *sci = new SortCacheItem;
00908   sci->setId(id);
00909   if (isThreaded()) {
00910     // make sure the id and subject dicts grow, if necessary
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         // The parent we found could be by subject, in which case it is
00928         // possible, that it would be preferrable to thread it below us,
00929         // not the other way around. Check that. This is not only
00930         // cosmetic, as getting this wrong leads to circular threading.
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     // o/` ... my buddy and me .. o/`
00947     hi->setSortCacheItem(sci);
00948     sci->setItem(hi);
00949 
00950     // Update and resize the id trees.
00951     mItems.resize( mFolder->count() );
00952     mItems[id] = hi;
00953 
00954     if ( !msgId.isEmpty() )
00955       mSortCacheItems.replace(msgId, sci);
00956     /* Add to the list of potential parents for subject threading. But only if
00957      * we are top level. */
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         // insertion sort by date. See buildThreadTrees for details.
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     // The message we just added might be a better parent for one of the as of
00981     // yet imperfectly threaded messages. Let's find out.
00982 
00983     /* In case the current item is taken during reparenting, prevent qlistview
00984      * from selecting some unrelated item as a result of take() emitting
00985      * currentChanged. */
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         // Check, whether our message is the replyToId or replyToAuxId of
00996         // this one. If so, thread it below our message, unless it is already
00997         // correctly threaded by replyToId.
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               // Thread below us by aux id, but keep on the list of
01013               // imperfectly threaded messages.
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           // The item was imperfectly thread before, now it's parent
01033           // is there. Update the .sorted file accordingly.
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     // Add ourselves only now, to avoid circularity above.
01044     if (hi && hi->sortCacheItem()->isImperfectlyThreaded())
01045       mImperfectlyThreadedList.append(hi);
01046   } else {
01047     // non-threaded case
01048     hi = new HeaderItem( this, id );
01049     mItems.resize( mFolder->count() );
01050     mItems[id] = hi;
01051     // o/` ... my buddy and me .. o/`
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); //inserted into sorted list
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   /* restore signal */
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    * qlistview has its own ideas about what to select as the next
01090    * item once this one is removed. Sine we have already selected
01091    * something in prepare/finalizeMove that's counter productive
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     // Remove the message from the list of potential parents for threading by
01114     // subject.
01115     if ( mSubjThreading && removedItem->sortCacheItem()->subjectThreadingList() )
01116       removedItem->sortCacheItem()->subjectThreadingList()->removeRef( removedItem->sortCacheItem() );
01117 
01118     // Reparent children of item.
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       // Just keep the item at top level, if it will be deleted anyhow
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   // Make sure our data structures are cleared.
01175   if (!mFolder->count())
01176       folderCleared();
01177 
01178   mImperfectlyThreadedList.removeRef( removedItem );
01179 #ifdef DEBUG
01180   // This should never happen, in this case the folders are inconsistent.
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   // we might have rethreaded it, in which case its current state will be lost
01188   if ( curItem ) {
01189     if ( curItem != removedItem ) {
01190       setCurrentItem( curItem );
01191       setSelectionAnchor( currentItem() );
01192     } else {
01193       // We've removed the current item, which means it was removed from
01194       // something other than a user move or copy, which would have selected
01195       // the next logical mail. This can happen when the mail is deleted by
01196       // a filter, or some other behind the scenes action. Select something
01197       // sensible, then, and make sure the reader window is cleared.
01198       emit maybeDeleting();
01199       int contentX, contentY;
01200       HeaderItem *nextItem = prepareMove( &contentX, &contentY );
01201       finalizeMove( nextItem, contentX, contentY );
01202     }
01203   }
01204   /* restore signal */
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   //  kdDebug() << k_funcinfo << endl;
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   // starting with the current item...
01240   TQListViewItem *curItem = currentItem();
01241   if (!curItem) return TQPtrList<TQListViewItem>();
01242 
01243   // ...find the top-level item:
01244   TQListViewItem *topOfThread = curItem;
01245   while ( topOfThread->parent() )
01246     topOfThread = topOfThread->parent();
01247 
01248   // collect the items in this thread:
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     // for each selected item...
01265     for (TQListViewItem *item = firstChild(); item; item = item->itemBelow())
01266       if (item->isSelected() ) {
01267         // ...find the top-level item:
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     // for each thread found...
01277     for ( TQPtrListIterator<TQListViewItem> it( topOfThreads ) ;
01278           it.current() ; ++ it ) {
01279         TQListViewItem *top = *it;
01280 
01281         // collect the items in this thread:
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; // messageRetrieve(0) is always possible
01309   msg->setTransferInProgress(false);
01310   int filterResult = kmkernel->filterMgr()->process(msg,KMFilterMgr::Explicit);
01311   if (filterResult == 2) {
01312     // something went horribly wrong (out of space?)
01313     kmkernel->emergencyExit( i18n("Unable to process messages: " ) + TQString::fromLocal8Bit(strerror(errno)));
01314     return 2;
01315   }
01316   if (msg->parent()) { // unGet this msg
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   // find top-level parent of currentItem().
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 ) // collapse can hide the current item:
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 ) { // collapse can hide the current item:
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   // set the width of the frame to a reasonable value for the current GUI style
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" ); // no need for "0 unread" to be added here
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     // uses action scheduler
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     //prevent issues with stale message pointers by using serial numbers instead
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   //make sure we have an associated folder (root of folder tree does not).
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   //  triggerUpdate();
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; // Catch the noop case
01590   if ( mFolder->isReadOnly() ) return;
01591 
01592   KMMessageList msgList = *selectedMsgs();
01593   if ( msgList.isEmpty() ) return;
01594   if ( !destFolder && askForConfirmation &&    // messages shall be deleted
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;  // user canceled the action
01603 
01604   // remember the message to select afterwards
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     // make sure the current item is shown
01622     makeHeaderVisible();
01623     BroadcastStatus::instance()->setStatusMsg(
01624        deleted ? i18n("Messages deleted successfully.") : i18n("Messages moved successfully") );
01625   } else {
01626     /* The move failed or the user canceled it; reset the state of all
01627      * messages involved and repaint.
01628      *
01629      * Note: This potentially resets too many items if there is more than one
01630      *       move going on. Oh well, I suppose no animals will be harmed.
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   // If the item is the parent of a closed thread recursively select
01717   // children .
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   // fugly, but I see no way around it
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() ) { // we are already working on this one
01763         if (toBeDeleted) {
01764           // make sure the item is not uselessly rethreaded and not selectable
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       /* test to see if we need to unselect messages on back track */
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       /* test to see if we need to unselect messages on back track */
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   /* itemAbove() is _slow_ */
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   // We have found an unread item, but it is not necessary the
01997   // first unread item.
01998   //
01999   // Find the ancestor of the unread item closest to the
02000   // root and recursively sort all of that ancestors children.
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   // A kludge to try to keep the number of unread messages in sync
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); // from top
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() ); // from bottom
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   // This causes Kolab issue 1569 (encrypted mails sometimes not dragable)
02090   // This was introduced in r73594 to fix interference between dnd and
02091   // pinentry, which is no longer reproducable now. However, since the
02092   // original problem was probably a race and might reappear, let's keep
02093   // this workaround in for now and just disable it.
02094 //   mMousePressed = false;
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   // shouldnt happen but will crash if it does
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     // only reset exactly during minute switch
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 //  if (kmkernel->folderIsDraftOrOutbox(mFolder))
02181 //    setOpen(lvi, !lvi->isOpen());
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); // will contain deleted pointers
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 // KMail Header list selection/navigation description
02205 //
02206 // If the selection state changes the reader window is updated to show the
02207 // current item.
02208 //
02209 // (The selection state of a message or messages can be changed by pressing
02210 //  space, or normal/shift/cntrl clicking).
02211 //
02212 // The following keyboard events are supported when the messages headers list
02213 // has focus, Ctrl+Key_Down, Ctrl+Key_Up, Ctrl+Key_Home, Ctrl+Key_End,
02214 // Ctrl+Key_Next, Ctrl+Key_Prior, these events change the current item but do
02215 // not change the selection state.
02216 //
02217 // Exception: When shift selecting either with mouse or key press the reader
02218 // window is updated regardless of whether of not the selection has changed.
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     // If no current item, make some first item current when a key is pressed
02229     if (!cur) {
02230       setCurrentItem( firstChild() );
02231       setSelectionAnchor( currentItem() );
02232       return;
02233     }
02234 
02235     // Handle space key press
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 // Handle RMB press, show pop up menu
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         // Check if our item is the parent of a closed thread and if so, if the root
02292         // decoration of the item was clicked (i.e. the +/- sign) which would expand
02293         // the thread. In that case, deselect all children, so opening the thread
02294         // doesn't cause a flicker.
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   // let tdelistview do it's thing, expanding/collapsing, selection/deselection
02305   TDEListView::contentsMousePressEvent(e);
02306   /* TQListView's shift-select selects also invisible items. Until that is
02307      fixed, we have to deselect hidden items here manually, so the quick
02308      search doesn't mess things up. */
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       // select the thread's children after closing if the parent is selected
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     /* Explicitely set selection state. This is necessary because we want to
02327      * also select all children of closed threads when the parent is selected. */
02328 
02329     // unless ctrl mask, set selected if it isn't already
02330     if ( !( e->state() & ControlButton ) && !wasSelected )
02331       setSelected( lvi, true );
02332     // if ctrl mask, toggle selection
02333     if ( e->state() & ControlButton )
02334       setSelected( lvi, !wasSelected );
02335 
02336     if ((e->button() == Qt::LeftButton) )
02337       mMousePressed = true;
02338   }
02339 
02340   // check if we are on a status column and toggle it
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           // FIXME: msg can be null here which crashes.  I think it's a race
02390           //        because it's very hard to reproduce. (GS)
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       // Set pixmap
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       // Calculate hotspot (as in Konqueror)
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; // safe bet
02428   mOwner->updateMessageActions();
02429 
02430   // check if the user clicked into a status column and only show the respective menues
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     // show most used actions
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 ); // Mark Message menu
02483   if ( mOwner->threadStatusMenu()->isEnabled() ) {
02484     mOwner->threadStatusMenu()->plug( menu ); // Mark Thread menu
02485   }
02486 
02487   if ( !out_folder && !tem_folder ) {
02488     menu->insertSeparator();
02489     mOwner->filterMenu()->plug( menu ); // Create Filter 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   // carsten: really needed?
02624 //    if (column != mSortCol)
02625 //      setColumnText( mSortCol, TQIconSet( TQPixmap()), columnText( mSortCol ));
02626     if(mSortInfo.dirty || column != mSortInfo.column || ascending != mSortInfo.ascending) { //dirtied
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   // Make sure the config and .sorted file are updated, otherwise stale info
02654   // is read on new imap mail. ( folder->folderComplete() -> readSortOrder() ).
02655   if ( mFolder ) {
02656     writeFolderConfig();
02657     writeSortOrder();
02658   }
02659 }
02660 
02661 //Flatten the list and write it to disk
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     //update the discovered change count
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(); //autoDelete is true
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       // Folder is empty now, remove the sort file.
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     //magic header information
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; //no parent, top level
02772       if (threaded) {
02773         kmb = mFolder->getMsgBase( i->msgId() );
02774         assert(kmb); // I have seen 0L come out of this, called from
02775                    // KMHeaders::setFolder(0xgoodpointer, false);
02776                    // I see this crash too. after rebuilding a broken index on a dimap folder. always
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         // We now have either found a parent, or set it to -1, which means that
02786         // it will be reevaluated when a message is added, for example. If there
02787         // is no replyToId and no replyToAuxId and the message is not prefixed,
02788         // this message is top level, and will always be, so no need to
02789         // reevaluate it.
02790         if (replymd5.isEmpty()
02791             && replyToAuxId.isEmpty()
02792             && !kmb->subjectIsPrefixed() )
02793           parent_id = -2;
02794         // FIXME also mark messages with -1 as -2 a certain amount of time after
02795         // their arrival, since it becomes very unlikely that a new parent for
02796         // them will show up. (Ingo suggests a month.) -till
02797       }
02798       internalWriteItem(sortStream, mFolder, i->msgId(), parent_id,
02799                         i->key(mSortCol, !mSortDescending), false);
02800       //double check for magic headers
02801       sorted_count++;
02802     }
02803 
02804     //magic header twice, case they've changed
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; //no parent, top level
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     //update the appended flag FIXME obsolete?
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 // Debugging helpers
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     // build a dict of all message id's
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();  // autoDelete is true
02961     mSubjectLists.resize( mFolder->count() * 2 );
02962 
02963     for(int x = 0; x < mFolder->count(); x++) {
02964         // Only a lot items that are now toplevel
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         /* For each subject, keep a list of items with that subject
02976          * (stripped of prefixes) sorted by date. */
02977         if (!mSubjectLists.find(subjMD5))
02978             mSubjectLists.insert(subjMD5, new TQPtrList<SortCacheItem>());
02979         /* Insertion sort by date. These lists are expected to be very small.
02980          * Also, since the messages are roughly ordered by date in the store,
02981          * they should mostly be prepended at the very start, so insertion is
02982          * cheap. */
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     /* First, try if the message our Reply-To header points to
03005      * is available to thread below. */
03006     if(!replyToIdMD5.isEmpty()) {
03007         parent = mSortCacheItems[replyToIdMD5];
03008         if (parent)
03009             item->setImperfectlyThreaded(false);
03010     }
03011     if (!parent) {
03012         // If we dont have a replyToId, or if we have one and the
03013         // corresponding message is not in this folder, as happens
03014         // if you keep your outgoing messages in an OUTBOX, for
03015         // example, try the list of references, because the second
03016         // to last will likely be in this folder. replyToAuxIdMD5
03017         // contains the second to last one.
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     // Let's try by subject, but only if the  subject is prefixed.
03033     // This is necessary to make for example cvs commit mailing lists
03034     // work as expected without having to turn threading off alltogether.
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         /* Iterate over the list of potential parents with the same
03042          * subject, and take the closest one by date. */
03043         for (TQPtrListIterator<SortCacheItem> it2(*mSubjectLists[subjMD5]);
03044                 it2.current(); ++it2) {
03045             KMMsgBase *mb = mFolder->getMsgBase((*it2)->id());
03046             if ( !mb ) return parent;
03047             // make sure it's not ourselves
03048             if ( item == (*it2) ) continue;
03049             int delta = msg->date() - mb->date();
03050             // delta == 0 is not allowed, to avoid circular threading
03051             // with duplicates.
03052             if (delta > 0 ) {
03053                 // Don't use parents more than 6 weeks older than us.
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     //all cases
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     //threaded cases
03080     TQPtrList<SortCacheItem> unparented;
03081     mImperfectlyThreadedList.clear();
03082 
03083     //cleanup
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             //Hackyness to work around qlistview problems
03111             TDEListView::setSorting(-1);
03112             header()->setSortIndicator(column, ascending);
03113             TQObject::connect(header(), TQT_SIGNAL(clicked(int)), this, TQT_SLOT(dirtySortOrder(int)));
03114             //setup mSortInfo here now, as above may change it
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                 //parse
03133                 if(!fread(&serNum, sizeof(serNum), 1, sortStream) || //short read means to end
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(""); //yuck
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)) { // sanity checking
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)) {// sanity check
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     //fill in empty holes
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     // Make sure we've placed everything in parent/child relationship. All
03262     // messages with a parent id of -1 in the sort file are reevaluated here.
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             // If we have a parent, make sure it's not ourselves
03274             if ( parent && (parent != (*it)) ) {
03275                 parent->addUnsortedChild((*it));
03276                 if(sortStream)
03277                     (*it)->updateSortFile(sortStream, mFolder);
03278             } else {
03279                 // if we will attempt subject threading, add to the list,
03280                 // otherwise to the root with them
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                     //oh well we tried, to the root with you!
03300                     mRoot->addUnsortedChild((*it));
03301                 }
03302             }
03303         }
03304         END_TIMER(reparent);
03305         SHOW_TIMER(reparent);
03306     }
03307     //create headeritems
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 *), //sort
03322                   compare_SortCacheItem);
03323 
03324         /* The sorted list now contains all sorted children of this item, while
03325          * the (aptly named) unsorted array contains all as of yet unsorted
03326          * ones. It has just been qsorted, so it is in itself sorted. These two
03327          * sorted lists are now merged into one. */
03328         for(TQPtrListIterator<SortCacheItem> it(*sorted);
03329             (unsorted && unsorted_off < unsorted_count) || it.current(); ) {
03330             /* As long as we have something in the sorted list and there is
03331                nothing unsorted left, use the item from the sorted list. Also
03332                if we are sorting descendingly and the sorted item is supposed
03333                to be sorted before the unsorted one do so. In the ascending
03334                case we invert the logic for non top level items. */
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                 /* Otherwise use the next item of the unsorted list */
03349                 new_kci = unsorted[unsorted_off++];
03350             }
03351             if(new_kci->item() || new_kci->parent() != i) //could happen if you reparent
03352                 continue;
03353 
03354             if(threaded && i->item()) {
03355                 // If the parent is watched or ignored, propagate that to it's
03356                 // children
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             // we always jump to new messages, but we only jump to
03369             // unread messages if we are told to do so
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         // If we are sorting by date and ascending the top level items are sorted
03391         // ascending and the threads themselves are sorted descending. One wants
03392         // to have new threads on top but the threads themselves top down.
03393         if (mSortCol == paintInfo()->dateCol)
03394           compare_toplevel = false;
03395     } while(!s.isEmpty());
03396 
03397     for(int x = 0; x < mFolder->count(); x++) {     //cleanup
03398         if (!sortCache[x]) { // not yet there?
03399             continue;
03400         }
03401 
03402         if (!sortCache[x]->item()) { // we missed a message, how did that happen ?
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         // Add all imperfectly threaded items to a list, so they can be
03409         // reevaluated when a new message arrives which might be a better parent.
03410         // Important for messages arriving out of order.
03411         if (threaded && sortCache[x]->isImperfectlyThreaded()) {
03412             mImperfectlyThreadedList.append(sortCache[x]->item());
03413         }
03414         // Set the reverse mapping HeaderItem -> SortCacheItem. Needed for
03415         // keeping the data structures up to date on removal, for example.
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) { //update the .sorted file now
03427         // heuristic for when it's time to rewrite the .sorted file
03428         if( discovered_count * discovered_count > sorted_count - deleted_count ) {
03429             mSortInfo.dirty = true;
03430         } else {
03431             //update the appended flag
03432             appended = 0;
03433             fseek(sortStream, KMAIL_MAGIC_HEADER_OFFSET + 16, SEEK_SET);
03434             fwrite(&appended, sizeof(appended), 1, sortStream);
03435         }
03436     }
03437 
03438     // Select a message, depending on the "When entering a folder:" setting
03439     CREATE_TIMER(selection);
03440     START_TIMER(selection);
03441     if(set_selection) {
03442 
03443         // Search for the id of the first unread/new item, should there be any
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         // No unread message to select, so either select the newest, oldest or lastest selected
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         // There is an unread item to select, so select it
03482         } else {
03483             setCurrentItemByIndex(first_unread);
03484             makeHeaderVisible();
03485             center( contentsX(), itemPos(mItems[first_unread]), 0, 9.0 );
03486         }
03487 
03488     // we are told to not change the selection
03489     } else {
03490         // only reset the selection if we have no current item
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   // Linear search == slow. Don't overuse this method.
03517   // It's currently only used for finding the current item again
03518   // after expiry deleted mails (so the index got invalidated).
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   // Not found. Maybe we should select the last item instead?
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         // the item's parent is closed, don't traverse any more of this subtree
03625         TQListViewItem * lastAncestorWithSiblings = it.current()->parent();
03626         // travel towards the root until we find an ancestor with siblings
03627         while ( ( lastAncestorWithSiblings->depth() > 0 ) && !lastAncestorWithSiblings->nextSibling() )
03628           lastAncestorWithSiblings = lastAncestorWithSiblings->parent();
03629         // move the iterator to that ancestor's next sibling
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"