kmail

kmheaders.cpp
1 // kmheaders.cpp
2 
3 #include <config.h>
4 
5 #include "kmheaders.h"
6 #include "headeritem.h"
8 
9 #include "kcursorsaver.h"
10 #include "kmcommands.h"
11 #include "kmmainwidget.h"
12 #include "kmfiltermgr.h"
13 #include "undostack.h"
14 #include "kmmsgdict.h"
15 #include "kmdebug.h"
16 #include "kmfoldertree.h"
17 #include "folderjob.h"
18 using KMail::FolderJob;
19 #include "actionscheduler.h"
20 using KMail::ActionScheduler;
21 #include "messagecopyhelper.h"
23 #include "broadcaststatus.h"
24 using KPIM::BroadcastStatus;
25 #include "progressmanager.h"
26 using KPIM::ProgressManager;
27 using KPIM::ProgressItem;
28 #include <maillistdrag.h>
29 #include "globalsettings.h"
30 using namespace KPIM;
31 #include "messageactions.h"
32 
33 #include <tdeapplication.h>
34 #include <tdeaccelmanager.h>
35 #include <tdeglobalsettings.h>
36 #include <tdemessagebox.h>
37 #include <kiconloader.h>
38 #include <tdepopupmenu.h>
39 #include <kimageio.h>
40 #include <tdeconfig.h>
41 #include <tdelocale.h>
42 #include <kdebug.h>
43 
44 #include <tqbuffer.h>
45 #include <tqeventloop.h>
46 #include <tqfile.h>
47 #include <tqheader.h>
48 #include <tqptrstack.h>
49 #include <tqptrqueue.h>
50 #include <tqpainter.h>
51 #include <tqtextcodec.h>
52 #include <tqstyle.h>
53 #include <tqlistview.h>
54 
55 #include <mimelib/enum.h>
56 #include <mimelib/field.h>
57 #include <mimelib/mimepp.h>
58 
59 #include <stdlib.h>
60 #include <errno.h>
61 
62 #include "textsource.h"
63 
64 TQPixmap* KMHeaders::pixNew = 0;
65 TQPixmap* KMHeaders::pixUns = 0;
66 TQPixmap* KMHeaders::pixDel = 0;
67 TQPixmap* KMHeaders::pixRead = 0;
68 TQPixmap* KMHeaders::pixRep = 0;
69 TQPixmap* KMHeaders::pixQueued = 0;
70 TQPixmap* KMHeaders::pixTodo = 0;
71 TQPixmap* KMHeaders::pixSent = 0;
72 TQPixmap* KMHeaders::pixFwd = 0;
73 TQPixmap* KMHeaders::pixFlag = 0;
74 TQPixmap* KMHeaders::pixWatched = 0;
75 TQPixmap* KMHeaders::pixIgnored = 0;
76 TQPixmap* KMHeaders::pixSpam = 0;
77 TQPixmap* KMHeaders::pixHam = 0;
78 TQPixmap* KMHeaders::pixFullySigned = 0;
79 TQPixmap* KMHeaders::pixPartiallySigned = 0;
80 TQPixmap* KMHeaders::pixUndefinedSigned = 0;
81 TQPixmap* KMHeaders::pixFullyEncrypted = 0;
82 TQPixmap* KMHeaders::pixPartiallyEncrypted = 0;
83 TQPixmap* KMHeaders::pixUndefinedEncrypted = 0;
84 TQPixmap* KMHeaders::pixEncryptionProblematic = 0;
85 TQPixmap* KMHeaders::pixSignatureProblematic = 0;
86 TQPixmap* KMHeaders::pixAttachment = 0;
87 TQPixmap* KMHeaders::pixInvitation = 0;
88 TQPixmap* KMHeaders::pixReadFwd = 0;
89 TQPixmap* KMHeaders::pixReadReplied = 0;
90 TQPixmap* KMHeaders::pixReadFwdReplied = 0;
91 
92 
93 //-----------------------------------------------------------------------------
94 KMHeaders::KMHeaders(KMMainWidget *aOwner, TQWidget *parent,
95  const char *name) :
96  TDEListView( parent, name ),
97  mIgnoreSortOrderChanges( false )
98 {
99  static bool pixmapsLoaded = false;
100  //qInitImageIO();
101  KImageIO::registerFormats();
102  mOwner = aOwner;
103  mFolder = 0;
104  noRepaint = false;
105  getMsgIndex = -1;
106  mTopItem = 0;
107  setSelectionMode( TQListView::Extended );
108  setAllColumnsShowFocus( true );
109  mNested = false;
110  nestingPolicy = OpenUnread;
111  mNestedOverride = false;
112  mSubjThreading = true;
113  mMousePressed = false;
114  mSortInfo.dirty = true;
115  mSortInfo.fakeSort = 0;
116  mSortInfo.removed = 0;
117  mSortInfo.column = 0;
118  mSortCol = 2; // 2 == date
119  mSortDescending = false;
120  mSortInfo.ascending = false;
121  mReaderWindowActive = false;
122  mRoot = new SortCacheItem;
123  mRoot->setId(-666); //mark of the root!
124  setStyleDependantFrameWidth();
125  // popup-menu
126  header()->setClickEnabled(true);
127  header()->installEventFilter(this);
128  mPopup = new TDEPopupMenu(this);
129  mPopup->insertTitle(i18n("View Columns"));
130  mPopup->setCheckable(true);
131  mPopup->insertItem(i18n("Status"), KPaintInfo::COL_STATUS);
132  mPopup->insertItem(i18n("Important"), KPaintInfo::COL_IMPORTANT);
133  mPopup->insertItem(i18n("Action Item"), KPaintInfo::COL_TODO);
134  mPopup->insertItem(i18n("Attachment"), KPaintInfo::COL_ATTACHMENT);
135  mPopup->insertItem(i18n("Invitation"), KPaintInfo::COL_INVITATION);
136  mPopup->insertItem(i18n("Spam/Ham"), KPaintInfo::COL_SPAM_HAM);
137  mPopup->insertItem(i18n("Watched/Ignored"), KPaintInfo::COL_WATCHED_IGNORED);
138  mPopup->insertItem(i18n("Signature"), KPaintInfo::COL_SIGNED);
139  mPopup->insertItem(i18n("Encryption"), KPaintInfo::COL_CRYPTO);
140  mPopup->insertItem(i18n("Size"), KPaintInfo::COL_SIZE);
141  mPopup->insertItem(i18n("Receiver"), KPaintInfo::COL_RECEIVER);
142 
143  connect(mPopup, TQ_SIGNAL(activated(int)), this, TQ_SLOT(slotToggleColumn(int)));
144 
145  setShowSortIndicator(true);
146  setFocusPolicy( TQWidget::WheelFocus );
147 
148  if (!pixmapsLoaded)
149  {
150  pixmapsLoaded = true;
151  pixNew = new TQPixmap( UserIcon( "kmmsgnew" ) );
152  pixUns = new TQPixmap( UserIcon( "kmmsgunseen" ) );
153  pixDel = new TQPixmap( UserIcon( "kmmsgdel" ) );
154  pixRead = new TQPixmap( UserIcon( "kmmsgread" ) );
155  pixRep = new TQPixmap( UserIcon( "kmmsgreplied" ) );
156  pixQueued = new TQPixmap( UserIcon( "kmmsgqueued" ) );
157  pixTodo = new TQPixmap( UserIcon( "kmmsgtodo" ) );
158  pixSent = new TQPixmap( UserIcon( "kmmsgsent" ) );
159  pixFwd = new TQPixmap( UserIcon( "kmmsgforwarded" ) );
160  pixFlag = new TQPixmap( UserIcon( "kmmsgflag" ) );
161  pixWatched = new TQPixmap( UserIcon( "kmmsgwatched" ) );
162  pixIgnored = new TQPixmap( UserIcon( "kmmsgignored" ) );
163  pixSpam = new TQPixmap( UserIcon( "kmmsgspam" ) );
164  pixHam = new TQPixmap( UserIcon( "kmmsgham" ) );
165  pixFullySigned = new TQPixmap( UserIcon( "kmmsgfullysigned" ) );
166  pixPartiallySigned = new TQPixmap( UserIcon( "kmmsgpartiallysigned" ) );
167  pixUndefinedSigned = new TQPixmap( UserIcon( "kmmsgundefinedsigned" ) );
168  pixFullyEncrypted = new TQPixmap( UserIcon( "kmmsgfullyencrypted" ) );
169  pixPartiallyEncrypted = new TQPixmap( UserIcon( "kmmsgpartiallyencrypted" ) );
170  pixUndefinedEncrypted = new TQPixmap( UserIcon( "kmmsgundefinedencrypted" ) );
171  pixEncryptionProblematic = new TQPixmap( UserIcon( "kmmsgencryptionproblematic" ) );
172  pixSignatureProblematic = new TQPixmap( UserIcon( "kmmsgsignatureproblematic" ) );
173  pixAttachment = new TQPixmap( UserIcon( "kmmsgattachment" ) );
174  pixInvitation = new TQPixmap( UserIcon( "kmmsginvitation" ) );
175  pixReadFwd = new TQPixmap( UserIcon( "kmmsgread_fwd" ) );
176  pixReadReplied = new TQPixmap( UserIcon( "kmmsgread_replied" ) );
177  pixReadFwdReplied = new TQPixmap( UserIcon( "kmmsgread_fwd_replied" ) );
178  }
179 
180  header()->setStretchEnabled( false );
181  header()->setResizeEnabled( false );
182 
183  mPaintInfo.subCol = addColumn( i18n("Subject"), 310 );
184  mPaintInfo.senderCol = addColumn( i18n("Sender"), 170 );
185  mPaintInfo.dateCol = addColumn( i18n("Date"), 170 );
186  mPaintInfo.sizeCol = addColumn( i18n("Size"), 0 );
187  mPaintInfo.receiverCol = addColumn( i18n("Receiver"), 0 );
188 
189  mPaintInfo.statusCol = addColumn( *pixNew , "", 0 );
190  mPaintInfo.importantCol = addColumn( *pixFlag , "", 0 );
191  mPaintInfo.todoCol = addColumn( *pixTodo , "", 0 );
192  mPaintInfo.attachmentCol = addColumn( *pixAttachment , "", 0 );
193  mPaintInfo.invitationCol = addColumn( *pixInvitation , "", 0 );
194  mPaintInfo.spamHamCol = addColumn( *pixSpam , "", 0 );
195  mPaintInfo.watchedIgnoredCol = addColumn( *pixWatched , "", 0 );
196  mPaintInfo.signedCol = addColumn( *pixFullySigned , "", 0 );
197  mPaintInfo.cryptoCol = addColumn( *pixFullyEncrypted, "", 0 );
198 
199  setResizeMode( TQListView::NoColumn );
200 
201  // only the non-optional columns shall be resizeable
202  header()->setResizeEnabled( true, mPaintInfo.subCol );
203  header()->setResizeEnabled( true, mPaintInfo.senderCol );
204  header()->setResizeEnabled( true, mPaintInfo.dateCol );
205 
206  connect( this, TQ_SIGNAL( contextMenuRequested( TQListViewItem*, const TQPoint &, int )),
207  this, TQ_SLOT( rightButtonPressed( TQListViewItem*, const TQPoint &, int )));
208  connect(this, TQ_SIGNAL(doubleClicked(TQListViewItem*)),
209  this,TQ_SLOT(selectMessage(TQListViewItem*)));
210  connect(this,TQ_SIGNAL(currentChanged(TQListViewItem*)),
211  this,TQ_SLOT(highlightMessage(TQListViewItem*)));
212  resetCurrentTime();
213 
214  mSubjectLists.setAutoDelete( true );
215 
216  mMoveMessages = false;
217  connect( this, TQ_SIGNAL(selectionChanged()), TQ_SLOT(updateActions()) );
218 }
219 
220 
221 //-----------------------------------------------------------------------------
222 KMHeaders::~KMHeaders ()
223 {
224  if (mFolder)
225  {
227  writeSortOrder();
228  mFolder->close("kmheaders");
229  }
230  writeConfig();
231  delete mRoot;
232 }
233 
234 //-----------------------------------------------------------------------------
235 bool KMHeaders::eventFilter ( TQObject *o, TQEvent *e )
236 {
237  if ( e->type() == TQEvent::MouseButtonPress &&
238  static_cast<TQMouseEvent*>(e)->button() == TQt::RightButton &&
239  o->isA("TQHeader") )
240  {
241  // if we currently only show one of either sender/receiver column
242  // modify the popup text in the way, that it displays the text of the other of the two
243  if ( mPaintInfo.showReceiver )
244  mPopup->changeItem(KPaintInfo::COL_RECEIVER, i18n("Receiver"));
245  else
246  if ( mFolder && (mFolder->whoField().lower() == "to") )
247  mPopup->changeItem(KPaintInfo::COL_RECEIVER, i18n("Sender"));
248  else
249  mPopup->changeItem(KPaintInfo::COL_RECEIVER, i18n("Receiver"));
250 
251  mPopup->popup( static_cast<TQMouseEvent*>(e)->globalPos() );
252  return true;
253  }
254  return TDEListView::eventFilter(o, e);
255 }
256 
257 //-----------------------------------------------------------------------------
258 
259 void KMHeaders::slotToggleColumn(int id, int mode)
260 {
261  bool *show = 0;
262  int *col = 0;
263  int width = 0;
264  int moveToCol = -1;
265 
266  switch ( static_cast<KPaintInfo::ColumnIds>(id) )
267  {
268  case KPaintInfo::COL_SIZE:
269  {
270  show = &mPaintInfo.showSize;
271  col = &mPaintInfo.sizeCol;
272  width = 80;
273  break;
274  }
275  case KPaintInfo::COL_ATTACHMENT:
276  {
277  show = &mPaintInfo.showAttachment;
278  col = &mPaintInfo.attachmentCol;
279  width = pixAttachment->width() + 8;
280  if ( *col == header()->mapToIndex( *col ) )
281  moveToCol = 0;
282  break;
283  }
284  case KPaintInfo::COL_INVITATION:
285  {
286  show = &mPaintInfo.showInvitation;
287  col = &mPaintInfo.invitationCol;
288  width = pixAttachment->width() + 8;
289  if ( *col == header()->mapToIndex( *col ) )
290  moveToCol = 0;
291  break;
292  }
293  case KPaintInfo::COL_IMPORTANT:
294  {
295  show = &mPaintInfo.showImportant;
296  col = &mPaintInfo.importantCol;
297  width = pixFlag->width() + 8;
298  if ( *col == header()->mapToIndex( *col ) )
299  moveToCol = 0;
300  break;
301  }
302  case KPaintInfo::COL_TODO:
303  {
304  show = &mPaintInfo.showTodo;
305  col = &mPaintInfo.todoCol;
306  width = pixTodo->width() + 8;
307  if ( *col == header()->mapToIndex( *col ) )
308  moveToCol = 0;
309  break;
310  }
311  case KPaintInfo::COL_SPAM_HAM:
312  {
313  show = &mPaintInfo.showSpamHam;
314  col = &mPaintInfo.spamHamCol;
315  width = pixSpam->width() + 8;
316  if ( *col == header()->mapToIndex( *col ) )
317  moveToCol = 0;
318  break;
319  }
320  case KPaintInfo::COL_WATCHED_IGNORED:
321  {
322  show = &mPaintInfo.showWatchedIgnored;
323  col = &mPaintInfo.watchedIgnoredCol;
324  width = pixWatched->width() + 8;
325  if ( *col == header()->mapToIndex( *col ) )
326  moveToCol = 0;
327  break;
328  }
329  case KPaintInfo::COL_STATUS:
330  {
331  show = &mPaintInfo.showStatus;
332  col = &mPaintInfo.statusCol;
333  width = pixNew->width() + 8;
334  if ( *col == header()->mapToIndex( *col ) )
335  moveToCol = 0;
336  break;
337  }
338  case KPaintInfo::COL_SIGNED:
339  {
340  show = &mPaintInfo.showSigned;
341  col = &mPaintInfo.signedCol;
342  width = pixFullySigned->width() + 8;
343  if ( *col == header()->mapToIndex( *col ) )
344  moveToCol = 0;
345  break;
346  }
347  case KPaintInfo::COL_CRYPTO:
348  {
349  show = &mPaintInfo.showCrypto;
350  col = &mPaintInfo.cryptoCol;
351  width = pixFullyEncrypted->width() + 8;
352  if ( *col == header()->mapToIndex( *col ) )
353  moveToCol = 0;
354  break;
355  }
356  case KPaintInfo::COL_RECEIVER:
357  {
358  show = &mPaintInfo.showReceiver;
359  col = &mPaintInfo.receiverCol;
360  width = 170;
361  break;
362  }
363  case KPaintInfo::COL_SCORE: ; // only used by KNode
364  // don't use default, so that the compiler tells us you forgot to code here for a new column
365  }
366 
367  assert(show);
368 
369  if (mode == -1)
370  *show = !*show;
371  else
372  *show = mode;
373 
374  mPopup->setItemChecked(id, *show);
375 
376  if (*show) {
377  header()->setResizeEnabled(true, *col);
378  setColumnWidth(*col, width);
379  if ( moveToCol >= 0 )
380  header()->moveSection( *col, moveToCol );
381  }
382  else {
383  header()->setResizeEnabled(false, *col);
384  header()->setStretchEnabled(false, *col);
385  hideColumn(*col);
386  }
387 
388  // if we change the visibility of the receiver column,
389  // the sender column has to show either the sender or the receiver
390  if ( static_cast<KPaintInfo::ColumnIds>(id) == KPaintInfo::COL_RECEIVER ) {
391  TQString colText = i18n( "Sender" );
392  if ( mFolder && (mFolder->whoField().lower() == "to") && !mPaintInfo.showReceiver)
393  colText = i18n( "Receiver" );
394  setColumnText( mPaintInfo.senderCol, colText );
395  }
396 
397  if (mode == -1)
398  writeConfig();
399 }
400 
401 //-----------------------------------------------------------------------------
402 // Support for backing pixmap
403 void KMHeaders::paintEmptyArea( TQPainter * p, const TQRect & rect )
404 {
405  if (mPaintInfo.pixmapOn)
406  p->drawTiledPixmap( rect.left(), rect.top(), rect.width(), rect.height(),
407  mPaintInfo.pixmap,
408  rect.left() + contentsX(),
409  rect.top() + contentsY() );
410  else
411  p->fillRect( rect, colorGroup().base() );
412 }
413 
414 bool KMHeaders::event(TQEvent *e)
415 {
416  bool result = TDEListView::event(e);
417  if (e->type() == TQEvent::ApplicationPaletteChange)
418  {
419  readColorConfig();
420  }
421  return result;
422 }
423 
424 
425 //-----------------------------------------------------------------------------
427 {
428  TDEConfig* config = KMKernel::config();
429  // Custom/System colors
430  TDEConfigGroupSaver saver(config, "Reader");
431  TQColor c1=TQColor(kapp->palette().active().text());
432  TQColor c2=TQColor("red");
433  TQColor c3=TQColor("blue");
434  TQColor c4=TQColor(kapp->palette().active().base());
435  TQColor c5=TQColor(0,0x7F,0);
436  TQColor c6=TQColor(0,0x98,0);
437  TQColor c7=TDEGlobalSettings::alternateBackgroundColor();
438 
439  if (!config->readBoolEntry("defaultColors",true)) {
440  mPaintInfo.colFore = config->readColorEntry("ForegroundColor",&c1);
441  mPaintInfo.colBack = config->readColorEntry("BackgroundColor",&c4);
442  TQPalette newPal = kapp->palette();
443  newPal.setColor( TQColorGroup::Base, mPaintInfo.colBack );
444  newPal.setColor( TQColorGroup::Text, mPaintInfo.colFore );
445  setPalette( newPal );
446  mPaintInfo.colNew = config->readColorEntry("NewMessage",&c2);
447  mPaintInfo.colUnread = config->readColorEntry("UnreadMessage",&c3);
448  mPaintInfo.colFlag = config->readColorEntry("FlagMessage",&c5);
449  mPaintInfo.colTodo = config->readColorEntry("TodoMessage",&c6);
450  c7 = config->readColorEntry("AltBackgroundColor",&c7);
451  }
452  else {
453  mPaintInfo.colFore = c1;
454  mPaintInfo.colBack = c4;
455  TQPalette newPal = kapp->palette();
456  newPal.setColor( TQColorGroup::Base, c4 );
457  newPal.setColor( TQColorGroup::Text, c1 );
458  setPalette( newPal );
459  mPaintInfo.colNew = c2;
460  mPaintInfo.colUnread = c3;
461  mPaintInfo.colFlag = c5;
462  mPaintInfo.colTodo = c6;
463  }
464  setAlternateBackground(c7);
465 }
466 
467 //-----------------------------------------------------------------------------
469 {
470  TDEConfig* config = KMKernel::config();
471 
472  // Backing pixmap support
473  { // area for config group "Pixmaps"
474  TDEConfigGroupSaver saver(config, "Pixmaps");
475  TQString pixmapFile = config->readEntry("Headers");
476  mPaintInfo.pixmapOn = false;
477  if (!pixmapFile.isEmpty()) {
478  mPaintInfo.pixmapOn = true;
479  mPaintInfo.pixmap = TQPixmap( pixmapFile );
480  }
481  }
482 
483  { // area for config group "General"
484  TDEConfigGroupSaver saver(config, "General");
485  bool show = config->readBoolEntry("showMessageSize");
486  slotToggleColumn(KPaintInfo::COL_SIZE, show);
487 
488  show = config->readBoolEntry("showAttachmentColumn");
489  slotToggleColumn(KPaintInfo::COL_ATTACHMENT, show);
490 
491  show = config->readBoolEntry("showInvitationColumn");
492  slotToggleColumn(KPaintInfo::COL_INVITATION, show);
493 
494  show = config->readBoolEntry("showImportantColumn");
495  slotToggleColumn(KPaintInfo::COL_IMPORTANT, show);
496 
497  show = config->readBoolEntry("showTodoColumn");
498  slotToggleColumn(KPaintInfo::COL_TODO, show);
499 
500  show = config->readBoolEntry("showSpamHamColumn");
501  slotToggleColumn(KPaintInfo::COL_SPAM_HAM, show);
502 
503  show = config->readBoolEntry("showWatchedIgnoredColumn");
504  slotToggleColumn(KPaintInfo::COL_WATCHED_IGNORED, show);
505 
506  show = config->readBoolEntry("showStatusColumn");
507  slotToggleColumn(KPaintInfo::COL_STATUS, show);
508 
509  show = config->readBoolEntry("showSignedColumn");
510  slotToggleColumn(KPaintInfo::COL_SIGNED, show);
511 
512  show = config->readBoolEntry("showCryptoColumn");
513  slotToggleColumn(KPaintInfo::COL_CRYPTO, show);
514 
515  show = config->readBoolEntry("showReceiverColumn");
516  slotToggleColumn(KPaintInfo::COL_RECEIVER, show);
517 
518  mPaintInfo.showCryptoIcons = config->readBoolEntry( "showCryptoIcons", false );
519  mPaintInfo.showAttachmentIcon = config->readBoolEntry( "showAttachmentIcon", true );
520  mPaintInfo.showInvitationIcon = config->readBoolEntry( "showInvitationIcon", false );
521 
522  KMime::DateFormatter::FormatType t =
523  (KMime::DateFormatter::FormatType) config->readNumEntry("dateFormat", KMime::DateFormatter::Fancy ) ;
524  mDate.setCustomFormat( config->readEntry("customDateFormat") );
525  mDate.setFormat( t );
526  }
527 
528  readColorConfig();
529 
530  // Custom/System fonts
531  { // area for config group "General"
532  TDEConfigGroupSaver saver(config, "Fonts");
533  if (!(config->readBoolEntry("defaultFonts",true)))
534  {
535  TQFont listFont( TDEGlobalSettings::generalFont() );
536  listFont = config->readFontEntry( "list-font", &listFont );
537  setFont( listFont );
538  mNewFont = config->readFontEntry( "list-new-font", &listFont );
539  mUnreadFont = config->readFontEntry( "list-unread-font", &listFont );
540  mImportantFont = config->readFontEntry( "list-important-font", &listFont );
541  mTodoFont = config->readFontEntry( "list-todo-font", &listFont );
542  mDateFont = TDEGlobalSettings::fixedFont();
543  mDateFont = config->readFontEntry( "list-date-font", &mDateFont );
544  } else {
545  mNewFont= mUnreadFont = mImportantFont = mDateFont = mTodoFont =
546  TDEGlobalSettings::generalFont();
547  setFont( mDateFont );
548  }
549  }
550 
551  // Behavior
552  {
553  TDEConfigGroupSaver saver(config, "Geometry");
554  mReaderWindowActive = config->readEntry( "readerWindowMode", "below" ) != "hide";
555  }
556 }
557 
558 //-----------------------------------------------------------------------------
559 void KMHeaders::restoreColumnLayout( TDEConfig *config, const TQString &group )
560 {
561  // TDEListView::restoreLayout() will call setSorting(), which is reimplemented by us.
562  // We don't want to change the sort order, so we set a flag here that is checked in
563  // setSorting().
564  mIgnoreSortOrderChanges = true;
565  restoreLayout( config, group );
566  mIgnoreSortOrderChanges = false;
567 }
568 
569 //-----------------------------------------------------------------------------
571 {
572  int top = topItemIndex();
573  int id = currentItemIndex();
574  noRepaint = true;
575  clear();
576  TQString colText = i18n( "Sender" );
577  if ( mFolder && (mFolder->whoField().lower() == "to") && !mPaintInfo.showReceiver)
578  colText = i18n( "Receiver" );
579  setColumnText( mPaintInfo.senderCol, colText );
580  noRepaint = false;
581  mItems.resize(0);
582  updateMessageList();
583  setCurrentMsg(id);
584  setTopItemByIndex(top);
585  ensureCurrentItemVisible();
586 }
587 
588 //-----------------------------------------------------------------------------
590 {
591  bool oldState = isThreaded();
592  NestingPolicy oldNestPolicy = nestingPolicy;
593  TDEConfig* config = KMKernel::config();
594  TDEConfigGroupSaver saver(config, "Geometry");
595  mNested = config->readBoolEntry( "nestedMessages", false );
596 
597  nestingPolicy = (NestingPolicy)config->readNumEntry( "nestingPolicy", OpenUnread );
598  if ((nestingPolicy != oldNestPolicy) ||
599  (oldState != isThreaded()))
600  {
601  setRootIsDecorated( nestingPolicy != AlwaysOpen && isThreaded() );
602  reset();
603  }
604 
605 }
606 
607 //-----------------------------------------------------------------------------
609 {
610  if (!mFolder) return;
611  TDEConfig* config = KMKernel::config();
612 
613  TDEConfigGroupSaver saver(config, "Folder-" + mFolder->idString());
614  mNestedOverride = config->readBoolEntry( "threadMessagesOverride", false );
615  mSortCol = config->readNumEntry("SortColumn", mSortCol+1 /* inited to date column */);
616  mSortDescending = (mSortCol < 0);
617  mSortCol = abs(mSortCol) - 1;
618 
619  mTopItem = config->readNumEntry("Top", 0);
620  mCurrentItem = config->readNumEntry("Current", 0);
621  mCurrentItemSerNum = config->readNumEntry("CurrentSerialNum", 0);
622 
623  mPaintInfo.orderOfArrival = config->readBoolEntry( "OrderOfArrival", false );
624  mPaintInfo.status = config->readBoolEntry( "Status", false );
625 
626  { //area for config group "Geometry"
627  TDEConfigGroupSaver saver(config, "Geometry");
628  mNested = config->readBoolEntry( "nestedMessages", false );
629  nestingPolicy = (NestingPolicy)config->readNumEntry( "nestingPolicy", OpenUnread );
630  }
631 
632  setRootIsDecorated( nestingPolicy != AlwaysOpen && isThreaded() );
633  mSubjThreading = config->readBoolEntry( "threadMessagesBySubject", true );
634 }
635 
636 
637 //-----------------------------------------------------------------------------
639 {
640  if (!mFolder) return;
641  TDEConfig* config = KMKernel::config();
642  int mSortColAdj = mSortCol + 1;
643 
644  TDEConfigGroupSaver saver(config, "Folder-" + mFolder->idString());
645  config->writeEntry("SortColumn", (mSortDescending ? -mSortColAdj : mSortColAdj));
646  config->writeEntry("Top", topItemIndex());
647  config->writeEntry("Current", currentItemIndex());
648  HeaderItem* current = currentHeaderItem();
649  ulong sernum = 0;
650  if ( current && mFolder->getMsgBase( current->msgId() ) )
651  sernum = mFolder->getMsgBase( current->msgId() )->getMsgSerNum();
652  config->writeEntry("CurrentSerialNum", sernum);
653 
654  config->writeEntry("OrderOfArrival", mPaintInfo.orderOfArrival);
655  config->writeEntry("Status", mPaintInfo.status);
656 }
657 
658 //-----------------------------------------------------------------------------
660 {
661  TDEConfig* config = KMKernel::config();
662  saveLayout(config, "Header-Geometry");
663  TDEConfigGroupSaver saver(config, "General");
664  config->writeEntry("showMessageSize" , mPaintInfo.showSize);
665  config->writeEntry("showAttachmentColumn" , mPaintInfo.showAttachment);
666  config->writeEntry("showInvitationColumn" , mPaintInfo.showInvitation);
667  config->writeEntry("showImportantColumn" , mPaintInfo.showImportant);
668  config->writeEntry("showTodoColumn" , mPaintInfo.showTodo);
669  config->writeEntry("showSpamHamColumn" , mPaintInfo.showSpamHam);
670  config->writeEntry("showWatchedIgnoredColumn", mPaintInfo.showWatchedIgnored);
671  config->writeEntry("showStatusColumn" , mPaintInfo.showStatus);
672  config->writeEntry("showSignedColumn" , mPaintInfo.showSigned);
673  config->writeEntry("showCryptoColumn" , mPaintInfo.showCrypto);
674  config->writeEntry("showReceiverColumn" , mPaintInfo.showReceiver);
675 }
676 
677 //-----------------------------------------------------------------------------
678 void KMHeaders::setFolder( KMFolder *aFolder, bool forceJumpToUnread )
679 {
680  CREATE_TIMER(set_folder);
681  START_TIMER(set_folder);
682 
683  int id;
684  TQString str;
685 
686  mSortInfo.fakeSort = 0;
687  if ( mFolder && static_cast<KMFolder*>(mFolder) == aFolder ) {
688  int top = topItemIndex();
689  id = currentItemIndex();
692  updateMessageList(); // do not change the selection
693  setCurrentMsg(id);
694  setTopItemByIndex(top);
695  } else {
696  if (mFolder) {
697  // WABA: Make sure that no KMReaderWin is still using a msg
698  // from this folder, since it's msg's are about to be deleted.
699  highlightMessage(0, false);
700 
701  disconnect(mFolder, TQ_SIGNAL(numUnreadMsgsChanged(KMFolder*)),
702  this, TQ_SLOT(setFolderInfoStatus()));
703 
704  mFolder->markNewAsUnread();
706  disconnect(mFolder, TQ_SIGNAL(msgHeaderChanged(KMFolder*,int)),
707  this, TQ_SLOT(msgHeaderChanged(KMFolder*,int)));
708  disconnect(mFolder, TQ_SIGNAL(msgAdded(int)),
709  this, TQ_SLOT(msgAdded(int)));
710  disconnect(mFolder, TQ_SIGNAL( msgRemoved( int, TQString ) ),
711  this, TQ_SLOT( msgRemoved( int, TQString ) ) );
712  disconnect(mFolder, TQ_SIGNAL(changed()),
713  this, TQ_SLOT(msgChanged()));
714  disconnect(mFolder, TQ_SIGNAL(cleared()),
715  this, TQ_SLOT(folderCleared()));
716  disconnect(mFolder, TQ_SIGNAL(expunged( KMFolder* )),
717  this, TQ_SLOT(folderCleared()));
718  disconnect(mFolder, TQ_SIGNAL(closed()),
719  this, TQ_SLOT(folderClosed()));
720  disconnect( mFolder, TQ_SIGNAL( statusMsg( const TQString& ) ),
721  BroadcastStatus::instance(), TQ_SLOT( setStatusMsg( const TQString& ) ) );
722  disconnect(mFolder, TQ_SIGNAL(viewConfigChanged()), this, TQ_SLOT(reset()));
723  writeSortOrder();
724  mFolder->close("kmheaders");
725  // System folders remain open but we also should write the index from
726  // time to time
727  if (mFolder->dirty()) mFolder->writeIndex();
728  }
729 
730  mSortInfo.removed = 0;
731  mFolder = aFolder;
732  mSortInfo.dirty = true;
733 
734  mOwner->useAction()->setEnabled( mFolder ?
735  ( kmkernel->folderIsTemplates( mFolder ) ) : false );
736  mOwner->messageActions()->replyListAction()->setEnabled( mFolder ?
737  mFolder->isMailingListEnabled() : false );
738  if ( mFolder ) {
739  connect(mFolder, TQ_SIGNAL(msgHeaderChanged(KMFolder*,int)),
740  this, TQ_SLOT(msgHeaderChanged(KMFolder*,int)));
741  connect(mFolder, TQ_SIGNAL(msgAdded(int)),
742  this, TQ_SLOT(msgAdded(int)));
743  connect(mFolder, TQ_SIGNAL(msgRemoved(int,TQString)),
744  this, TQ_SLOT(msgRemoved(int,TQString)));
745  connect(mFolder, TQ_SIGNAL(changed()),
746  this, TQ_SLOT(msgChanged()));
747  connect(mFolder, TQ_SIGNAL(cleared()),
748  this, TQ_SLOT(folderCleared()));
749  connect(mFolder, TQ_SIGNAL(expunged( KMFolder* )),
750  this, TQ_SLOT(folderCleared()));
751  connect(mFolder, TQ_SIGNAL(closed()),
752  this, TQ_SLOT(folderClosed()));
753  connect(mFolder, TQ_SIGNAL(statusMsg(const TQString&)),
754  BroadcastStatus::instance(), TQ_SLOT( setStatusMsg( const TQString& ) ) );
755  connect(mFolder, TQ_SIGNAL(numUnreadMsgsChanged(KMFolder*)),
756  this, TQ_SLOT(setFolderInfoStatus()));
757  connect(mFolder, TQ_SIGNAL(viewConfigChanged()), this, TQ_SLOT(reset()));
758 
759  // Not very nice, but if we go from nested to non-nested
760  // in the folderConfig below then we need to do this otherwise
761  // updateMessageList would do something unspeakable
762  if (isThreaded()) {
763  noRepaint = true;
764  clear();
765  noRepaint = false;
766  mItems.resize( 0 );
767  }
768 
770 
771  CREATE_TIMER(kmfolder_open);
772  START_TIMER(kmfolder_open);
773  mFolder->open("kmheaders");
774  END_TIMER(kmfolder_open);
775  SHOW_TIMER(kmfolder_open);
776 
777  if (isThreaded()) {
778  noRepaint = true;
779  clear();
780  noRepaint = false;
781  mItems.resize( 0 );
782  }
783  }
784 
785  CREATE_TIMER(updateMsg);
786  START_TIMER(updateMsg);
787  updateMessageList(true, forceJumpToUnread);
788  END_TIMER(updateMsg);
789  SHOW_TIMER(updateMsg);
792 
793  TQString colText = i18n( "Sender" );
794  if (mFolder && (mFolder->whoField().lower() == "to") && !mPaintInfo.showReceiver)
795  colText = i18n("Receiver");
796  setColumnText( mPaintInfo.senderCol, colText);
797 
798  colText = i18n( "Date" );
799  if (mPaintInfo.orderOfArrival)
800  colText = i18n( "Order of Arrival" );
801  setColumnText( mPaintInfo.dateCol, colText);
802 
803  colText = i18n( "Subject" );
804  if (mPaintInfo.status)
805  colText = colText + i18n( " (Status)" );
806  setColumnText( mPaintInfo.subCol, colText);
807  }
808 
809  updateActions();
810 
811  END_TIMER(set_folder);
812  SHOW_TIMER(set_folder);
813 }
814 
815 //-----------------------------------------------------------------------------
817 {
818  if (mFolder->count() == 0) { // Folder cleared
819  mItems.resize(0);
820  clear();
821  return;
822  }
823  if (!isUpdatesEnabled()) return;
824 
825  // Remember selected messages, current message and some scrollbar data, as we have to restore it
826  const TQValueList<int> oldSelectedItems = selectedItems();
827  const int oldCurrentItemIndex = currentItemIndex();
828  const bool scrollbarAtTop = verticalScrollBar() &&
829  verticalScrollBar()->value() == verticalScrollBar()->minValue();
830  const bool scrollbarAtBottom = verticalScrollBar() &&
831  verticalScrollBar()->value() == verticalScrollBar()->maxValue();
832  const HeaderItem * const oldFirstVisibleItem = dynamic_cast<HeaderItem*>( itemAt( TQPoint( 0, 0 ) ) );
833  const int oldOffsetOfFirstVisibleItem = itemRect( oldFirstVisibleItem ).y();
834  const uint oldSerNumOfFirstVisibleItem = oldFirstVisibleItem ? oldFirstVisibleItem->msgSerNum() : 0;
835 
836  TQString msgIdMD5;
837  TQListViewItem *item = currentItem();
838  HeaderItem *hi = dynamic_cast<HeaderItem*>(item);
839  if (item && hi) {
840  // get the msgIdMD5 to compare it later
841  KMMsgBase *mb = mFolder->getMsgBase(hi->msgId());
842  if (mb)
843  msgIdMD5 = mb->msgIdMD5();
844  }
845 // if (!isUpdatesEnabled()) return;
846  // prevent IMAP messages from scrolling to top
847  disconnect(this,TQ_SIGNAL(currentChanged(TQListViewItem*)),
848  this,TQ_SLOT(highlightMessage(TQListViewItem*)));
849 
850  updateMessageList(); // do not change the selection
851 
852  // Restore scrollbar state and selected and current messages
853  setCurrentMsg( oldCurrentItemIndex );
854  setSelectedByIndex( oldSelectedItems, true );
855  if ( scrollbarAtTop ) {
856  setContentsPos( 0, 0 );
857  } else if ( scrollbarAtBottom ) {
858  setContentsPos( 0, contentsHeight() );
859  } else if ( oldSerNumOfFirstVisibleItem > 0 ) {
860  for ( uint i = 0; i < mItems.size(); ++i ) {
861  const KMMsgBase * const mMsgBase = mFolder->getMsgBase( i );
862  if ( mMsgBase->getMsgSerNum() == oldSerNumOfFirstVisibleItem ) {
863  setContentsPos( 0, itemPos( mItems[i] ) - oldOffsetOfFirstVisibleItem );
864  break;
865  }
866  }
867  }
868 
869  connect(this,TQ_SIGNAL(currentChanged(TQListViewItem*)),
870  this,TQ_SLOT(highlightMessage(TQListViewItem*)));
871 
872  // if the current message has changed then emit
873  // the selected signal to force an update
874 
875  // Normally the serial number of the message would be
876  // used to do this, but because we don't yet have
877  // guaranteed serial numbers for IMAP messages fall back
878  // to using the MD5 checksum of the msgId.
879  item = currentItem();
880  hi = dynamic_cast<HeaderItem*>(item);
881  if (item && hi) {
882  KMMsgBase *mb = mFolder->getMsgBase(hi->msgId());
883  if (mb) {
884  if (msgIdMD5.isEmpty() || (msgIdMD5 != mb->msgIdMD5()))
885  emit selected(mFolder->getMsg(hi->msgId()));
886  } else {
887  emit selected(0);
888  }
889  } else
890  emit selected(0);
891 }
892 
893 
894 //-----------------------------------------------------------------------------
896 {
897  HeaderItem* hi = 0;
898  if (!isUpdatesEnabled()) return;
899 
900  CREATE_TIMER(msgAdded);
901  START_TIMER(msgAdded);
902 
903  assert( mFolder->getMsgBase( id ) ); // otherwise using count() is wrong
904 
905  /* Create a new SortCacheItem to be used for threading. */
906  SortCacheItem *sci = new SortCacheItem;
907  sci->setId(id);
908  if (isThreaded()) {
909  // make sure the id and subject dicts grow, if necessary
910  if (mSortCacheItems.count() == (uint)mFolder->count()
911  || mSortCacheItems.count() == 0) {
912  kdDebug (5006) << "KMHeaders::msgAdded - Resizing id and subject trees of " << mFolder->label()
913  << ": before=" << mSortCacheItems.count() << " ,after=" << (mFolder->count()*2) << endl;
914  mSortCacheItems.resize(mFolder->count()*2);
915  mSubjectLists.resize(mFolder->count()*2);
916  }
917  TQString msgId = mFolder->getMsgBase(id)->msgIdMD5();
918  if (msgId.isNull())
919  msgId = "";
920  TQString replyToId = mFolder->getMsgBase(id)->replyToIdMD5();
921 
922  SortCacheItem *parent = findParent( sci );
923  if (!parent && mSubjThreading) {
924  parent = findParentBySubject( sci );
925  if (parent && sci->isImperfectlyThreaded()) {
926  // The parent we found could be by subject, in which case it is
927  // possible, that it would be preferrable to thread it below us,
928  // not the other way around. Check that. This is not only
929  // cosmetic, as getting this wrong leads to circular threading.
930  if (msgId == mFolder->getMsgBase(parent->item()->msgId())->replyToIdMD5()
931  || msgId == mFolder->getMsgBase(parent->item()->msgId())->replyToAuxIdMD5())
932  parent = NULL;
933  }
934  }
935 
936  if (parent && mFolder->getMsgBase(parent->id())->isWatched())
937  mFolder->getMsgBase(id)->setStatus( KMMsgStatusWatched );
938  else if (parent && mFolder->getMsgBase(parent->id())->isIgnored())
939  mFolder->getMsgBase(id)->setStatus( KMMsgStatusIgnored );
940  if (parent)
941  hi = new HeaderItem( parent->item(), id );
942  else
943  hi = new HeaderItem( this, id );
944 
945  // o/` ... my buddy and me .. o/`
946  hi->setSortCacheItem(sci);
947  sci->setItem(hi);
948 
949  // Update and resize the id trees.
950  mItems.resize( mFolder->count() );
951  mItems[id] = hi;
952 
953  if ( !msgId.isEmpty() )
954  mSortCacheItems.replace(msgId, sci);
955  /* Add to the list of potential parents for subject threading. But only if
956  * we are top level. */
957  if (mSubjThreading && parent) {
958  TQString subjMD5 = mFolder->getMsgBase(id)->strippedSubjectMD5();
959  if (subjMD5.isEmpty()) {
960  mFolder->getMsgBase(id)->initStrippedSubjectMD5();
961  subjMD5 = mFolder->getMsgBase(id)->strippedSubjectMD5();
962  }
963  if( !subjMD5.isEmpty()) {
964  if ( !mSubjectLists.find(subjMD5) )
965  mSubjectLists.insert(subjMD5, new TQPtrList<SortCacheItem>());
966  // insertion sort by date. See buildThreadTrees for details.
967  int p=0;
968  for (TQPtrListIterator<SortCacheItem> it(*mSubjectLists[subjMD5]);
969  it.current(); ++it) {
970  KMMsgBase *mb = mFolder->getMsgBase((*it)->id());
971  if ( mb->date() < mFolder->getMsgBase(id)->date())
972  break;
973  p++;
974  }
975  mSubjectLists[subjMD5]->insert( p, sci);
976  sci->setSubjectThreadingList( mSubjectLists[subjMD5] );
977  }
978  }
979  // The message we just added might be a better parent for one of the as of
980  // yet imperfectly threaded messages. Let's find out.
981 
982  /* In case the current item is taken during reparenting, prevent qlistview
983  * from selecting some unrelated item as a result of take() emitting
984  * currentChanged. */
985  disconnect( this, TQ_SIGNAL(currentChanged(TQListViewItem*)),
986  this, TQ_SLOT(highlightMessage(TQListViewItem*)));
987 
988  if ( !msgId.isEmpty() ) {
989  TQPtrListIterator<HeaderItem> it(mImperfectlyThreadedList);
990  HeaderItem *cur;
991  while ( (cur = it.current()) ) {
992  ++it;
993  int tryMe = cur->msgId();
994  // Check, whether our message is the replyToId or replyToAuxId of
995  // this one. If so, thread it below our message, unless it is already
996  // correctly threaded by replyToId.
997  bool perfectParent = true;
998  KMMsgBase *otherMsg = mFolder->getMsgBase(tryMe);
999  if ( !otherMsg ) {
1000  kdDebug(5006) << "otherMsg is NULL !!! tryMe: " << tryMe << endl;
1001  continue;
1002  }
1003  TQString otherId = otherMsg->replyToIdMD5();
1004  if (msgId != otherId) {
1005  if (msgId != otherMsg->replyToAuxIdMD5())
1006  continue;
1007  else {
1008  if (!otherId.isEmpty() && mSortCacheItems.find(otherId))
1009  continue;
1010  else
1011  // Thread below us by aux id, but keep on the list of
1012  // imperfectly threaded messages.
1013  perfectParent = false;
1014  }
1015  }
1016  TQListViewItem *newParent = mItems[id];
1017  TQListViewItem *msg = mItems[tryMe];
1018 
1019  if (msg->parent())
1020  msg->parent()->takeItem(msg);
1021  else
1022  takeItem(msg);
1023  newParent->insertItem(msg);
1024  HeaderItem *hi = static_cast<HeaderItem*>( newParent );
1025  hi->sortCacheItem()->addSortedChild( cur->sortCacheItem() );
1026 
1028 
1029  if (perfectParent) {
1030  mImperfectlyThreadedList.removeRef (mItems[tryMe]);
1031  // The item was imperfectly thread before, now it's parent
1032  // is there. Update the .sorted file accordingly.
1033  TQString sortFile = KMAIL_SORT_FILE(mFolder);
1034  FILE *sortStream = fopen(TQFile::encodeName(sortFile), "r+");
1035  if (sortStream) {
1036  mItems[tryMe]->sortCacheItem()->updateSortFile( sortStream, mFolder );
1037  fclose (sortStream);
1038  }
1039  }
1040  }
1041  }
1042  // Add ourselves only now, to avoid circularity above.
1043  if (hi && hi->sortCacheItem()->isImperfectlyThreaded())
1044  mImperfectlyThreadedList.append(hi);
1045  } else {
1046  // non-threaded case
1047  hi = new HeaderItem( this, id );
1048  mItems.resize( mFolder->count() );
1049  mItems[id] = hi;
1050  // o/` ... my buddy and me .. o/`
1051  hi->setSortCacheItem(sci);
1052  sci->setItem(hi);
1053  }
1054  if (mSortInfo.fakeSort) {
1055  TQObject::disconnect(header(), TQ_SIGNAL(clicked(int)), this, TQ_SLOT(dirtySortOrder(int)));
1056  TDEListView::setSorting(mSortCol, !mSortDescending );
1057  mSortInfo.fakeSort = 0;
1058  }
1059  appendItemToSortFile(hi); //inserted into sorted list
1060 
1061  msgHeaderChanged(mFolder,id);
1062 
1063  if ((childCount() == 1) && hi) {
1064  setSelected( hi, true );
1065  setCurrentItem( firstChild() );
1066  setSelectionAnchor( currentItem() );
1067  highlightMessage( currentItem() );
1068  }
1069 
1070  /* restore signal */
1071  connect( this, TQ_SIGNAL(currentChanged(TQListViewItem*)),
1072  this, TQ_SLOT(highlightMessage(TQListViewItem*)));
1073 
1074  emit msgAddedToListView( hi );
1075  END_TIMER(msgAdded);
1076  SHOW_TIMER(msgAdded);
1077 }
1078 
1079 
1080 //-----------------------------------------------------------------------------
1081 void KMHeaders::msgRemoved(int id, TQString msgId )
1082 {
1083  if (!isUpdatesEnabled()) return;
1084 
1085  if ((id < 0) || (id >= (int)mItems.size()))
1086  return;
1087  /*
1088  * qlistview has its own ideas about what to select as the next
1089  * item once this one is removed. Sine we have already selected
1090  * something in prepare/finalizeMove that's counter productive
1091  */
1092  disconnect( this, TQ_SIGNAL(currentChanged(TQListViewItem*)),
1093  this, TQ_SLOT(highlightMessage(TQListViewItem*)));
1094 
1095  HeaderItem *removedItem = mItems[id];
1096  if (!removedItem) return;
1097  HeaderItem *curItem = currentHeaderItem();
1098 
1099  for (int i = id; i < (int)mItems.size() - 1; ++i) {
1100  mItems[i] = mItems[i+1];
1101  mItems[i]->setMsgId( i );
1102  mItems[i]->sortCacheItem()->setId( i );
1103  }
1104 
1105  mItems.resize( mItems.size() - 1 );
1106 
1107  if (isThreaded() && mFolder->count()) {
1108  if ( !msgId.isEmpty() && mSortCacheItems[msgId] ) {
1109  if (mSortCacheItems[msgId] == removedItem->sortCacheItem())
1110  mSortCacheItems.remove(msgId);
1111  }
1112  // Remove the message from the list of potential parents for threading by
1113  // subject.
1114  if ( mSubjThreading && removedItem->sortCacheItem()->subjectThreadingList() )
1115  removedItem->sortCacheItem()->subjectThreadingList()->removeRef( removedItem->sortCacheItem() );
1116 
1117  // Reparent children of item.
1118  TQListViewItem *myParent = removedItem;
1119  TQListViewItem *myChild = myParent->firstChild();
1120  TQListViewItem *threadRoot = myParent;
1121  while (threadRoot->parent())
1122  threadRoot = threadRoot->parent();
1123  TQString key = static_cast<HeaderItem*>(threadRoot)->key(mSortCol, !mSortDescending);
1124 
1125  TQPtrList<TQListViewItem> childList;
1126  while (myChild) {
1127  HeaderItem *item = static_cast<HeaderItem*>(myChild);
1128  // Just keep the item at top level, if it will be deleted anyhow
1129  if ( !item->aboutToBeDeleted() ) {
1130  childList.append(myChild);
1131  }
1132  myChild = myChild->nextSibling();
1133  if ( item->aboutToBeDeleted() ) {
1134  myParent->takeItem( item );
1135  insertItem( item );
1136  mRoot->addSortedChild( item->sortCacheItem() );
1137  }
1138  item->setTempKey( key + item->key( mSortCol, !mSortDescending ));
1139  if (mSortInfo.fakeSort) {
1140  TQObject::disconnect(header(), TQ_SIGNAL(clicked(int)), this, TQ_SLOT(dirtySortOrder(int)));
1141  TDEListView::setSorting(mSortCol, !mSortDescending );
1142  mSortInfo.fakeSort = 0;
1143  }
1144  }
1145 
1146  for (TQPtrListIterator<TQListViewItem> it(childList); it.current() ; ++it ) {
1147  TQListViewItem *lvi = *it;
1148  HeaderItem *item = static_cast<HeaderItem*>(lvi);
1149  SortCacheItem *sci = item->sortCacheItem();
1150  SortCacheItem *parent = findParent( sci );
1151  if ( !parent && mSubjThreading )
1152  parent = findParentBySubject( sci );
1153 
1154  Q_ASSERT( !parent || parent->item() != removedItem );
1155  myParent->takeItem(lvi);
1156  if ( parent && parent->item() != item && parent->item() != removedItem ) {
1157  parent->item()->insertItem(lvi);
1158  parent->addSortedChild( sci );
1159  } else {
1160  insertItem(lvi);
1161  mRoot->addSortedChild( sci );
1162  }
1163 
1164  if ((!parent || sci->isImperfectlyThreaded())
1165  && !mImperfectlyThreadedList.containsRef(item))
1166  mImperfectlyThreadedList.append(item);
1167 
1168  if (parent && !sci->isImperfectlyThreaded()
1169  && mImperfectlyThreadedList.containsRef(item))
1170  mImperfectlyThreadedList.removeRef(item);
1171  }
1172  }
1173  // Make sure our data structures are cleared.
1174  if (!mFolder->count())
1175  folderCleared();
1176 
1177  mImperfectlyThreadedList.removeRef( removedItem );
1178 #ifdef DEBUG
1179  // This should never happen, in this case the folders are inconsistent.
1180  while ( mImperfectlyThreadedList.findRef( removedItem ) != -1 ) {
1181  mImperfectlyThreadedList.remove();
1182  kdDebug(5006) << "Remove doubled item from mImperfectlyThreadedList: " << removedItem << endl;
1183  }
1184 #endif
1185  delete removedItem;
1186  // we might have rethreaded it, in which case its current state will be lost
1187  if ( curItem ) {
1188  if ( curItem != removedItem ) {
1189  setCurrentItem( curItem );
1190  setSelectionAnchor( currentItem() );
1191  } else {
1192  // We've removed the current item, which means it was removed from
1193  // something other than a user move or copy, which would have selected
1194  // the next logical mail. This can happen when the mail is deleted by
1195  // a filter, or some other behind the scenes action. Select something
1196  // sensible, then, and make sure the reader window is cleared.
1197  emit maybeDeleting();
1198  int contentX, contentY;
1199  HeaderItem *nextItem = prepareMove( &contentX, &contentY );
1200  finalizeMove( nextItem, contentX, contentY );
1201  }
1202  }
1203  /* restore signal */
1204  connect( this, TQ_SIGNAL(currentChanged(TQListViewItem*)),
1205  this, TQ_SLOT(highlightMessage(TQListViewItem*)));
1206 }
1207 
1208 
1209 //-----------------------------------------------------------------------------
1211 {
1212  if (msgId<0 || msgId >= (int)mItems.size() || !isUpdatesEnabled()) return;
1213  HeaderItem *item = mItems[msgId];
1214  if (item) {
1215  item->irefresh();
1216  item->repaint();
1217  }
1218 }
1219 
1220 
1221 //-----------------------------------------------------------------------------
1222 void KMHeaders::setMsgStatus (KMMsgStatus status, bool toggle)
1223 {
1224  // kdDebug() << k_funcinfo << endl;
1225  SerNumList serNums = selectedVisibleSernums();
1226  if (serNums.empty())
1227  return;
1228 
1229  KMCommand *command = new KMSeStatusCommand( status, serNums, toggle );
1230  command->start();
1231 }
1232 
1233 
1234 TQPtrList<TQListViewItem> KMHeaders::currentThread() const
1235 {
1236  if (!mFolder) return TQPtrList<TQListViewItem>();
1237 
1238  // starting with the current item...
1239  TQListViewItem *curItem = currentItem();
1240  if (!curItem) return TQPtrList<TQListViewItem>();
1241 
1242  // ...find the top-level item:
1243  TQListViewItem *topOfThread = curItem;
1244  while ( topOfThread->parent() )
1245  topOfThread = topOfThread->parent();
1246 
1247  // collect the items in this thread:
1248  TQPtrList<TQListViewItem> list;
1249  TQListViewItem *topOfNextThread = topOfThread->nextSibling();
1250  for ( TQListViewItemIterator it( topOfThread ) ;
1251  it.current() && it.current() != topOfNextThread ; ++it )
1252  list.append( it.current() );
1253  return list;
1254 }
1255 
1256 void KMHeaders::setThreadStatus(KMMsgStatus status, bool toggle)
1257 {
1258  TQPtrList<TQListViewItem> curThread;
1259 
1260  if (mFolder) {
1261  TQPtrList<TQListViewItem> topOfThreads;
1262 
1263  // for each selected item...
1264  for (TQListViewItem *item = firstChild(); item; item = item->itemBelow())
1265  if (item->isSelected() ) {
1266  // ...find the top-level item:
1267  TQListViewItem *top = item;
1268  while ( top->parent() )
1269  top = top->parent();
1270  if (!topOfThreads.contains(top)) {
1271  topOfThreads.append(top);
1272  }
1273  }
1274 
1275  // for each thread found...
1276  for ( TQPtrListIterator<TQListViewItem> it( topOfThreads ) ;
1277  it.current() ; ++ it ) {
1278  TQListViewItem *top = *it;
1279 
1280  // collect the items in this thread:
1281  TQListViewItem *topOfNextThread = top->nextSibling();
1282  for ( TQListViewItemIterator it( top ) ;
1283  it.current() && it.current() != topOfNextThread ; ++it )
1284  curThread.append( it.current() );
1285  }
1286  }
1287 
1288  TQPtrListIterator<TQListViewItem> it( curThread );
1289  SerNumList serNums;
1290 
1291  for ( it.toFirst() ; it.current() ; ++it ) {
1292  int id = static_cast<HeaderItem*>(*it)->msgId();
1293  KMMsgBase *msgBase = mFolder->getMsgBase( id );
1294  serNums.append( msgBase->getMsgSerNum() );
1295  }
1296 
1297  if (serNums.empty())
1298  return;
1299 
1300  KMCommand *command = new KMSeStatusCommand( status, serNums, toggle );
1301  command->start();
1302 }
1303 
1304 //-----------------------------------------------------------------------------
1306 {
1307  if ( !msg ) return 2; // messageRetrieve(0) is always possible
1308  msg->setTransferInProgress(false);
1309  int filterResult = kmkernel->filterMgr()->process(msg,KMFilterMgr::Explicit);
1310  if (filterResult == 2) {
1311  // something went horribly wrong (out of space?)
1312  kmkernel->emergencyExit( i18n("Unable to process messages: " ) + TQString::fromLocal8Bit(strerror(errno)));
1313  return 2;
1314  }
1315  if (msg->parent()) { // unGet this msg
1316  int idx = -1;
1317  KMFolder * p = 0;
1318  KMMsgDict::instance()->getLocation( msg, &p, &idx );
1319  assert( p == msg->parent() ); assert( idx >= 0 );
1320  p->unGetMsg( idx );
1321  }
1322 
1323  return filterResult;
1324 }
1325 
1326 
1328 {
1329  if ( !isThreaded() ) return;
1330  // find top-level parent of currentItem().
1331  TQListViewItem *item = currentItem();
1332  if ( !item ) return;
1333  clearSelection();
1334  item->setSelected( true );
1335  while ( item->parent() )
1336  item = item->parent();
1337  HeaderItem * hdrItem = static_cast<HeaderItem*>(item);
1338  hdrItem->setOpenRecursive( expand );
1339  if ( !expand ) // collapse can hide the current item:
1340  setCurrentMsg( hdrItem->msgId() );
1341  ensureItemVisible( currentItem() );
1342 }
1343 
1345 {
1346  if ( !isThreaded() ) return;
1347 
1348  TQListViewItem * item = currentItem();
1349  if( item ) {
1350  clearSelection();
1351  item->setSelected( true );
1352  }
1353 
1354  for ( TQListViewItem *item = firstChild() ;
1355  item ; item = item->nextSibling() )
1356  static_cast<HeaderItem*>(item)->setOpenRecursive( expand );
1357  if ( !expand ) { // collapse can hide the current item:
1358  TQListViewItem * item = currentItem();
1359  if( item ) {
1360  while ( item->parent() )
1361  item = item->parent();
1362  setCurrentMsg( static_cast<HeaderItem*>(item)->msgId() );
1363  }
1364  }
1365  ensureItemVisible( currentItem() );
1366 }
1367 
1368 //-----------------------------------------------------------------------------
1370 {
1371  // set the width of the frame to a reasonable value for the current GUI style
1372  int frameWidth;
1373  if( style().isA("KeramikStyle") )
1374  frameWidth = style().pixelMetric( TQStyle::PM_DefaultFrameWidth ) - 1;
1375  else
1376  frameWidth = style().pixelMetric( TQStyle::PM_DefaultFrameWidth );
1377  if ( frameWidth < 0 )
1378  frameWidth = 0;
1379  if ( frameWidth != lineWidth() )
1380  setLineWidth( frameWidth );
1381 }
1382 
1383 //-----------------------------------------------------------------------------
1384 void KMHeaders::styleChange( TQStyle& oldStyle )
1385 {
1387  TDEListView::styleChange( oldStyle );
1388 }
1389 
1390 //-----------------------------------------------------------------------------
1392 {
1393  if ( !mFolder ) return;
1394  TQString str;
1395  const int unread = mFolder->countUnread();
1396  if ( static_cast<KMFolder*>(mFolder) == kmkernel->outboxFolder() )
1397  str = unread ? i18n( "1 unsent", "%n unsent", unread ) : i18n( "0 unsent" );
1398  else
1399  str = unread ? i18n( "1 unread", "%n unread", unread ) : i18n( "0 unread" );
1400  const int count = mFolder->count();
1401  str = count ? i18n( "1 message, %1.", "%n messages, %1.", count ).arg( str )
1402  : i18n( "0 messages" ); // no need for "0 unread" to be added here
1403  if ( mFolder->isReadOnly() )
1404  str = i18n("%1 = n messages, m unread.", "%1 Folder is read-only.").arg( str );
1405  BroadcastStatus::instance()->setStatusMsg(str);
1406 }
1407 
1408 //-----------------------------------------------------------------------------
1409 void KMHeaders::applyFiltersOnMsg()
1410 {
1411  if (ActionScheduler::isEnabled() ||
1412  kmkernel->filterMgr()->atLeastOneOnlineImapFolderTarget()) {
1413  // uses action scheduler
1414  KMFilterMgr::FilterSet set = KMFilterMgr::Explicit;
1415  TQValueList<KMFilter*> filters = kmkernel->filterMgr()->filters();
1416  ActionScheduler *scheduler = new ActionScheduler( set, filters, this );
1417  scheduler->setAutoDestruct( true );
1418 
1419  int contentX, contentY;
1420  HeaderItem *nextItem = prepareMove( &contentX, &contentY );
1421  TQPtrList<KMMsgBase> msgList = *selectedMsgs(true);
1422  finalizeMove( nextItem, contentX, contentY );
1423 
1424  for (KMMsgBase *msg = msgList.first(); msg; msg = msgList.next())
1425  scheduler->execFilters( msg );
1426  } else {
1427  int contentX, contentY;
1428  HeaderItem *nextItem = prepareMove( &contentX, &contentY );
1429 
1430  //prevent issues with stale message pointers by using serial numbers instead
1431  TQValueList<unsigned long> serNums = KMMsgDict::serNumList( *selectedMsgs() );
1432  if ( serNums.isEmpty() )
1433  return;
1434 
1435  finalizeMove( nextItem, contentX, contentY );
1436  CREATE_TIMER(filter);
1437  START_TIMER(filter);
1438 
1439  KCursorSaver busy( KBusyPtr::busy() );
1440  int msgCount = 0;
1441  int msgCountToFilter = serNums.count();
1442  ProgressItem* progressItem =
1443  ProgressManager::createProgressItem( "filter"+ProgressManager::getUniqueID(),
1444  i18n( "Filtering messages" ) );
1445  progressItem->setTotalItems( msgCountToFilter );
1446 
1447  for ( TQValueList<unsigned long>::ConstIterator it = serNums.constBegin();
1448  it != serNums.constEnd(); ++it ) {
1449  msgCount++;
1450  if ( msgCountToFilter - msgCount < 10 || !( msgCount % 20 ) || msgCount <= 10 ) {
1451  progressItem->updateProgress();
1452  TQString statusMsg = i18n("Filtering message %1 of %2");
1453  statusMsg = statusMsg.arg( msgCount ).arg( msgCountToFilter );
1454  KPIM::BroadcastStatus::instance()->setStatusMsg( statusMsg );
1455  TDEApplication::kApplication()->eventLoop()->processEvents( TQEventLoop::ExcludeUserInput, 50 );
1456  }
1457 
1458  KMFolder *folder = 0;
1459  int idx;
1460  KMMsgDict::instance()->getLocation( *it, &folder, &idx );
1461  KMMessage *msg = 0;
1462  if (folder)
1463  msg = folder->getMsg(idx);
1464  if (msg) {
1465  if (msg->transferInProgress())
1466  continue;
1467  msg->setTransferInProgress(true);
1468  if (!msg->isComplete()) {
1469  FolderJob *job = mFolder->createJob(msg);
1470  connect(job, TQ_SIGNAL(messageRetrieved(KMMessage*)),
1471  this, TQ_SLOT(slotFilterMsg(KMMessage*)));
1472  job->start();
1473  } else {
1474  if (slotFilterMsg(msg) == 2)
1475  break;
1476  }
1477  } else {
1478  kdDebug (5006) << "####### KMHeaders::applyFiltersOnMsg -"
1479  " A message went missing during filtering " << endl;
1480  }
1481  progressItem->incCompletedItems();
1482  }
1483  progressItem->setComplete();
1484  progressItem = 0;
1485  END_TIMER(filter);
1486  SHOW_TIMER(filter);
1487  }
1488 }
1489 
1490 
1491 //-----------------------------------------------------------------------------
1492 void KMHeaders::setMsgRead (int msgId)
1493 {
1494  KMMsgBase *msgBase = mFolder->getMsgBase( msgId );
1495  if (!msgBase)
1496  return;
1497 
1498  SerNumList serNums;
1499  if (msgBase->isNew() || msgBase->isUnread()) {
1500  serNums.append( msgBase->getMsgSerNum() );
1501  }
1502 
1503  KMCommand *command = new KMSeStatusCommand( KMMsgStatusRead, serNums );
1504  command->start();
1505 }
1506 
1507 
1508 //-----------------------------------------------------------------------------
1509 void KMHeaders::deleteMsg ()
1510 {
1511  //make sure we have an associated folder (root of folder tree does not).
1512  if (!mFolder)
1513  return;
1514 
1515  int contentX, contentY;
1516  HeaderItem *nextItem = prepareMove( &contentX, &contentY );
1517  KMMessageList msgList = *selectedMsgs(true);
1518  finalizeMove( nextItem, contentX, contentY );
1519 
1520  KMCommand *command = new KMDeleteMsgCommand( mFolder, msgList );
1521  connect( command, TQ_SIGNAL( completed( KMCommand * ) ),
1522  this, TQ_SLOT( slotMoveCompleted( KMCommand * ) ) );
1523  command->start();
1524 
1525  BroadcastStatus::instance()->setStatusMsg("");
1526  // triggerUpdate();
1527 }
1528 
1529 
1530 //-----------------------------------------------------------------------------
1532 {
1533  if (mMenuToFolder[menuId])
1534  moveMsgToFolder( mMenuToFolder[menuId] );
1535 }
1536 
1537 //-----------------------------------------------------------------------------
1538 HeaderItem* KMHeaders::prepareMove( int *contentX, int *contentY )
1539 {
1540  HeaderItem *ret = 0;
1541  emit maybeDeleting();
1542 
1543  disconnect( this, TQ_SIGNAL(currentChanged(TQListViewItem*)),
1544  this, TQ_SLOT(highlightMessage(TQListViewItem*)));
1545 
1546  TQListViewItem *curItem;
1547  HeaderItem *item;
1548  curItem = currentItem();
1549  while (curItem && curItem->isSelected() && curItem->itemBelow())
1550  curItem = curItem->itemBelow();
1551  while (curItem && curItem->isSelected() && curItem->itemAbove())
1552  curItem = curItem->itemAbove();
1553  item = static_cast<HeaderItem*>(curItem);
1554 
1555  *contentX = contentsX();
1556  *contentY = contentsY();
1557 
1558  if (item && !item->isSelected())
1559  ret = item;
1560 
1561  return ret;
1562 }
1563 
1564 //-----------------------------------------------------------------------------
1565 void KMHeaders::finalizeMove( HeaderItem *item, int contentX, int contentY )
1566 {
1567  emit selected( 0 );
1568  clearSelection();
1569 
1570  if ( item ) {
1571  setCurrentItem( item );
1572  setSelected( item, true );
1573  setSelectionAnchor( currentItem() );
1574  mPrevCurrent = 0;
1575  highlightMessage( item, false);
1576  }
1577 
1578  setContentsPos( contentX, contentY );
1580  connect( this, TQ_SIGNAL(currentChanged(TQListViewItem*)),
1581  this, TQ_SLOT(highlightMessage(TQListViewItem*)));
1582 }
1583 
1584 
1585 //-----------------------------------------------------------------------------
1586 void KMHeaders::moveMsgToFolder ( KMFolder* destFolder, bool askForConfirmation )
1587 {
1588  if ( destFolder == mFolder ) return; // Catch the noop case
1589  if ( mFolder->isReadOnly() ) return;
1590 
1591  KMMessageList msgList = *selectedMsgs();
1592  if ( msgList.isEmpty() ) return;
1593  if ( !destFolder && askForConfirmation && // messages shall be deleted
1594  KMessageBox::warningContinueCancel(this,
1595  i18n("<qt>Do you really want to delete the selected message?<br>"
1596  "Once deleted, it cannot be restored.</qt>",
1597  "<qt>Do you really want to delete the %n selected messages?<br>"
1598  "Once deleted, they cannot be restored.</qt>", msgList.count() ),
1599  msgList.count()>1 ? i18n("Delete Messages") : i18n("Delete Message"), KStdGuiItem::del(),
1600  "NoConfirmDelete") == KMessageBox::Cancel )
1601  return; // user canceled the action
1602 
1603  // remember the message to select afterwards
1604  int contentX, contentY;
1605  HeaderItem *nextItem = prepareMove( &contentX, &contentY );
1606  msgList = *selectedMsgs(true);
1607  finalizeMove( nextItem, contentX, contentY );
1608 
1609  KMCommand *command = new KMMoveCommand( destFolder, msgList );
1610  connect( command, TQ_SIGNAL( completed( KMCommand * ) ),
1611  this, TQ_SLOT( slotMoveCompleted( KMCommand * ) ) );
1612  command->start();
1613 }
1614 
1615 void KMHeaders::slotMoveCompleted( KMCommand *command )
1616 {
1617  kdDebug(5006) << k_funcinfo << command->result() << endl;
1618  bool deleted = static_cast<KMMoveCommand *>( command )->destFolder() == 0;
1619  if ( command->result() == KMCommand::OK ) {
1620  // make sure the current item is shown
1622  BroadcastStatus::instance()->setStatusMsg(
1623  deleted ? i18n("Messages deleted successfully.") : i18n("Messages moved successfully") );
1624  } else {
1625  /* The move failed or the user canceled it; reset the state of all
1626  * messages involved and repaint.
1627  *
1628  * Note: This potentially resets too many items if there is more than one
1629  * move going on. Oh well, I suppose no animals will be harmed.
1630  * */
1631  for (TQListViewItemIterator it(this); it.current(); it++) {
1632  HeaderItem *item = static_cast<HeaderItem*>(it.current());
1633  if ( item->aboutToBeDeleted() ) {
1634  item->setAboutToBeDeleted ( false );
1635  item->setSelectable ( true );
1636  KMMsgBase *msgBase = mFolder->getMsgBase(item->msgId());
1637  if ( msgBase->isMessage() ) {
1638  KMMessage *msg = static_cast<KMMessage *>(msgBase);
1639  if ( msg ) msg->setTransferInProgress( false, true );
1640  }
1641  }
1642  }
1643  triggerUpdate();
1644  if ( command->result() == KMCommand::Failed )
1645  BroadcastStatus::instance()->setStatusMsg(
1646  deleted ? i18n("Deleting messages failed.") : i18n("Moving messages failed.") );
1647  else
1648  BroadcastStatus::instance()->setStatusMsg(
1649  deleted ? i18n("Deleting messages canceled.") : i18n("Moving messages canceled.") );
1650  }
1651  mOwner->updateMessageActions();
1652 }
1653 
1654 bool KMHeaders::canUndo() const
1655 {
1656  return ( kmkernel->undoStack()->size() > 0 );
1657 }
1658 
1659 //-----------------------------------------------------------------------------
1660 void KMHeaders::undo()
1661 {
1662  kmkernel->undoStack()->undo();
1663 }
1664 
1665 //-----------------------------------------------------------------------------
1667 {
1668  if (mMenuToFolder[menuId])
1669  copyMsgToFolder( mMenuToFolder[menuId] );
1670 }
1671 
1672 
1673 //-----------------------------------------------------------------------------
1675 {
1676  if ( !destFolder )
1677  return;
1678 
1679  KMCommand * command = 0;
1680  if (aMsg)
1681  command = new KMCopyCommand( destFolder, aMsg );
1682  else {
1683  KMMessageList msgList = *selectedMsgs();
1684  command = new KMCopyCommand( destFolder, msgList );
1685  }
1686 
1687  command->start();
1688 }
1689 
1690 
1691 //-----------------------------------------------------------------------------
1693 {
1694  if (!mFolder) return;
1695  if (cur >= mFolder->count()) cur = mFolder->count() - 1;
1696  if ((cur >= 0) && (cur < (int)mItems.size())) {
1697  clearSelection();
1698  setCurrentItem( mItems[cur] );
1699  setSelected( mItems[cur], true );
1700  setSelectionAnchor( currentItem() );
1701  }
1704 }
1705 
1706 //-----------------------------------------------------------------------------
1707 void KMHeaders::setSelected( TQListViewItem *item, bool selected )
1708 {
1709  if ( !item )
1710  return;
1711 
1712  if ( item->isVisible() )
1713  TDEListView::setSelected( item, selected );
1714 
1715  // If the item is the parent of a closed thread recursively select
1716  // children .
1717  if ( isThreaded() && !item->isOpen() && item->firstChild() ) {
1718  TQListViewItem *nextRoot = item->itemBelow();
1719  TQListViewItemIterator it( item->firstChild() );
1720  for( ; (*it) != nextRoot; ++it ) {
1721  if ( (*it)->isVisible() )
1722  (*it)->setSelected( selected );
1723  }
1724  }
1725 }
1726 
1727 void KMHeaders::setSelectedByIndex( TQValueList<int> items, bool selected )
1728 {
1729  for ( TQValueList<int>::Iterator it = items.begin(); it != items.end(); ++it )
1730  {
1731  if ( ((*it) >= 0) && ((*it) < (int)mItems.size()) )
1732  {
1733  setSelected( mItems[(*it)], selected );
1734  }
1735  }
1736 }
1737 
1739 {
1740  // fugly, but I see no way around it
1741  for (TQListViewItemIterator it(this); it.current(); it++) {
1742  HeaderItem *item = static_cast<HeaderItem*>(it.current());
1743  if ( item->aboutToBeDeleted() ) {
1744  KMMsgBase *msgBase = mFolder->getMsgBase( item->msgId() );
1745  if ( serNum == msgBase->getMsgSerNum() ) {
1746  item->setAboutToBeDeleted ( false );
1747  item->setSelectable ( true );
1748  }
1749  }
1750  }
1751  triggerUpdate();
1752 }
1753 
1754 //-----------------------------------------------------------------------------
1755 KMMessageList* KMHeaders::selectedMsgs(bool toBeDeleted)
1756 {
1757  mSelMsgBaseList.clear();
1758  for (TQListViewItemIterator it(this); it.current(); it++) {
1759  if ( it.current()->isSelected() && it.current()->isVisible() ) {
1760  HeaderItem *item = static_cast<HeaderItem*>(it.current());
1761  if ( !item->aboutToBeDeleted() ) { // we are already working on this one
1762  if (toBeDeleted) {
1763  // make sure the item is not uselessly rethreaded and not selectable
1764  item->setAboutToBeDeleted ( true );
1765  item->setSelectable ( false );
1766  }
1767  KMMsgBase *msgBase = mFolder->getMsgBase(item->msgId());
1768  mSelMsgBaseList.append(msgBase);
1769  }
1770  }
1771  }
1772  return &mSelMsgBaseList;
1773 }
1774 
1775 //-----------------------------------------------------------------------------
1776 TQValueList<int> KMHeaders::selectedItems()
1777 {
1778  TQValueList<int> items;
1779  for ( TQListViewItemIterator it(this); it.current(); it++ )
1780  {
1781  if ( it.current()->isSelected() && it.current()->isVisible() )
1782  {
1783  HeaderItem* item = static_cast<HeaderItem*>( it.current() );
1784  items.append( item->msgId() );
1785  }
1786  }
1787  return items;
1788 }
1789 
1790 //-----------------------------------------------------------------------------
1792 {
1793  int selectedMsg = -1;
1794  TQListViewItem *item;
1795  for (item = firstChild(); item; item = item->itemBelow())
1796  if (item->isSelected()) {
1797  selectedMsg = (static_cast<HeaderItem*>(item))->msgId();
1798  break;
1799  }
1800  return selectedMsg;
1801 }
1802 
1803 //-----------------------------------------------------------------------------
1805 {
1806  TQListViewItem *lvi = currentItem();
1807  if (lvi && lvi->itemBelow()) {
1808  clearSelection();
1809  setSelected( lvi, false );
1811  setSelectionAnchor( currentItem() );
1812  ensureCurrentItemVisible();
1813  }
1814 }
1815 
1817 {
1818  KMMessage *cm = currentMsg();
1819  if ( cm && cm->isBeingParsed() )
1820  return;
1821  TQListViewItem *lvi = currentItem();
1822  if( lvi ) {
1823  TQListViewItem *below = lvi->itemBelow();
1824  TQListViewItem *temp = lvi;
1825  if (lvi && below ) {
1826  while (temp) {
1827  temp->firstChild();
1828  temp = temp->parent();
1829  }
1830  lvi->repaint();
1831  /* test to see if we need to unselect messages on back track */
1832  (below->isSelected() ? setSelected(lvi, false) : setSelected(below, true));
1833  setCurrentItem(below);
1836  }
1837  }
1838 }
1839 
1840 //-----------------------------------------------------------------------------
1842 {
1843  TQListViewItem *lvi = currentItem();
1844  if (lvi && lvi->itemAbove()) {
1845  clearSelection();
1846  setSelected( lvi, false );
1848  setSelectionAnchor( currentItem() );
1849  ensureCurrentItemVisible();
1850  }
1851 }
1852 
1854 {
1855  KMMessage *cm = currentMsg();
1856  if ( cm && cm->isBeingParsed() )
1857  return;
1858  TQListViewItem *lvi = currentItem();
1859  if( lvi ) {
1860  TQListViewItem *above = lvi->itemAbove();
1861  TQListViewItem *temp = lvi;
1862 
1863  if (lvi && above) {
1864  while (temp) {
1865  temp->firstChild();
1866  temp = temp->parent();
1867  }
1868  lvi->repaint();
1869  /* test to see if we need to unselect messages on back track */
1870  (above->isSelected() ? setSelected(lvi, false) : setSelected(above, true));
1871  setCurrentItem(above);
1874  }
1875  }
1876 }
1877 
1878 
1880 {
1881  KMMessage *cm = currentMsg();
1882  if ( cm && cm->isBeingParsed() )
1883  return;
1884  TQListViewItem *lvi = currentItem();
1885  if ( lvi && lvi->itemBelow() ) {
1886 
1887  disconnect(this,TQ_SIGNAL(currentChanged(TQListViewItem*)),
1888  this,TQ_SLOT(highlightMessage(TQListViewItem*)));
1889  setCurrentItem( lvi->itemBelow() );
1890  ensureCurrentItemVisible();
1891  setFocus();
1892  connect(this,TQ_SIGNAL(currentChanged(TQListViewItem*)),
1893  this,TQ_SLOT(highlightMessage(TQListViewItem*)));
1894  }
1895 }
1896 
1898 {
1899  KMMessage *cm = currentMsg();
1900  if ( cm && cm->isBeingParsed() )
1901  return;
1902  TQListViewItem *lvi = currentItem();
1903  if ( lvi && lvi->itemAbove() ) {
1904  disconnect(this,TQ_SIGNAL(currentChanged(TQListViewItem*)),
1905  this,TQ_SLOT(highlightMessage(TQListViewItem*)));
1906  setCurrentItem( lvi->itemAbove() );
1907  ensureCurrentItemVisible();
1908  setFocus();
1909  connect(this,TQ_SIGNAL(currentChanged(TQListViewItem*)),
1910  this,TQ_SLOT(highlightMessage(TQListViewItem*)));
1911  }
1912 }
1913 
1915 {
1917  highlightMessage( currentItem() );
1918 }
1919 
1920 //-----------------------------------------------------------------------------
1922  bool & foundUnreadMessage,
1923  bool onlyNew,
1924  bool aDirNext )
1925 {
1926  KMMsgBase* msgBase = 0;
1927  HeaderItem *lastUnread = 0;
1928  /* itemAbove() is _slow_ */
1929  if (aDirNext)
1930  {
1931  while (item) {
1932  msgBase = mFolder->getMsgBase(item->msgId());
1933  if (!msgBase) continue;
1934  if (msgBase->isUnread() || msgBase->isNew())
1935  foundUnreadMessage = true;
1936 
1937  if (!onlyNew && (msgBase->isUnread() || msgBase->isNew())) break;
1938  if (onlyNew && msgBase->isNew()) break;
1939  item = static_cast<HeaderItem*>(item->itemBelow());
1940  }
1941  } else {
1942  HeaderItem *newItem = static_cast<HeaderItem*>(firstChild());
1943  while (newItem)
1944  {
1945  msgBase = mFolder->getMsgBase(newItem->msgId());
1946  if (!msgBase) continue;
1947  if (msgBase->isUnread() || msgBase->isNew())
1948  foundUnreadMessage = true;
1949  if ( ( !onlyNew && (msgBase->isUnread() || msgBase->isNew()) )
1950  || ( onlyNew && msgBase->isNew() ) )
1951  lastUnread = newItem;
1952  if (newItem == item) break;
1953  newItem = static_cast<HeaderItem*>(newItem->itemBelow());
1954  }
1955  item = lastUnread;
1956  }
1957 }
1958 
1959 //-----------------------------------------------------------------------------
1960 int KMHeaders::findUnread(bool aDirNext, int aStartAt, bool onlyNew, bool acceptCurrent)
1961 {
1962  HeaderItem *item, *pitem;
1963  bool foundUnreadMessage = false;
1964 
1965  if (!mFolder) return -1;
1966  if (mFolder->count() <= 0) return -1;
1967 
1968  if ((aStartAt >= 0) && (aStartAt < (int)mItems.size()))
1969  item = mItems[aStartAt];
1970  else {
1971  item = currentHeaderItem();
1972  if (!item) {
1973  if (aDirNext)
1974  item = static_cast<HeaderItem*>(firstChild());
1975  else
1976  item = static_cast<HeaderItem*>(lastChild());
1977  }
1978  if (!item)
1979  return -1;
1980 
1981  if ( !acceptCurrent ) {
1982  if (aDirNext) {
1983  item = static_cast<HeaderItem*>(item->itemBelow());
1984  }
1985  else {
1986  item = static_cast<HeaderItem*>(item->itemAbove());
1987  }
1988  }
1989  }
1990 
1991  pitem = item;
1992 
1993  findUnreadAux( item, foundUnreadMessage, onlyNew, aDirNext );
1994 
1995  // We have found an unread item, but it is not necessary the
1996  // first unread item.
1997  //
1998  // Find the ancestor of the unread item closest to the
1999  // root and recursively sort all of that ancestors children.
2000  if (item) {
2001  TQListViewItem *next = item;
2002  while (next->parent())
2003  next = next->parent();
2004  next = static_cast<HeaderItem*>(next)->firstChildNonConst();
2005  while (next && (next != item))
2006  if (static_cast<HeaderItem*>(next)->firstChildNonConst())
2007  next = next->firstChild();
2008  else if (next->nextSibling())
2009  next = next->nextSibling();
2010  else {
2011  while (next && (next != item)) {
2012  next = next->parent();
2013  if (next == item)
2014  break;
2015  if (next && next->nextSibling()) {
2016  next = next->nextSibling();
2017  break;
2018  }
2019  }
2020  }
2021  }
2022 
2023  item = pitem;
2024 
2025  findUnreadAux( item, foundUnreadMessage, onlyNew, aDirNext );
2026  if (item)
2027  return item->msgId();
2028 
2029 
2030  // A kludge to try to keep the number of unread messages in sync
2031  int unread = mFolder->countUnread();
2032  if (((unread == 0) && foundUnreadMessage) ||
2033  ((unread > 0) && !foundUnreadMessage)) {
2034  mFolder->correctUnreadMsgsCount();
2035  }
2036  return -1;
2037 }
2038 
2039 //-----------------------------------------------------------------------------
2040 bool KMHeaders::nextUnreadMessage(bool acceptCurrent)
2041 {
2042  if ( !mFolder || !mFolder->countUnread() ) return false;
2043  int i = findUnread(true, -1, false, acceptCurrent);
2044  if ( i < 0 && GlobalSettings::self()->loopOnGotoUnread() !=
2045  GlobalSettings::EnumLoopOnGotoUnread::DontLoop )
2046  {
2047  HeaderItem * first = static_cast<HeaderItem*>(firstChild());
2048  if ( first )
2049  i = findUnread(true, first->msgId(), false, acceptCurrent); // from top
2050  }
2051  if ( i < 0 )
2052  return false;
2053  setCurrentMsg(i);
2054  ensureCurrentItemVisible();
2055  return true;
2056 }
2057 
2058 void KMHeaders::ensureCurrentItemVisible()
2059 {
2060  int i = currentItemIndex();
2061  if ((i >= 0) && (i < (int)mItems.size()))
2062  center( contentsX(), itemPos(mItems[i]), 0, 9.0 );
2063 }
2064 
2065 //-----------------------------------------------------------------------------
2067 {
2068  if ( !mFolder || !mFolder->countUnread() ) return false;
2069  int i = findUnread(false);
2070  if ( i < 0 && GlobalSettings::self()->loopOnGotoUnread() !=
2071  GlobalSettings::EnumLoopOnGotoUnread::DontLoop )
2072  {
2073  HeaderItem * last = static_cast<HeaderItem*>(lastItem());
2074  if ( last )
2075  i = findUnread(false, last->msgId() ); // from bottom
2076  }
2077  if ( i < 0 )
2078  return false;
2079  setCurrentMsg(i);
2080  ensureCurrentItemVisible();
2081  return true;
2082 }
2083 
2084 
2085 //-----------------------------------------------------------------------------
2087 {
2088  // This causes Kolab issue 1569 (encrypted mails sometimes not dragable)
2089  // This was introduced in r73594 to fix interference between dnd and
2090  // pinentry, which is no longer reproducable now. However, since the
2091  // original problem was probably a race and might reappear, let's keep
2092  // this workaround in for now and just disable it.
2093 // mMousePressed = false;
2094 }
2095 
2096 
2097 //-----------------------------------------------------------------------------
2099 {
2100  if (currentItem())
2101  ensureItemVisible( currentItem() );
2102 }
2103 
2104 //-----------------------------------------------------------------------------
2105 void KMHeaders::highlightMessage(TQListViewItem* lvi, bool markitread)
2106 {
2107  // shouldnt happen but will crash if it does
2108  if (lvi && !lvi->isSelectable()) return;
2109 
2110  HeaderItem *item = static_cast<HeaderItem*>(lvi);
2111  if (lvi != mPrevCurrent) {
2112  if (mPrevCurrent && mFolder)
2113  {
2114  KMMessage *prevMsg = mFolder->getMsg(mPrevCurrent->msgId());
2115  if (prevMsg && mReaderWindowActive)
2116  {
2117  mFolder->ignoreJobsForMessage(prevMsg);
2118  if (!prevMsg->transferInProgress())
2119  mFolder->unGetMsg(mPrevCurrent->msgId());
2120  }
2121  }
2122  mPrevCurrent = item;
2123  }
2124 
2125  if (!item) {
2126  emit selected( 0 ); return;
2127  }
2128 
2129  int idx = item->msgId();
2130  KMMessage *msg = mFolder->getMsg(idx);
2131  if (mReaderWindowActive && !msg) {
2132  emit selected( 0 );
2133  mPrevCurrent = 0;
2134  return;
2135  }
2136 
2137  BroadcastStatus::instance()->setStatusMsg("");
2138  if (markitread && idx >= 0) setMsgRead(idx);
2139  mItems[idx]->irefresh();
2140  mItems[idx]->repaint();
2141  emit selected( msg );
2143 }
2144 
2145 void KMHeaders::highlightCurrentThread()
2146 {
2147  TQPtrList<TQListViewItem> curThread = currentThread();
2148  TQPtrListIterator<TQListViewItem> it( curThread );
2149 
2150  for ( it.toFirst() ; it.current() ; ++it ) {
2151  TQListViewItem *lvi = *it;
2152  lvi->setSelected( true );
2153  lvi->repaint();
2154  }
2155 }
2156 
2158 {
2159  mDate.reset();
2160  // only reset exactly during minute switch
2161  TQTimer::singleShot( ( 60-TQTime::currentTime().second() ) * 1000,
2162  this, TQ_SLOT( resetCurrentTime() ) );
2163 }
2164 
2165 //-----------------------------------------------------------------------------
2166 void KMHeaders::selectMessage(TQListViewItem* lvi)
2167 {
2168  HeaderItem *item = static_cast<HeaderItem*>(lvi);
2169  if (!item)
2170  return;
2171 
2172  int idx = item->msgId();
2173  KMMessage *msg = mFolder->getMsg(idx);
2174  if (msg && !msg->transferInProgress())
2175  {
2176  emit activated(mFolder->getMsg(idx));
2177  }
2178 
2179 // if (kmkernel->folderIsDraftOrOutbox(mFolder))
2180 // setOpen(lvi, !lvi->isOpen());
2181 }
2182 
2183 
2184 //-----------------------------------------------------------------------------
2185 void KMHeaders::updateMessageList( bool set_selection, bool forceJumpToUnread )
2186 {
2187  mPrevCurrent = 0;
2188  noRepaint = true;
2189  clear();
2190  mItems.resize(0); // will contain deleted pointers
2191  noRepaint = false;
2192  TDEListView::setSorting( mSortCol, !mSortDescending );
2193  if (!mFolder) {
2194  repaint();
2195  return;
2196  }
2197  readSortOrder( set_selection, forceJumpToUnread );
2198  emit messageListUpdated();
2199 }
2200 
2201 
2202 //-----------------------------------------------------------------------------
2203 // KMail Header list selection/navigation description
2204 //
2205 // If the selection state changes the reader window is updated to show the
2206 // current item.
2207 //
2208 // (The selection state of a message or messages can be changed by pressing
2209 // space, or normal/shift/cntrl clicking).
2210 //
2211 // The following keyboard events are supported when the messages headers list
2212 // has focus, Ctrl+Key_Down, Ctrl+Key_Up, Ctrl+Key_Home, Ctrl+Key_End,
2213 // Ctrl+Key_Next, Ctrl+Key_Prior, these events change the current item but do
2214 // not change the selection state.
2215 //
2216 // Exception: When shift selecting either with mouse or key press the reader
2217 // window is updated regardless of whether of not the selection has changed.
2218 void KMHeaders::keyPressEvent( TQKeyEvent * e )
2219 {
2220  bool cntrl = (e->state() & ControlButton );
2221  bool shft = (e->state() & ShiftButton );
2222  TQListViewItem *cur = currentItem();
2223 
2224  if (!e || !firstChild())
2225  return;
2226 
2227  // If no current item, make some first item current when a key is pressed
2228  if (!cur) {
2229  setCurrentItem( firstChild() );
2230  setSelectionAnchor( currentItem() );
2231  return;
2232  }
2233 
2234  // Handle space key press
2235  if (cur->isSelectable() && e->ascii() == ' ' ) {
2236  setSelected( cur, !cur->isSelected() );
2237  highlightMessage( cur, false);
2238  return;
2239  }
2240 
2241  if (cntrl) {
2242  if (!shft)
2243  disconnect(this,TQ_SIGNAL(currentChanged(TQListViewItem*)),
2244  this,TQ_SLOT(highlightMessage(TQListViewItem*)));
2245  switch (e->key()) {
2246  case Key_Down:
2247  case Key_Up:
2248  case Key_Home:
2249  case Key_End:
2250  case Key_Next:
2251  case Key_Prior:
2252  case Key_Escape:
2253  TDEListView::keyPressEvent( e );
2254  }
2255  if (!shft)
2256  connect(this,TQ_SIGNAL(currentChanged(TQListViewItem*)),
2257  this,TQ_SLOT(highlightMessage(TQListViewItem*)));
2258  }
2259 }
2260 
2261 //-----------------------------------------------------------------------------
2262 // Handle RMB press, show pop up menu
2263 void KMHeaders::rightButtonPressed( TQListViewItem *lvi, const TQPoint &, int )
2264 {
2265  if (!lvi)
2266  return;
2267 
2268  if (!(lvi->isSelected())) {
2269  clearSelection();
2270  }
2271  setSelected( lvi, true );
2272  slotRMB();
2273 }
2274 
2275 //-----------------------------------------------------------------------------
2277 {
2278  mPressPos = e->pos();
2279  TQListViewItem *lvi = itemAt( contentsToViewport( e->pos() ));
2280  bool wasSelected = false;
2281  bool rootDecoClicked = false;
2282  if (lvi) {
2283  wasSelected = lvi->isSelected();
2284  rootDecoClicked =
2285  ( mPressPos.x() <= header()->cellPos( header()->mapToActual( 0 ) ) +
2286  treeStepSize() * ( lvi->depth() + ( rootIsDecorated() ? 1 : 0 ) ) + itemMargin() )
2287  && ( mPressPos.x() >= header()->cellPos( header()->mapToActual( 0 ) ) );
2288 
2289  if ( rootDecoClicked ) {
2290  // Check if our item is the parent of a closed thread and if so, if the root
2291  // decoration of the item was clicked (i.e. the +/- sign) which would expand
2292  // the thread. In that case, deselect all children, so opening the thread
2293  // doesn't cause a flicker.
2294  if ( !lvi->isOpen() && lvi->firstChild() ) {
2295  TQListViewItem *nextRoot = lvi->itemBelow();
2296  TQListViewItemIterator it( lvi->firstChild() );
2297  for( ; (*it) != nextRoot; ++it )
2298  (*it)->setSelected( false );
2299  }
2300  }
2301  }
2302 
2303  // let tdelistview do it's thing, expanding/collapsing, selection/deselection
2304  TDEListView::contentsMousePressEvent(e);
2305  /* TQListView's shift-select selects also invisible items. Until that is
2306  fixed, we have to deselect hidden items here manually, so the quick
2307  search doesn't mess things up. */
2308  if ( e->state() & ShiftButton ) {
2309  TQListViewItemIterator it( this, TQListViewItemIterator::Invisible );
2310  while ( it.current() ) {
2311  it.current()->setSelected( false );
2312  ++it;
2313  }
2314  }
2315 
2316  if ( rootDecoClicked ) {
2317  // select the thread's children after closing if the parent is selected
2318  if ( lvi && !lvi->isOpen() && lvi->isSelected() )
2319  setSelected( lvi, true );
2320  }
2321 
2322  if ( lvi && !rootDecoClicked ) {
2323  if ( lvi != currentItem() )
2324  highlightMessage( lvi );
2325  /* Explicitely set selection state. This is necessary because we want to
2326  * also select all children of closed threads when the parent is selected. */
2327 
2328  // unless ctrl mask, set selected if it isn't already
2329  if ( !( e->state() & ControlButton ) && !wasSelected )
2330  setSelected( lvi, true );
2331  // if ctrl mask, toggle selection
2332  if ( e->state() & ControlButton )
2333  setSelected( lvi, !wasSelected );
2334 
2335  if ((e->button() == TQt::LeftButton) )
2336  mMousePressed = true;
2337  }
2338 
2339  // check if we are on a status column and toggle it
2340  if ( lvi && e->button() == TQt::LeftButton && !( e->state() & (ShiftButton | ControlButton | AltButton | MetaButton) ) ) {
2341  bool flagsToggleable = GlobalSettings::self()->allowLocalFlags() || !(mFolder ? mFolder->isReadOnly() : true);
2342  int section = header()->sectionAt( e->pos().x() );
2343  HeaderItem *item = static_cast<HeaderItem*>( lvi );
2344  KMMsgBase *msg = mFolder->getMsgBase(item->msgId());
2345  if ( section == mPaintInfo.flagCol && flagsToggleable ) {
2346  setMsgStatus( KMMsgStatusFlag, true );
2347  } else if ( section == mPaintInfo.importantCol && flagsToggleable ) {
2348  setMsgStatus( KMMsgStatusFlag, true );
2349  } else if ( section == mPaintInfo.todoCol && flagsToggleable ) {
2350  setMsgStatus( KMMsgStatusTodo, true );
2351  } else if ( section == mPaintInfo.watchedIgnoredCol && flagsToggleable ) {
2352  if ( msg->isWatched() || msg->isIgnored() )
2353  setMsgStatus( KMMsgStatusIgnored, true );
2354  else
2355  setMsgStatus( KMMsgStatusWatched, true );
2356  } else if ( section == mPaintInfo.statusCol ) {
2357  if ( msg->isUnread() || msg->isNew() )
2358  setMsgStatus( KMMsgStatusRead );
2359  else
2360  setMsgStatus( KMMsgStatusUnread );
2361  }
2362  }
2363 }
2364 
2365 //-----------------------------------------------------------------------------
2366 void KMHeaders::contentsMouseReleaseEvent(TQMouseEvent* e)
2367 {
2368  if (e->button() != TQt::RightButton)
2369  TDEListView::contentsMouseReleaseEvent(e);
2370 
2371  mMousePressed = false;
2372 }
2373 
2374 //-----------------------------------------------------------------------------
2375 void KMHeaders::contentsMouseMoveEvent( TQMouseEvent* e )
2376 {
2377  if (mMousePressed &&
2378  (e->pos() - mPressPos).manhattanLength() > TDEGlobalSettings::dndEventDelay()) {
2379  mMousePressed = false;
2380  TQListViewItem *item = itemAt( contentsToViewport(mPressPos) );
2381  if ( item ) {
2382  MailList mailList;
2383  unsigned int count = 0;
2384  for( TQListViewItemIterator it(this); it.current(); it++ )
2385  if( it.current()->isSelected() ) {
2386  HeaderItem *item = static_cast<HeaderItem*>(it.current());
2387  KMMsgBase *msg = mFolder->getMsgBase(item->msgId());
2388  // FIXME: msg can be null here which crashes. I think it's a race
2389  // because it's very hard to reproduce. (GS)
2390  MailSummary mailSummary( msg->getMsgSerNum(), msg->msgIdMD5(),
2391  msg->subject(), msg->fromStrip(),
2392  msg->toStrip(), msg->date() );
2393  mailList.append( mailSummary );
2394  ++count;
2395  }
2396  MailListDrag *d = new MailListDrag( mailList, viewport(), new KMTextSource );
2397 
2398  // Set pixmap
2399  TQPixmap pixmap;
2400  if( count == 1 )
2401  pixmap = TQPixmap( DesktopIcon("message", TDEIcon::SizeSmall) );
2402  else
2403  pixmap = TQPixmap( DesktopIcon("application-vnd.tde.tdemultiple", TDEIcon::SizeSmall) );
2404 
2405  // Calculate hotspot (as in Konqueror)
2406  if( !pixmap.isNull() ) {
2407  TQPoint hotspot( pixmap.width() / 2, pixmap.height() / 2 );
2408  d->setPixmap( pixmap, hotspot );
2409  }
2410  if ( mFolder->isReadOnly() )
2411  d->dragCopy();
2412  else
2413  d->drag();
2414  }
2415  }
2416 }
2417 
2418 void KMHeaders::highlightMessage(TQListViewItem* i)
2419 {
2420  highlightMessage( i, false );
2421 }
2422 
2423 //-----------------------------------------------------------------------------
2425 {
2426  if (!topLevelWidget()) return; // safe bet
2427  mOwner->updateMessageActions();
2428 
2429  // check if the user clicked into a status column and only show the respective menues
2430  TQListViewItem *item = itemAt( viewport()->mapFromGlobal( TQCursor::pos() ) );
2431  if ( item ) {
2432  int section = header()->sectionAt( viewportToContents( viewport()->mapFromGlobal( TQCursor::pos() ) ).x() );
2433  if ( section == mPaintInfo.flagCol || section == mPaintInfo.importantCol
2434  || section == mPaintInfo.todoCol || section == mPaintInfo.statusCol ) {
2435  mOwner->statusMenu()->popup( TQCursor::pos() );
2436  return;
2437  }
2438  if ( section == mPaintInfo.watchedIgnoredCol ) {
2439  mOwner->threadStatusMenu()->popup( TQCursor::pos() );
2440  return;
2441  }
2442  }
2443 
2444  TQPopupMenu *menu = new TQPopupMenu(this);
2445 
2446  mMenuToFolder.clear();
2447 
2448  mOwner->updateMessageMenu();
2449 
2450  bool out_folder = kmkernel->folderIsDraftOrOutbox( mFolder );
2451  bool tem_folder = kmkernel->folderIsTemplates( mFolder );
2452  if ( tem_folder ) {
2453  mOwner->useAction()->plug( menu );
2454  } else {
2455  // show most used actions
2456  mOwner->messageActions()->replyMenu()->plug( menu );
2457  mOwner->forwardMenu()->plug( menu );
2458  if( mOwner->sendAgainAction()->isEnabled() ) {
2459  mOwner->sendAgainAction()->plug( menu );
2460  } else {
2461  mOwner->editAction()->plug( menu );
2462  }
2463  }
2464  menu->insertSeparator();
2465 
2466  TQPopupMenu *msgCopyMenu = new TQPopupMenu(menu);
2467  mOwner->folderTree()->folderToPopupMenu( KMFolderTree::CopyMessage, this,
2468  &mMenuToFolder, msgCopyMenu );
2469  menu->insertItem(i18n("&Copy To"), msgCopyMenu);
2470 
2471  if ( !mFolder->canDeleteMessages() ) {
2472  int id = menu->insertItem( i18n("&Move To") );
2473  menu->setItemEnabled( id, false );
2474  } else {
2475  TQPopupMenu *msgMoveMenu = new TQPopupMenu(menu);
2476  mOwner->folderTree()->folderToPopupMenu( KMFolderTree::MoveMessage, this,
2477  &mMenuToFolder, msgMoveMenu );
2478  menu->insertItem(i18n("&Move To"), msgMoveMenu);
2479  }
2480  menu->insertSeparator();
2481  mOwner->statusMenu()->plug( menu ); // Mark Message menu
2482  if ( mOwner->threadStatusMenu()->isEnabled() ) {
2483  mOwner->threadStatusMenu()->plug( menu ); // Mark Thread menu
2484  }
2485 
2486  if ( !out_folder && !tem_folder ) {
2487  menu->insertSeparator();
2488  mOwner->filterMenu()->plug( menu ); // Create Filter menu
2489  mOwner->action( "apply_filter_actions" )->plug( menu );
2490  }
2491 
2492  menu->insertSeparator();
2493  mOwner->printAction()->plug(menu);
2494  mOwner->saveAsAction()->plug(menu);
2495  mOwner->saveAttachmentsAction()->plug(menu);
2496  menu->insertSeparator();
2497  if ( mFolder->isTrash() ) {
2498  mOwner->deleteAction()->plug(menu);
2499  if ( mOwner->trashThreadAction()->isEnabled() )
2500  mOwner->deleteThreadAction()->plug(menu);
2501  } else {
2502  mOwner->trashAction()->plug(menu);
2503  if ( mOwner->trashThreadAction()->isEnabled() )
2504  mOwner->trashThreadAction()->plug(menu);
2505  }
2506  menu->insertSeparator();
2507  mOwner->messageActions()->createTodoAction()->plug( menu );
2508 
2509  TDEAcceleratorManager::manage(menu);
2510  kmkernel->setContextMenuShown( true );
2511  menu->exec(TQCursor::pos(), 0);
2512  kmkernel->setContextMenuShown( false );
2513  delete menu;
2514 }
2515 
2516 //-----------------------------------------------------------------------------
2518 {
2519  HeaderItem *hi = currentHeaderItem();
2520  if (!hi)
2521  return 0;
2522  else
2523  return mFolder->getMsg(hi->msgId());
2524 }
2525 
2526 //-----------------------------------------------------------------------------
2528 {
2529  return static_cast<HeaderItem*>(currentItem());
2530 }
2531 
2532 //-----------------------------------------------------------------------------
2534 {
2535  HeaderItem* item = currentHeaderItem();
2536  if (item)
2537  return item->msgId();
2538  else
2539  return -1;
2540 }
2541 
2542 //-----------------------------------------------------------------------------
2544 {
2545  if (!mFolder->isOpened()) setFolder(mFolder);
2546 
2547  if ((msgIdx >= 0) && (msgIdx < (int)mItems.size())) {
2548  clearSelection();
2549  bool unchanged = (currentItem() == mItems[msgIdx]);
2550  setCurrentItem( mItems[msgIdx] );
2551  setSelected( mItems[msgIdx], true );
2552  setSelectionAnchor( currentItem() );
2553  if (unchanged)
2554  highlightMessage( mItems[msgIdx], false);
2556  }
2557 }
2558 
2559 //-----------------------------------------------------------------------------
2561 {
2562  HeaderItem *item = static_cast<HeaderItem*>( itemAt( TQPoint( 1, 1 ) ) );
2563  if ( item )
2564  return item->msgId();
2565  else
2566  return -1;
2567 }
2568 
2569 //-----------------------------------------------------------------------------
2571 {
2572  if ( aMsgIdx < 0 || static_cast<unsigned int>( aMsgIdx ) >= mItems.size() )
2573  return;
2574  const TQListViewItem * const item = mItems[aMsgIdx];
2575  if ( item )
2576  setContentsPos( 0, itemPos( item ) );
2577 }
2578 
2579 //-----------------------------------------------------------------------------
2580 void KMHeaders::setNestedOverride( bool override )
2581 {
2582  mSortInfo.dirty = true;
2583  mNestedOverride = override;
2584  setRootIsDecorated( nestingPolicy != AlwaysOpen
2585  && isThreaded() );
2586  TQString sortFile = mFolder->indexLocation() + ".sorted";
2587  unlink(TQFile::encodeName(sortFile));
2588  reset();
2589 }
2590 
2591 //-----------------------------------------------------------------------------
2592 void KMHeaders::setSubjectThreading( bool aSubjThreading )
2593 {
2594  mSortInfo.dirty = true;
2595  mSubjThreading = aSubjThreading;
2596  TQString sortFile = mFolder->indexLocation() + ".sorted";
2597  unlink(TQFile::encodeName(sortFile));
2598  reset();
2599 }
2600 
2601 //-----------------------------------------------------------------------------
2602 void KMHeaders::setOpen( TQListViewItem *item, bool open )
2603 {
2604  if ((nestingPolicy != AlwaysOpen)|| open)
2605  ((HeaderItem*)item)->setOpenRecursive( open );
2606 }
2607 
2608 //-----------------------------------------------------------------------------
2609 const KMMsgBase* KMHeaders::getMsgBaseForItem( const TQListViewItem *item ) const
2610 {
2611  const HeaderItem *hi = static_cast<const HeaderItem *> ( item );
2612  return mFolder->getMsgBase( hi->msgId() );
2613 }
2614 
2615 //-----------------------------------------------------------------------------
2616 void KMHeaders::setSorting( int column, bool ascending )
2617 {
2618  if ( mIgnoreSortOrderChanges )
2619  return;
2620 
2621  if (column != -1) {
2622  // carsten: really needed?
2623 // if (column != mSortCol)
2624 // setColumnText( mSortCol, TQIconSet( TQPixmap()), columnText( mSortCol ));
2625  if(mSortInfo.dirty || column != mSortInfo.column || ascending != mSortInfo.ascending) { //dirtied
2626  TQObject::disconnect(header(), TQ_SIGNAL(clicked(int)), this, TQ_SLOT(dirtySortOrder(int)));
2627  mSortInfo.dirty = true;
2628  }
2629 
2630  assert(column >= 0);
2631  mSortCol = column;
2632  mSortDescending = !ascending;
2633 
2634  if (!ascending && (column == mPaintInfo.dateCol))
2635  mPaintInfo.orderOfArrival = !mPaintInfo.orderOfArrival;
2636 
2637  if (!ascending && (column == mPaintInfo.subCol))
2638  mPaintInfo.status = !mPaintInfo.status;
2639 
2640  TQString colText = i18n( "Date" );
2641  if (mPaintInfo.orderOfArrival)
2642  colText = i18n( "Order of Arrival" );
2643  setColumnText( mPaintInfo.dateCol, colText);
2644 
2645  colText = i18n( "Subject" );
2646  if (mPaintInfo.status)
2647  colText = colText + i18n( " (Status)" );
2648  setColumnText( mPaintInfo.subCol, colText);
2649  }
2650  TDEListView::setSorting( column, ascending );
2651  ensureCurrentItemVisible();
2652  // Make sure the config and .sorted file are updated, otherwise stale info
2653  // is read on new imap mail. ( folder->folderComplete() -> readSortOrder() ).
2654  if ( mFolder ) {
2656  writeSortOrder();
2657  }
2658 }
2659 
2660 //Flatten the list and write it to disk
2661 static void internalWriteItem(FILE *sortStream, KMFolder *folder, int msgid,
2662  int parent_id, TQString key,
2663  bool update_discover=true)
2664 {
2665  unsigned long msgSerNum;
2666  unsigned long parentSerNum;
2667  msgSerNum = KMMsgDict::instance()->getMsgSerNum( folder, msgid );
2668  if (parent_id >= 0)
2669  parentSerNum = KMMsgDict::instance()->getMsgSerNum( folder, parent_id ) + KMAIL_RESERVED;
2670  else
2671  parentSerNum = (unsigned long)(parent_id + KMAIL_RESERVED);
2672 
2673  fwrite(&msgSerNum, sizeof(msgSerNum), 1, sortStream);
2674  fwrite(&parentSerNum, sizeof(parentSerNum), 1, sortStream);
2675  TQ_INT32 len = key.length() * sizeof(TQChar);
2676  fwrite(&len, sizeof(len), 1, sortStream);
2677  if (len)
2678  fwrite(key.unicode(), TQMIN(len, KMAIL_MAX_KEY_LEN), 1, sortStream);
2679 
2680  if (update_discover) {
2681  //update the discovered change count
2682  TQ_INT32 discovered_count = 0;
2683  fseek(sortStream, KMAIL_MAGIC_HEADER_OFFSET + 20, SEEK_SET);
2684  fread(&discovered_count, sizeof(discovered_count), 1, sortStream);
2685  discovered_count++;
2686  fseek(sortStream, KMAIL_MAGIC_HEADER_OFFSET + 20, SEEK_SET);
2687  fwrite(&discovered_count, sizeof(discovered_count), 1, sortStream);
2688  }
2689 }
2690 
2692 {
2693  mSortCacheItems.clear(); //autoDelete is true
2694  mSubjectLists.clear();
2695  mImperfectlyThreadedList.clear();
2696  mPrevCurrent = 0;
2697  emit selected(0);
2698 }
2699 
2700 
2702 {
2703  if ( mFolder->open( "kmheaders" ) == 0 )
2704  updateMessageList();
2705  else
2706  folderCleared();
2707 }
2708 
2709 bool KMHeaders::writeSortOrder()
2710 {
2711  TQString sortFile = KMAIL_SORT_FILE(mFolder);
2712 
2713  if (!mSortInfo.dirty) {
2714  struct stat stat_tmp;
2715  if(stat(TQFile::encodeName(sortFile), &stat_tmp) == -1) {
2716  mSortInfo.dirty = true;
2717  }
2718  }
2719  if (mSortInfo.dirty) {
2720  if (!mFolder->count()) {
2721  // Folder is empty now, remove the sort file.
2722  unlink(TQFile::encodeName(sortFile));
2723  return true;
2724  }
2725  TQString tempName = sortFile + ".temp";
2726  unlink(TQFile::encodeName(tempName));
2727  FILE *sortStream = fopen(TQFile::encodeName(tempName), "w");
2728  if (!sortStream)
2729  return false;
2730 
2731  mSortInfo.ascending = !mSortDescending;
2732  mSortInfo.dirty = false;
2733  mSortInfo.column = mSortCol;
2734  fprintf(sortStream, KMAIL_SORT_HEADER, KMAIL_SORT_VERSION);
2735  //magic header information
2736  TQ_INT32 byteOrder = 0x12345678;
2737  TQ_INT32 column = mSortCol;
2738  TQ_INT32 ascending= !mSortDescending;
2739  TQ_INT32 threaded = isThreaded();
2740  TQ_INT32 appended=0;
2741  TQ_INT32 discovered_count = 0;
2742  TQ_INT32 sorted_count=0;
2743  fwrite(&byteOrder, sizeof(byteOrder), 1, sortStream);
2744  fwrite(&column, sizeof(column), 1, sortStream);
2745  fwrite(&ascending, sizeof(ascending), 1, sortStream);
2746  fwrite(&threaded, sizeof(threaded), 1, sortStream);
2747  fwrite(&appended, sizeof(appended), 1, sortStream);
2748  fwrite(&discovered_count, sizeof(discovered_count), 1, sortStream);
2749  fwrite(&sorted_count, sizeof(sorted_count), 1, sortStream);
2750 
2751  TQPtrStack<HeaderItem> items;
2752  {
2753  TQPtrStack<TQListViewItem> s;
2754  for (TQListViewItem * i = firstChild(); i; ) {
2755  items.push((HeaderItem *)i);
2756  if ( i->firstChild() ) {
2757  s.push( i );
2758  i = i->firstChild();
2759  } else if( i->nextSibling()) {
2760  i = i->nextSibling();
2761  } else {
2762  for(i=0; !i && s.count(); i = s.pop()->nextSibling())
2763  ;
2764  }
2765  }
2766  }
2767 
2768  KMMsgBase *kmb;
2769  while(HeaderItem *i = items.pop()) {
2770  int parent_id = -1; //no parent, top level
2771  if (threaded) {
2772  kmb = mFolder->getMsgBase( i->msgId() );
2773  assert(kmb); // I have seen 0L come out of this, called from
2774  // KMHeaders::setFolder(0xgoodpointer, false);
2775  // I see this crash too. after rebuilding a broken index on a dimap folder. always
2776  TQString replymd5 = kmb->replyToIdMD5();
2777  TQString replyToAuxId = kmb->replyToAuxIdMD5();
2778  SortCacheItem *p = NULL;
2779  if(!replymd5.isEmpty())
2780  p = mSortCacheItems[replymd5];
2781 
2782  if (p)
2783  parent_id = p->id();
2784  // We now have either found a parent, or set it to -1, which means that
2785  // it will be reevaluated when a message is added, for example. If there
2786  // is no replyToId and no replyToAuxId and the message is not prefixed,
2787  // this message is top level, and will always be, so no need to
2788  // reevaluate it.
2789  if (replymd5.isEmpty()
2790  && replyToAuxId.isEmpty()
2791  && !kmb->subjectIsPrefixed() )
2792  parent_id = -2;
2793  // FIXME also mark messages with -1 as -2 a certain amount of time after
2794  // their arrival, since it becomes very unlikely that a new parent for
2795  // them will show up. (Ingo suggests a month.) -till
2796  }
2797  internalWriteItem(sortStream, mFolder, i->msgId(), parent_id,
2798  i->key(mSortCol, !mSortDescending), false);
2799  //double check for magic headers
2800  sorted_count++;
2801  }
2802 
2803  //magic header twice, case they've changed
2804  fseek(sortStream, KMAIL_MAGIC_HEADER_OFFSET, SEEK_SET);
2805  fwrite(&byteOrder, sizeof(byteOrder), 1, sortStream);
2806  fwrite(&column, sizeof(column), 1, sortStream);
2807  fwrite(&ascending, sizeof(ascending), 1, sortStream);
2808  fwrite(&threaded, sizeof(threaded), 1, sortStream);
2809  fwrite(&appended, sizeof(appended), 1, sortStream);
2810  fwrite(&discovered_count, sizeof(discovered_count), 1, sortStream);
2811  fwrite(&sorted_count, sizeof(sorted_count), 1, sortStream);
2812  if (sortStream && ferror(sortStream)) {
2813  fclose(sortStream);
2814  unlink(TQFile::encodeName(sortFile));
2815  kdWarning(5006) << "Error: Failure modifying " << sortFile << " (No space left on device?)" << endl;
2816  kdWarning(5006) << __FILE__ << ":" << __LINE__ << endl;
2817  kmkernel->emergencyExit( i18n("Failure modifying %1\n(No space left on device?)").arg( sortFile ));
2818  }
2819  fclose(sortStream);
2820  ::rename(TQFile::encodeName(tempName), TQFile::encodeName(sortFile));
2821  }
2822 
2823  return true;
2824 }
2825 
2826 void KMHeaders::appendItemToSortFile(HeaderItem *khi)
2827 {
2828  TQString sortFile = KMAIL_SORT_FILE(mFolder);
2829  if(FILE *sortStream = fopen(TQFile::encodeName(sortFile), "r+")) {
2830  int parent_id = -1; //no parent, top level
2831 
2832  if (isThreaded()) {
2833  SortCacheItem *sci = khi->sortCacheItem();
2834  KMMsgBase *kmb = mFolder->getMsgBase( khi->msgId() );
2835  if(sci->parent() && !sci->isImperfectlyThreaded())
2836  parent_id = sci->parent()->id();
2837  else if(kmb->replyToIdMD5().isEmpty()
2838  && kmb->replyToAuxIdMD5().isEmpty()
2839  && !kmb->subjectIsPrefixed())
2840  parent_id = -2;
2841  }
2842 
2843  internalWriteItem(sortStream, mFolder, khi->msgId(), parent_id,
2844  khi->key(mSortCol, !mSortDescending), false);
2845 
2846  //update the appended flag FIXME obsolete?
2847  TQ_INT32 appended = 1;
2848  fseek(sortStream, KMAIL_MAGIC_HEADER_OFFSET + 16, SEEK_SET);
2849  fwrite(&appended, sizeof(appended), 1, sortStream);
2850  fseek(sortStream, KMAIL_MAGIC_HEADER_OFFSET + 16, SEEK_SET);
2851 
2852  if (sortStream && ferror(sortStream)) {
2853  fclose(sortStream);
2854  unlink(TQFile::encodeName(sortFile));
2855  kdWarning(5006) << "Error: Failure modifying " << sortFile << " (No space left on device?)" << endl;
2856  kdWarning(5006) << __FILE__ << ":" << __LINE__ << endl;
2857  kmkernel->emergencyExit( i18n("Failure modifying %1\n(No space left on device?)").arg( sortFile ));
2858  }
2859  fclose(sortStream);
2860  } else {
2861  mSortInfo.dirty = true;
2862  }
2863 }
2864 
2866 {
2867  mSortInfo.dirty = true;
2868  TQObject::disconnect(header(), TQ_SIGNAL(clicked(int)), this, TQ_SLOT(dirtySortOrder(int)));
2869  setSorting(column, mSortInfo.column == column ? !mSortInfo.ascending : true);
2870 }
2871 
2872 // -----------------
2873 void SortCacheItem::updateSortFile( FILE *sortStream, KMFolder *folder,
2874  bool waiting_for_parent, bool update_discover)
2875 {
2876  if(mSortOffset == -1) {
2877  fseek(sortStream, 0, SEEK_END);
2878  mSortOffset = ftell(sortStream);
2879  } else {
2880  fseek(sortStream, mSortOffset, SEEK_SET);
2881  }
2882 
2883  int parent_id = -1;
2884  if(!waiting_for_parent) {
2885  if(mParent && !isImperfectlyThreaded())
2886  parent_id = mParent->id();
2887  }
2888  internalWriteItem(sortStream, folder, mId, parent_id, mKey, update_discover);
2889 }
2890 
2891 static bool compare_ascending = false;
2892 static bool compare_toplevel = true;
2893 static int compare_SortCacheItem(const void *s1, const void *s2)
2894 {
2895  if ( !s1 || !s2 )
2896  return 0;
2897  SortCacheItem **b1 = (SortCacheItem **)s1;
2898  SortCacheItem **b2 = (SortCacheItem **)s2;
2899  int ret = (*b1)->key().compare((*b2)->key());
2900  if(compare_ascending || !compare_toplevel)
2901  ret = -ret;
2902  return ret;
2903 }
2904 
2905 // Debugging helpers
2906 void KMHeaders::printSubjectThreadingTree()
2907 {
2908  TQDictIterator< TQPtrList< SortCacheItem > > it ( mSubjectLists );
2909  kdDebug(5006) << "SubjectThreading tree: " << endl;
2910  for( ; it.current(); ++it ) {
2911  TQPtrList<SortCacheItem> list = *( it.current() );
2912  TQPtrListIterator<SortCacheItem> it2( list ) ;
2913  kdDebug(5006) << "Subject MD5: " << it.currentKey() << " list: " << endl;
2914  for( ; it2.current(); ++it2 ) {
2915  SortCacheItem *sci = it2.current();
2916  kdDebug(5006) << " item:" << sci << " sci id: " << sci->id() << endl;
2917  }
2918  }
2919  kdDebug(5006) << endl;
2920 }
2921 
2922 void KMHeaders::printThreadingTree()
2923 {
2924  kdDebug(5006) << "Threading tree: " << endl;
2925  TQDictIterator<SortCacheItem> it( mSortCacheItems );
2926  kdDebug(5006) << endl;
2927  for( ; it.current(); ++it ) {
2928  SortCacheItem *sci = it.current();
2929  kdDebug(5006) << "MsgId MD5: " << it.currentKey() << " message id: " << sci->id() << endl;
2930  }
2931  for (int i = 0; i < (int)mItems.size(); ++i) {
2932  HeaderItem *item = mItems[i];
2933  int parentCacheId = item->sortCacheItem()->parent()?item->sortCacheItem()->parent()->id():0;
2934  kdDebug( 5006 ) << "SortCacheItem: " << item->sortCacheItem()->id() << " parent: " << parentCacheId << endl;
2935  kdDebug( 5006 ) << "Item: " << item << " sortCache: " << item->sortCacheItem() << " parent: " << item->sortCacheItem()->parent() << endl;
2936  }
2937  kdDebug(5006) << endl;
2938 }
2939 
2940 // -------------------------------------
2941 
2942 void KMHeaders::buildThreadingTree( TQMemArray<SortCacheItem *> sortCache )
2943 {
2944  mSortCacheItems.clear();
2945  mSortCacheItems.resize( mFolder->count() * 2 );
2946 
2947  // build a dict of all message id's
2948  for(int x = 0; x < mFolder->count(); x++) {
2949  KMMsgBase *mi = mFolder->getMsgBase(x);
2950  TQString md5 = mi->msgIdMD5();
2951  if(!md5.isEmpty())
2952  mSortCacheItems.replace(md5, sortCache[x]);
2953  }
2954 }
2955 
2956 
2957 void KMHeaders::buildSubjectThreadingTree( TQMemArray<SortCacheItem *> sortCache )
2958 {
2959  mSubjectLists.clear(); // autoDelete is true
2960  mSubjectLists.resize( mFolder->count() * 2 );
2961 
2962  for(int x = 0; x < mFolder->count(); x++) {
2963  // Only a lot items that are now toplevel
2964  if ( sortCache[x]->parent()
2965  && sortCache[x]->parent()->id() != -666 ) continue;
2966  KMMsgBase *mi = mFolder->getMsgBase(x);
2967  TQString subjMD5 = mi->strippedSubjectMD5();
2968  if (subjMD5.isEmpty()) {
2969  mFolder->getMsgBase(x)->initStrippedSubjectMD5();
2970  subjMD5 = mFolder->getMsgBase(x)->strippedSubjectMD5();
2971  }
2972  if( subjMD5.isEmpty() ) continue;
2973 
2974  /* For each subject, keep a list of items with that subject
2975  * (stripped of prefixes) sorted by date. */
2976  if (!mSubjectLists.find(subjMD5))
2977  mSubjectLists.insert(subjMD5, new TQPtrList<SortCacheItem>());
2978  /* Insertion sort by date. These lists are expected to be very small.
2979  * Also, since the messages are roughly ordered by date in the store,
2980  * they should mostly be prepended at the very start, so insertion is
2981  * cheap. */
2982  int p=0;
2983  for (TQPtrListIterator<SortCacheItem> it(*mSubjectLists[subjMD5]);
2984  it.current(); ++it) {
2985  KMMsgBase *mb = mFolder->getMsgBase((*it)->id());
2986  if ( mb->date() < mi->date())
2987  break;
2988  p++;
2989  }
2990  mSubjectLists[subjMD5]->insert( p, sortCache[x]);
2991  sortCache[x]->setSubjectThreadingList( mSubjectLists[subjMD5] );
2992  }
2993 }
2994 
2995 
2996 SortCacheItem* KMHeaders::findParent(SortCacheItem *item)
2997 {
2998  SortCacheItem *parent = NULL;
2999  if (!item) return parent;
3000  KMMsgBase *msg = mFolder->getMsgBase(item->id());
3001  TQString replyToIdMD5 = msg->replyToIdMD5();
3002  item->setImperfectlyThreaded(true);
3003  /* First, try if the message our Reply-To header points to
3004  * is available to thread below. */
3005  if(!replyToIdMD5.isEmpty()) {
3006  parent = mSortCacheItems[replyToIdMD5];
3007  if (parent)
3008  item->setImperfectlyThreaded(false);
3009  }
3010  if (!parent) {
3011  // If we dont have a replyToId, or if we have one and the
3012  // corresponding message is not in this folder, as happens
3013  // if you keep your outgoing messages in an OUTBOX, for
3014  // example, try the list of references, because the second
3015  // to last will likely be in this folder. replyToAuxIdMD5
3016  // contains the second to last one.
3017  TQString ref = msg->replyToAuxIdMD5();
3018  if (!ref.isEmpty())
3019  parent = mSortCacheItems[ref];
3020  }
3021  return parent;
3022 }
3023 
3024 SortCacheItem* KMHeaders::findParentBySubject(SortCacheItem *item)
3025 {
3026  SortCacheItem *parent = NULL;
3027  if (!item) return parent;
3028 
3029  KMMsgBase *msg = mFolder->getMsgBase(item->id());
3030 
3031  // Let's try by subject, but only if the subject is prefixed.
3032  // This is necessary to make for example cvs commit mailing lists
3033  // work as expected without having to turn threading off alltogether.
3034  if (!msg->subjectIsPrefixed())
3035  return parent;
3036 
3037  TQString replyToIdMD5 = msg->replyToIdMD5();
3038  TQString subjMD5 = msg->strippedSubjectMD5();
3039  if (!subjMD5.isEmpty() && mSubjectLists[subjMD5]) {
3040  /* Iterate over the list of potential parents with the same
3041  * subject, and take the closest one by date. */
3042  for (TQPtrListIterator<SortCacheItem> it2(*mSubjectLists[subjMD5]);
3043  it2.current(); ++it2) {
3044  KMMsgBase *mb = mFolder->getMsgBase((*it2)->id());
3045  if ( !mb ) return parent;
3046  // make sure it's not ourselves
3047  if ( item == (*it2) ) continue;
3048  int delta = msg->date() - mb->date();
3049  // delta == 0 is not allowed, to avoid circular threading
3050  // with duplicates.
3051  if (delta > 0 ) {
3052  // Don't use parents more than 6 weeks older than us.
3053  if (delta < 3628899)
3054  parent = (*it2);
3055  break;
3056  }
3057  }
3058  }
3059  return parent;
3060 }
3061 
3062 bool KMHeaders::readSortOrder( bool set_selection, bool forceJumpToUnread )
3063 {
3064  if (!mFolder->isOpened()) mFolder->open("kmheaders");
3065 
3066  //all cases
3067  TQ_INT32 column, ascending, threaded, discovered_count, sorted_count, appended;
3068  TQ_INT32 deleted_count = 0;
3069  bool unread_exists = false;
3070  bool jumpToUnread = (GlobalSettings::self()->actionEnterFolder() ==
3071  GlobalSettings::EnumActionEnterFolder::SelectFirstUnreadNew) ||
3072  forceJumpToUnread;
3073  HeaderItem *oldestItem = 0;
3074  HeaderItem *newestItem = 0;
3075  TQMemArray<SortCacheItem *> sortCache(mFolder->count());
3076  bool error = false;
3077 
3078  //threaded cases
3079  TQPtrList<SortCacheItem> unparented;
3080  mImperfectlyThreadedList.clear();
3081 
3082  //cleanup
3083  mItems.fill( 0, mFolder->count() );
3084  sortCache.fill( 0 );
3085 
3086  mRoot->clearChildren();
3087 
3088  TQString sortFile = KMAIL_SORT_FILE(mFolder);
3089  FILE *sortStream = fopen(TQFile::encodeName(sortFile), "r+");
3090  mSortInfo.fakeSort = 0;
3091 
3092  if(sortStream) {
3093  mSortInfo.fakeSort = 1;
3094  int version = 0;
3095  if (fscanf(sortStream, KMAIL_SORT_HEADER, &version) != 1)
3096  version = -1;
3097  if(version == KMAIL_SORT_VERSION) {
3098  TQ_INT32 byteOrder = 0;
3099  fread(&byteOrder, sizeof(byteOrder), 1, sortStream);
3100  if (byteOrder == 0x12345678)
3101  {
3102  fread(&column, sizeof(column), 1, sortStream);
3103  fread(&ascending, sizeof(ascending), 1, sortStream);
3104  fread(&threaded, sizeof(threaded), 1, sortStream);
3105  fread(&appended, sizeof(appended), 1, sortStream);
3106  fread(&discovered_count, sizeof(discovered_count), 1, sortStream);
3107  fread(&sorted_count, sizeof(sorted_count), 1, sortStream);
3108 
3109  //Hackyness to work around qlistview problems
3110  TDEListView::setSorting(-1);
3111  header()->setSortIndicator(column, ascending);
3112  TQObject::connect(header(), TQ_SIGNAL(clicked(int)), this, TQ_SLOT(dirtySortOrder(int)));
3113  //setup mSortInfo here now, as above may change it
3114  mSortInfo.dirty = false;
3115  mSortInfo.column = (short)column;
3116  mSortInfo.ascending = (compare_ascending = ascending);
3117 
3118  SortCacheItem *item;
3119  unsigned long serNum, parentSerNum;
3120  int id, len, parent, x;
3121  TQChar *tmp_qchar = 0;
3122  int tmp_qchar_len = 0;
3123  const int mFolderCount = mFolder->count();
3124  TQString key;
3125 
3126  CREATE_TIMER(parse);
3127  START_TIMER(parse);
3128  for(x = 0; !feof(sortStream) && !error; x++) {
3129  off_t offset = ftell(sortStream);
3130  KMFolder *folder;
3131  //parse
3132  if(!fread(&serNum, sizeof(serNum), 1, sortStream) || //short read means to end
3133  !fread(&parentSerNum, sizeof(parentSerNum), 1, sortStream) ||
3134  !fread(&len, sizeof(len), 1, sortStream)) {
3135  break;
3136  }
3137  if ((len < 0) || (len > KMAIL_MAX_KEY_LEN)) {
3138  kdDebug(5006) << "Whoa.2! len " << len << " " << __FILE__ << ":" << __LINE__ << endl;
3139  error = true;
3140  continue;
3141  }
3142  if(len) {
3143  if(len > tmp_qchar_len) {
3144  tmp_qchar = (TQChar *)realloc(tmp_qchar, len);
3145  tmp_qchar_len = len;
3146  }
3147  if(!fread(tmp_qchar, len, 1, sortStream))
3148  break;
3149  key = TQString(tmp_qchar, len / 2);
3150  } else {
3151  key = TQString(""); //yuck
3152  }
3153 
3154  KMMsgDict::instance()->getLocation(serNum, &folder, &id);
3155  if (folder != mFolder) {
3156  ++deleted_count;
3157  continue;
3158  }
3159  if (parentSerNum < KMAIL_RESERVED) {
3160  parent = (int)parentSerNum - KMAIL_RESERVED;
3161  } else {
3162  KMMsgDict::instance()->getLocation(parentSerNum - KMAIL_RESERVED, &folder, &parent);
3163  if (folder != mFolder)
3164  parent = -1;
3165  }
3166  if ((id < 0) || (id >= mFolderCount) ||
3167  (parent < -2) || (parent >= mFolderCount)) { // sanity checking
3168  kdDebug(5006) << "Whoa.1! " << __FILE__ << ":" << __LINE__ << endl;
3169  error = true;
3170  continue;
3171  }
3172 
3173  if ((item=sortCache[id])) {
3174  if (item->id() != -1) {
3175  kdDebug(5006) << "Whoa.3! " << __FILE__ << ":" << __LINE__ << endl;
3176  error = true;
3177  continue;
3178  }
3179  item->setKey(key);
3180  item->setId(id);
3181  item->setOffset(offset);
3182  } else {
3183  item = sortCache[id] = new SortCacheItem(id, key, offset);
3184  }
3185  if (threaded && parent != -2) {
3186  if(parent == -1) {
3187  unparented.append(item);
3188  mRoot->addUnsortedChild(item);
3189  } else {
3190  if( ! sortCache[parent] ) {
3191  sortCache[parent] = new SortCacheItem;
3192  }
3193  sortCache[parent]->addUnsortedChild(item);
3194  }
3195  } else {
3196  if(x < sorted_count )
3197  mRoot->addSortedChild(item);
3198  else {
3199  mRoot->addUnsortedChild(item);
3200  }
3201  }
3202  }
3203  if (error || (x != sorted_count + discovered_count)) {// sanity check
3204  kdDebug(5006) << endl << "Whoa: x " << x << ", sorted_count " << sorted_count << ", discovered_count " << discovered_count << ", count " << mFolder->count() << endl << endl;
3205  fclose(sortStream);
3206  sortStream = 0;
3207  }
3208 
3209  if(tmp_qchar)
3210  free(tmp_qchar);
3211  END_TIMER(parse);
3212  SHOW_TIMER(parse);
3213  }
3214  else {
3215  fclose(sortStream);
3216  sortStream = 0;
3217  }
3218  } else {
3219  fclose(sortStream);
3220  sortStream = 0;
3221  }
3222  }
3223 
3224  if (!sortStream) {
3225  mSortInfo.dirty = true;
3226  mSortInfo.column = column = mSortCol;
3227  mSortInfo.ascending = ascending = !mSortDescending;
3228  threaded = (isThreaded());
3229  sorted_count = discovered_count = appended = 0;
3230  TDEListView::setSorting( mSortCol, !mSortDescending );
3231  }
3232  //fill in empty holes
3233  if((sorted_count + discovered_count - deleted_count) < mFolder->count()) {
3234  CREATE_TIMER(holes);
3235  START_TIMER(holes);
3236  KMMsgBase *msg = 0;
3237  for(int x = 0; x < mFolder->count(); x++) {
3238  if((!sortCache[x] || (sortCache[x]->id() < 0)) && (msg=mFolder->getMsgBase(x))) {
3239  int sortOrder = column;
3240  if (mPaintInfo.orderOfArrival)
3241  sortOrder |= (1 << 6);
3242  if (mPaintInfo.status)
3243  sortOrder |= (1 << 5);
3244  sortCache[x] = new SortCacheItem(
3245  x, HeaderItem::generate_key( this, msg, &mPaintInfo, sortOrder ));
3246  if(threaded)
3247  unparented.append(sortCache[x]);
3248  else
3249  mRoot->addUnsortedChild(sortCache[x]);
3250  if(sortStream)
3251  sortCache[x]->updateSortFile(sortStream, mFolder, true, true);
3252  discovered_count++;
3253  appended = 1;
3254  }
3255  }
3256  END_TIMER(holes);
3257  SHOW_TIMER(holes);
3258  }
3259 
3260  // Make sure we've placed everything in parent/child relationship. All
3261  // messages with a parent id of -1 in the sort file are reevaluated here.
3262  if (threaded) buildThreadingTree( sortCache );
3263  TQPtrList<SortCacheItem> toBeSubjThreaded;
3264 
3265  if (threaded && !unparented.isEmpty()) {
3266  CREATE_TIMER(reparent);
3267  START_TIMER(reparent);
3268 
3269  for(TQPtrListIterator<SortCacheItem> it(unparented); it.current(); ++it) {
3270  SortCacheItem *item = (*it);
3271  SortCacheItem *parent = findParent( item );
3272  // If we have a parent, make sure it's not ourselves
3273  if ( parent && (parent != (*it)) ) {
3274  parent->addUnsortedChild((*it));
3275  if(sortStream)
3276  (*it)->updateSortFile(sortStream, mFolder);
3277  } else {
3278  // if we will attempt subject threading, add to the list,
3279  // otherwise to the root with them
3280  if (mSubjThreading)
3281  toBeSubjThreaded.append((*it));
3282  else
3283  mRoot->addUnsortedChild((*it));
3284  }
3285  }
3286 
3287  if (mSubjThreading) {
3288  buildSubjectThreadingTree( sortCache );
3289  for(TQPtrListIterator<SortCacheItem> it(toBeSubjThreaded); it.current(); ++it) {
3290  SortCacheItem *item = (*it);
3291  SortCacheItem *parent = findParentBySubject( item );
3292 
3293  if ( parent ) {
3294  parent->addUnsortedChild((*it));
3295  if(sortStream)
3296  (*it)->updateSortFile(sortStream, mFolder);
3297  } else {
3298  //oh well we tried, to the root with you!
3299  mRoot->addUnsortedChild((*it));
3300  }
3301  }
3302  }
3303  END_TIMER(reparent);
3304  SHOW_TIMER(reparent);
3305  }
3306  //create headeritems
3307  CREATE_TIMER(header_creation);
3308  START_TIMER(header_creation);
3309  HeaderItem *khi;
3310  SortCacheItem *i, *new_kci;
3311  TQPtrQueue<SortCacheItem> s;
3312  s.enqueue(mRoot);
3313  compare_toplevel = true;
3314  do {
3315  i = s.dequeue();
3316  const TQPtrList<SortCacheItem> *sorted = i->sortedChildren();
3317  int unsorted_count, unsorted_off=0;
3318  SortCacheItem **unsorted = i->unsortedChildren(unsorted_count);
3319  if(unsorted)
3320  qsort(unsorted, unsorted_count, sizeof(SortCacheItem *), //sort
3321  compare_SortCacheItem);
3322 
3323  /* The sorted list now contains all sorted children of this item, while
3324  * the (aptly named) unsorted array contains all as of yet unsorted
3325  * ones. It has just been qsorted, so it is in itself sorted. These two
3326  * sorted lists are now merged into one. */
3327  for(TQPtrListIterator<SortCacheItem> it(*sorted);
3328  (unsorted && unsorted_off < unsorted_count) || it.current(); ) {
3329  /* As long as we have something in the sorted list and there is
3330  nothing unsorted left, use the item from the sorted list. Also
3331  if we are sorting descendingly and the sorted item is supposed
3332  to be sorted before the unsorted one do so. In the ascending
3333  case we invert the logic for non top level items. */
3334  if( it.current() &&
3335  ( !unsorted || unsorted_off >= unsorted_count
3336  ||
3337  ( ( !ascending || (ascending && !compare_toplevel) )
3338  && (*it)->key() < unsorted[unsorted_off]->key() )
3339  ||
3340  ( ascending && (*it)->key() >= unsorted[unsorted_off]->key() )
3341  )
3342  )
3343  {
3344  new_kci = (*it);
3345  ++it;
3346  } else {
3347  /* Otherwise use the next item of the unsorted list */
3348  new_kci = unsorted[unsorted_off++];
3349  }
3350  if(new_kci->item() || new_kci->parent() != i) //could happen if you reparent
3351  continue;
3352 
3353  if(threaded && i->item()) {
3354  // If the parent is watched or ignored, propagate that to it's
3355  // children
3356  if (mFolder->getMsgBase(i->id())->isWatched())
3357  mFolder->getMsgBase(new_kci->id())->setStatus(KMMsgStatusWatched);
3358  if (mFolder->getMsgBase(i->id())->isIgnored())
3359  mFolder->getMsgBase(new_kci->id())->setStatus(KMMsgStatusIgnored);
3360  khi = new HeaderItem(i->item(), new_kci->id(), new_kci->key());
3361  } else {
3362  khi = new HeaderItem(this, new_kci->id(), new_kci->key());
3363  }
3364  new_kci->setItem(mItems[new_kci->id()] = khi);
3365  if(new_kci->hasChildren())
3366  s.enqueue(new_kci);
3367  // we always jump to new messages, but we only jump to
3368  // unread messages if we are told to do so
3369  if ( ( mFolder->getMsgBase(new_kci->id())->isNew() &&
3370  GlobalSettings::self()->actionEnterFolder() ==
3371  GlobalSettings::EnumActionEnterFolder::SelectFirstNew ) ||
3372  ( ( mFolder->getMsgBase(new_kci->id())->isNew() ||
3373  mFolder->getMsgBase(new_kci->id())->isUnread() ) &&
3374  jumpToUnread ) )
3375  {
3376  unread_exists = true;
3377  }
3378 
3379  if ( !oldestItem || mFolder->getMsgBase( oldestItem->msgId() )->date() >
3380  mFolder->getMsgBase( new_kci->id() )->date() ) {
3381  oldestItem = khi;
3382  }
3383 
3384  if ( !newestItem || mFolder->getMsgBase( newestItem->msgId() )->date() <
3385  mFolder->getMsgBase( new_kci->id() )->date() ) {
3386  newestItem = khi;
3387  }
3388  }
3389  // If we are sorting by date and ascending the top level items are sorted
3390  // ascending and the threads themselves are sorted descending. One wants
3391  // to have new threads on top but the threads themselves top down.
3392  if (mSortCol == paintInfo()->dateCol)
3393  compare_toplevel = false;
3394  } while(!s.isEmpty());
3395 
3396  for(int x = 0; x < mFolder->count(); x++) { //cleanup
3397  if (!sortCache[x]) { // not yet there?
3398  continue;
3399  }
3400 
3401  if (!sortCache[x]->item()) { // we missed a message, how did that happen ?
3402  kdDebug(5006) << "KMHeaders::readSortOrder - msg could not be threaded. "
3403  << endl << "Please talk to your threading counselor asap. " << endl;
3404  khi = new HeaderItem(this, sortCache[x]->id(), sortCache[x]->key());
3405  sortCache[x]->setItem(mItems[sortCache[x]->id()] = khi);
3406  }
3407  // Add all imperfectly threaded items to a list, so they can be
3408  // reevaluated when a new message arrives which might be a better parent.
3409  // Important for messages arriving out of order.
3410  if (threaded && sortCache[x]->isImperfectlyThreaded()) {
3411  mImperfectlyThreadedList.append(sortCache[x]->item());
3412  }
3413  // Set the reverse mapping HeaderItem -> SortCacheItem. Needed for
3414  // keeping the data structures up to date on removal, for example.
3415  sortCache[x]->item()->setSortCacheItem(sortCache[x]);
3416  }
3417 
3418  if (getNestingPolicy()<2)
3419  for (HeaderItem *khi=static_cast<HeaderItem*>(firstChild()); khi!=0;khi=static_cast<HeaderItem*>(khi->nextSibling()))
3420  khi->setOpen(true);
3421 
3422  END_TIMER(header_creation);
3423  SHOW_TIMER(header_creation);
3424 
3425  if(sortStream) { //update the .sorted file now
3426  // heuristic for when it's time to rewrite the .sorted file
3427  if( discovered_count * discovered_count > sorted_count - deleted_count ) {
3428  mSortInfo.dirty = true;
3429  } else {
3430  //update the appended flag
3431  appended = 0;
3432  fseek(sortStream, KMAIL_MAGIC_HEADER_OFFSET + 16, SEEK_SET);
3433  fwrite(&appended, sizeof(appended), 1, sortStream);
3434  }
3435  }
3436 
3437  // Select a message, depending on the "When entering a folder:" setting
3438  CREATE_TIMER(selection);
3439  START_TIMER(selection);
3440  if(set_selection) {
3441 
3442  // Search for the id of the first unread/new item, should there be any
3443  int first_unread = -1;
3444  if (unread_exists) {
3445  HeaderItem *item = static_cast<HeaderItem*>(firstChild());
3446  while (item) {
3447  if ( ( mFolder->getMsgBase(item->msgId())->isNew() &&
3448  GlobalSettings::self()->actionEnterFolder() ==
3449  GlobalSettings::EnumActionEnterFolder::SelectFirstNew ) ||
3450  ( ( mFolder->getMsgBase(item->msgId())->isNew() ||
3451  mFolder->getMsgBase(item->msgId())->isUnread() ) &&
3452  jumpToUnread ) )
3453  {
3454  first_unread = item->msgId();
3455  break;
3456  }
3457  item = static_cast<HeaderItem*>(item->itemBelow());
3458  }
3459  }
3460 
3461  // No unread message to select, so either select the newest, oldest or lastest selected
3462  if(first_unread == -1 ) {
3463  setTopItemByIndex( mTopItem );
3464 
3465  if ( GlobalSettings::self()->actionEnterFolder() ==
3466  GlobalSettings::EnumActionEnterFolder::SelectNewest && newestItem != 0 ) {
3467  setCurrentItemByIndex( newestItem->msgId() );
3468  }
3469  else if ( GlobalSettings::self()->actionEnterFolder() ==
3470  GlobalSettings::EnumActionEnterFolder::SelectOldest && oldestItem != 0 ) {
3471  setCurrentItemByIndex( oldestItem->msgId() );
3472  }
3473  else if ( mCurrentItem >= 0 )
3474  setCurrentItemByIndex( mCurrentItem );
3475  else if ( mCurrentItemSerNum > 0 )
3476  setCurrentItemBySerialNum( mCurrentItemSerNum );
3477  else
3478  setCurrentItemByIndex( 0 );
3479 
3480  // There is an unread item to select, so select it
3481  } else {
3482  setCurrentItemByIndex(first_unread);
3483  makeHeaderVisible();
3484  center( contentsX(), itemPos(mItems[first_unread]), 0, 9.0 );
3485  }
3486 
3487  // we are told to not change the selection
3488  } else {
3489  // only reset the selection if we have no current item
3490  if (mCurrentItem <= 0) {
3491  setTopItemByIndex(mTopItem);
3492  setCurrentItemByIndex(0);
3493  }
3494  }
3495  END_TIMER(selection);
3496  SHOW_TIMER(selection);
3497  if (error || (sortStream && ferror(sortStream))) {
3498  if ( sortStream )
3499  fclose(sortStream);
3500  unlink(TQFile::encodeName(sortFile));
3501  kdWarning(5006) << "Error: Failure modifying " << sortFile << " (No space left on device?)" << endl;
3502  kdWarning(5006) << __FILE__ << ":" << __LINE__ << endl;
3503 
3504  return true;
3505  }
3506  if(sortStream)
3507  fclose(sortStream);
3508 
3509  return true;
3510 }
3511 
3512 //-----------------------------------------------------------------------------
3513 void KMHeaders::setCurrentItemBySerialNum( unsigned long serialNum )
3514 {
3515  // Linear search == slow. Don't overuse this method.
3516  // It's currently only used for finding the current item again
3517  // after expiry deleted mails (so the index got invalidated).
3518  for (int i = 0; i < (int)mItems.size() - 1; ++i) {
3519  KMMsgBase *mMsgBase = mFolder->getMsgBase( i );
3520  if ( mMsgBase->getMsgSerNum() == serialNum ) {
3521  bool unchanged = (currentItem() == mItems[i]);
3522  setCurrentItem( mItems[i] );
3523  setSelected( mItems[i], true );
3524  setSelectionAnchor( currentItem() );
3525  if ( unchanged )
3526  highlightMessage( currentItem(), false );
3527  ensureCurrentItemVisible();
3528  return;
3529  }
3530  }
3531  // Not found. Maybe we should select the last item instead?
3532  kdDebug(5006) << "KMHeaders::setCurrentItem item with serial number " << serialNum << " NOT FOUND" << endl;
3533 }
3534 
3535 void KMHeaders::copyMessages()
3536 {
3537  mCopiedMessages.clear();
3538  KMMessageList* list = selectedMsgs();
3539  for ( uint i = 0; i < list->count(); ++ i )
3540  mCopiedMessages << list->at( i )->getMsgSerNum();
3541  mMoveMessages = false;
3542  updateActions();
3543  triggerUpdate();
3544 }
3545 
3546 void KMHeaders::cutMessages()
3547 {
3548  mCopiedMessages.clear();
3549  KMMessageList* list = selectedMsgs();
3550  for ( uint i = 0; i < list->count(); ++ i )
3551  mCopiedMessages << list->at( i )->getMsgSerNum();
3552  mMoveMessages = true;
3553  updateActions();
3554  triggerUpdate();
3555 }
3556 
3557 void KMHeaders::pasteMessages()
3558 {
3559  new MessageCopyHelper( mCopiedMessages, folder(), mMoveMessages, this );
3560  if ( mMoveMessages ) {
3561  mCopiedMessages.clear();
3562  updateActions();
3563  }
3564 }
3565 
3566 void KMHeaders::updateActions()
3567 {
3568  TDEAction *copy = owner()->action( "copy_messages" );
3569  TDEAction *cut = owner()->action( "cut_messages" );
3570  TDEAction *paste = owner()->action( "paste_messages" );
3571 
3572  if ( selectedItems().isEmpty() ) {
3573  copy->setEnabled( false );
3574  cut->setEnabled( false );
3575  } else {
3576  copy->setEnabled( true );
3577  if ( folder() && !folder()->canDeleteMessages() )
3578  cut->setEnabled( false );
3579  else
3580  cut->setEnabled( true );
3581  }
3582 
3583  if ( mCopiedMessages.isEmpty() || !folder() || folder()->isReadOnly() )
3584  paste->setEnabled( false );
3585  else
3586  paste->setEnabled( true );
3587 }
3588 
3589 void KMHeaders::setCopiedMessages(const TQValueList< TQ_UINT32 > & msgs, bool move)
3590 {
3591  mCopiedMessages = msgs;
3592  mMoveMessages = move;
3593  updateActions();
3594 }
3595 
3596 bool KMHeaders::isMessageCut(TQ_UINT32 serNum) const
3597 {
3598  return mMoveMessages && mCopiedMessages.contains( serNum );
3599 }
3600 
3601 TQValueList< TQ_UINT32 > KMHeaders::selectedSernums()
3602 {
3603  TQValueList<TQ_UINT32> list;
3604  for ( TQListViewItemIterator it(this); it.current(); it++ ) {
3605  if ( it.current()->isSelected() && it.current()->isVisible() ) {
3606  HeaderItem* item = static_cast<HeaderItem*>( it.current() );
3607  KMMsgBase *msgBase = mFolder->getMsgBase( item->msgId() );
3608  if ( msgBase ) {
3609  list.append( msgBase->getMsgSerNum() );
3610  }
3611  }
3612  }
3613  return list;
3614 }
3615 
3616 TQValueList< TQ_UINT32 > KMHeaders::selectedVisibleSernums()
3617 {
3618  TQValueList<TQ_UINT32> list;
3619  TQListViewItemIterator it(this, TQListViewItemIterator::Selected|TQListViewItemIterator::Visible);
3620  while( it.current() ) {
3621  if ( it.current()->isSelected() && it.current()->isVisible() ) {
3622  if ( it.current()->parent() && ( !it.current()->parent()->isOpen() ) ) {
3623  // the item's parent is closed, don't traverse any more of this subtree
3624  TQListViewItem * lastAncestorWithSiblings = it.current()->parent();
3625  // travel towards the root until we find an ancestor with siblings
3626  while ( ( lastAncestorWithSiblings->depth() > 0 ) && !lastAncestorWithSiblings->nextSibling() )
3627  lastAncestorWithSiblings = lastAncestorWithSiblings->parent();
3628  // move the iterator to that ancestor's next sibling
3629  it = TQListViewItemIterator( lastAncestorWithSiblings->nextSibling() );
3630  continue;
3631  }
3632  HeaderItem *item = static_cast<HeaderItem*>(it.current());
3633  KMMsgBase *msgBase = mFolder->getMsgBase( item->msgId() );
3634  if ( msgBase ) {
3635  list.append( msgBase->getMsgSerNum() );
3636  }
3637  }
3638  ++it;
3639  }
3640 
3641  return list;
3642 }
3643 
3644 #include "kmheaders.moc"
sets a cursor and makes sure it's restored on destruction Create a KCursorSaver object when you want ...
Definition: kcursorsaver.h:14
Mail folder.
Definition: kmfolder.h:69
KMMsgInfo * unGetMsg(int idx)
Replace KMMessage with KMMsgInfo and delete KMMessage
Definition: kmfolder.cpp:326
KMMessage * getMsg(int idx)
Read message at given index.
Definition: kmfolder.cpp:321
virtual void setFolder(KMFolder *, bool forceJumpToUnread=false)
A new folder has been selected update the list of headers shown To override the global settings for j...
Definition: kmheaders.cpp:678
TQValueList< TQ_UINT32 > selectedSernums()
Returns the sernums of all selected items.
Definition: kmheaders.cpp:3601
void incCurrentMessage()
Focus the next message, but don't select it.
Definition: kmheaders.cpp:1879
void msgAdded(int)
For when the message with the given message id has been added to a folder.
Definition: kmheaders.cpp:895
void setSelectedByIndex(TQValueList< int > items, bool selected)
Select several items by message index and if they are the parent of a closed thread,...
Definition: kmheaders.cpp:1727
void setFolderInfoStatus()
Provide information about number of messages in a folder.
Definition: kmheaders.cpp:1391
TQValueList< TQ_UINT32 > selectedVisibleSernums()
Returns the sernums of all visible (ie.
Definition: kmheaders.cpp:3616
virtual void copySelectedToFolder(int menuId)
Same thing but copy.
Definition: kmheaders.cpp:1666
virtual void paintEmptyArea(TQPainter *p, const TQRect &rect)
Overridden to support backing pixmap.
Definition: kmheaders.cpp:403
bool isThreaded() const
Returns true if the current header list is threaded.
Definition: kmheaders.h:166
virtual void contentsMousePressEvent(TQMouseEvent *)
Handle shift and control selection.
Definition: kmheaders.cpp:2276
void msgChanged()
For when the list of messages in a folder has changed.
Definition: kmheaders.cpp:816
const KMMsgBase * getMsgBaseForItem(const TQListViewItem *item) const
gets the message represented by the item as a KMMsgBase.
Definition: kmheaders.cpp:2609
virtual void readFolderConfig(void)
Read per-folder config options and apply them.
Definition: kmheaders.cpp:608
void reset()
Refresh the list of message headers shown.
Definition: kmheaders.cpp:570
void selected(KMMessage *)
emitted when the list view item corresponding to this message has been selected
void styleChange(TQStyle &oldStyle)
reimplemented in order to update the frame width in case of a changed GUI style
Definition: kmheaders.cpp:1384
virtual KMMessageList * selectedMsgs(bool toBeDeleted=false)
Returns list of selected messages.
Definition: kmheaders.cpp:1755
void restoreColumnLayout(TDEConfig *config, const TQString &group)
Same as TDEListView::restoreLayout(), only that this does not restore the sort order.
Definition: kmheaders.cpp:559
KMFolder * folder(void)
Return the folder whose message headers are being displayed.
Definition: kmheaders.h:64
virtual void setOpen(TQListViewItem *, bool)
Double force items to always be open.
Definition: kmheaders.cpp:2602
virtual void moveSelectedToFolder(int menuId)
Move messages corresponding to the selected items to the folder corresponding to the given menuId.
Definition: kmheaders.cpp:1531
void msgRemoved(int, TQString)
For when the message with the given id has been removed for a folder.
Definition: kmheaders.cpp:1081
void makeHeaderVisible()
Ensure the current item is visible.
Definition: kmheaders.cpp:2098
virtual void readConfig(void)
Read config options.
Definition: kmheaders.cpp:468
void selectCurrentMessage()
Select the message which currently has focus, if it's not already selected.
Definition: kmheaders.cpp:1914
void slotExpandOrCollapseAllThreads(bool expand)
Expands (expand == true) or collapses (expand == false) all threads.
Definition: kmheaders.cpp:1344
virtual void setSelected(TQListViewItem *item, bool selected)
Select an item and if it is the parent of a closed thread, also recursively select its children.
Definition: kmheaders.cpp:1707
void decCurrentMessage()
Focus the previous message, but don't select it.
Definition: kmheaders.cpp:1897
virtual void clearSelectableAndAboutToBeDeleted(TQ_UINT32 serNum)
Resets toBeDeleted and selectable status of all selected items.
Definition: kmheaders.cpp:1738
virtual KMMessage * currentMsg()
Return the current message.
Definition: kmheaders.cpp:2517
virtual void setCurrentItemByIndex(int msgIdx)
Set the current item to the one corresponding to the given msg id.
Definition: kmheaders.cpp:2543
virtual bool event(TQEvent *e)
Look for color changes.
Definition: kmheaders.cpp:414
virtual int slotFilterMsg(KMMessage *)
Apply the filter Rules to a single message.
Definition: kmheaders.cpp:1305
void folderClosed()
For when the folder has been cleared.
Definition: kmheaders.cpp:2701
void slotNoDrag()
Don't show a drag cursor.
Definition: kmheaders.cpp:2086
void resetCurrentTime()
timer function to set the current time regularly
Definition: kmheaders.cpp:2157
void prevMessage()
Make the previous header visible scrolling if necessary.
Definition: kmheaders.cpp:1841
TQValueList< int > selectedItems()
Returns the index values of currently selected items.
Definition: kmheaders.cpp:1776
void selectPrevMessage()
Same as prevMessage() but don't clear the current selection.
Definition: kmheaders.cpp:1853
void nextMessage()
Make the next header visible scrolling if necessary.
Definition: kmheaders.cpp:1804
void setCopiedMessages(const TQValueList< TQ_UINT32 > &msgs, bool move)
Sets the list of copied/cutted messages.
Definition: kmheaders.cpp:3589
void slotToggleColumn(int id, int mode=-1)
switch a column with the given id (see KPaintInfo enum) 1 for activate, 0 for deactivate,...
Definition: kmheaders.cpp:259
void dirtySortOrder(int)
dirties the sort order
Definition: kmheaders.cpp:2865
void selectNextMessage()
Same as nextMessage() but don't clear the current selection.
Definition: kmheaders.cpp:1816
bool nextUnreadMessage(bool acceptCurrent=false)
Make the nextUnread message header visible scrolling if necessary, returning true if an unread messag...
Definition: kmheaders.cpp:2040
virtual void moveMsgToFolder(KMFolder *destination, bool askForConfirmation=true)
If destination is 0 then the messages are deleted, otherwise they are moved to this folder.
Definition: kmheaders.cpp:1586
virtual void setSorting(int column, bool ascending=true)
Called when a header is clicked.
Definition: kmheaders.cpp:2616
void contentsMouseMoveEvent(TQMouseEvent *e)
To initiate a drag operation.
Definition: kmheaders.cpp:2375
void msgHeaderChanged(KMFolder *folder, int msgId)
Refresh list view item corresponding to the messae with the given id.
Definition: kmheaders.cpp:1210
virtual void readColorConfig(void)
Read color options and set palette.
Definition: kmheaders.cpp:426
void setCurrentItemBySerialNum(unsigned long serialNum)
Set the current item to the one corresponding to the given serial number (slow!)
Definition: kmheaders.cpp:3513
virtual HeaderItem * currentHeaderItem()
Return the current list view item.
Definition: kmheaders.cpp:2527
virtual void copyMsgToFolder(KMFolder *destination, KMMessage *aMsg=0)
Messages are duplicated and added to given folder.
Definition: kmheaders.cpp:1674
void refreshNestedState(void)
read the config file and update nested state if necessary
Definition: kmheaders.cpp:589
void rightButtonPressed(TQListViewItem *, const TQPoint &, int)
show context menu
Definition: kmheaders.cpp:2263
TQPtrList< TQListViewItem > currentThread() const
Get a list of all items in the current thread.
Definition: kmheaders.cpp:1234
virtual void setThreadStatus(KMMsgStatus status, bool toggle=false)
Set all messages in the current thread to status status or toggle it, if specified.
Definition: kmheaders.cpp:1256
bool prevUnreadMessage()
Make the previous message header visible scrolling if necessary, returning true if an unread message ...
Definition: kmheaders.cpp:2066
void activated(KMMessage *)
emitted when the list view item corresponding to this message has been double clicked
void setStyleDependantFrameWidth()
Set the width of the frame to a reasonable value for the current GUI style.
Definition: kmheaders.cpp:1369
void folderCleared()
For when the folder has been cleared.
Definition: kmheaders.cpp:2691
void slotRMB()
For when righ mouse button is pressed.
Definition: kmheaders.cpp:2424
virtual int firstSelectedMsg() const
Returns message index of first selected message of the messages where the message with the given id i...
Definition: kmheaders.cpp:1791
virtual void setTopItemByIndex(int aMsgIdx)
Make the item corresponding to the message with the given id the top most visible item.
Definition: kmheaders.cpp:2570
void slotExpandOrCollapseThread(bool expand)
Expands (expand == true) or collapses (expand == false) the current thread.
Definition: kmheaders.cpp:1327
void msgAddedToListView(TQListViewItem *)
emitted after a new item has been fully built and added to the list view.
virtual void setCurrentMsg(int msgId)
Set current message.
Definition: kmheaders.cpp:1692
virtual int currentItemIndex()
Return the index of the message corresponding to the current item.
Definition: kmheaders.cpp:2533
virtual void setMsgStatus(KMMsgStatus status, bool toggle=false)
The following methods processes all selected messages.
Definition: kmheaders.cpp:1222
virtual void writeFolderConfig(void)
Write per-folder config options.
Definition: kmheaders.cpp:638
virtual void writeConfig(void)
Write global config options.
Definition: kmheaders.cpp:659
void selectMessage(TQListViewItem *)
For when a list view item has been double clicked.
Definition: kmheaders.cpp:2166
void findUnreadAux(HeaderItem *&, bool &, bool, bool)
Auxillary method to findUnread.
Definition: kmheaders.cpp:1921
void messageListUpdated()
emitted when the list of messages has been completely rebuilt
bool isMessageCut(TQ_UINT32 serNum) const
Returns true if the message with the given serial number has been cut.
Definition: kmheaders.cpp:3596
virtual int findUnread(bool findNext, int startAt=-1, bool onlyNew=false, bool acceptCurrent=false)
Find next/prev unread message.
Definition: kmheaders.cpp:1960
virtual int topItemIndex()
Return the message id of the top most visible item.
Definition: kmheaders.cpp:2560
void maybeDeleting()
emitted when we might be about to delete messages
This is a Mime Message.
Definition: kmmessage.h:68
void setTransferInProgress(bool value, bool force=false)
Set that the message shall not be deleted because it is still required.
Definition: kmmessage.cpp:243
bool transferInProgress() const
Return, if the message should not be deleted.
Definition: kmmessage.cpp:236
bool isComplete() const
Return true if the complete message is available without referring to the backing store.
Definition: kmmessage.h:867
unsigned long getMsgSerNum(KMFolder *folder, int index) const
Find the message serial number for the message located at index index in folder folder.
Definition: kmmsgdict.cpp:345
static TQValueList< unsigned long > serNumList(TQPtrList< KMMsgBase > msgList)
Convert a list of KMMsgBase pointers to a list of serial numbers.
Definition: kmmsgdict.cpp:359
void getLocation(unsigned long key, KMFolder **retFolder, int *retIndex) const
Returns the folder the message represented by the serial number key is in and the index in that folde...
Definition: kmmsgdict.cpp:319
static const KMMsgDict * instance()
Access the globally unique MessageDict.
Definition: kmmsgdict.cpp:167
Visual representation of a member of the set of displayables (mails in the current folder).
Definition: headeritem.h:164
SortCacheItem * sortCacheItem() const
Returns the KMail::SortCacheItem associated with this display item.
Definition: headeritem.h:231
void setAboutToBeDeleted(bool val)
Set the item to be in about-to-be-deleted state, which means it cannot be selected and will be painte...
Definition: headeritem.h:225
int msgId() const
Return the msgId of the message associated with this item.
Definition: headeritem.cpp:113
bool aboutToBeDeleted() const
Returns whether the item is about to be removed from the list view as a result of some user action.
Definition: headeritem.h:222
void setSortCacheItem(SortCacheItem *item)
Associate a KMail::SortCacheItem with this item.
Definition: headeritem.h:229
void setOpenRecursive(bool open)
Expands all children of the list view item.
Definition: headeritem.cpp:149
Helper class to copy/move a set of messages defined by their serial numbers from arbitrary folders in...
Represents an item in the set of mails to be displayed but only as far as sorting,...
Definition: headeritem.h:54
bool isImperfectlyThreaded() const
Returs whether the item is so far imperfectly threaded.
Definition: headeritem.h:74
SortCacheItem * parent() const
The parent node of the item in the threading hierarchy.
Definition: headeritem.h:68
const TQPtrList< SortCacheItem > * sortedChildren() const
The sorted children are an array of sortcache items we know are below the current one and are already...
Definition: headeritem.h:85
void addUnsortedChild(SortCacheItem *i)
Add an item to this itme's list of unsorted children.
Definition: headeritem.h:97
TQPtrList< SortCacheItem > * subjectThreadingList() const
The list of mails with a certain subject that this item is on.
Definition: headeritem.h:140
const TQString & key() const
sort key as used by the listview
Definition: headeritem.h:121
void addSortedChild(SortCacheItem *i)
Add an item to this itme's list of already sorted children.
Definition: headeritem.h:92
void setImperfectlyThreaded(bool val)
Set whether the item is currently imperfectly threaded (by References or Subject, not by In-Reply-To)...
Definition: headeritem.h:78
bool hasChildren() const
Returns whether the item has other items below it.
Definition: headeritem.h:81
void setSubjectThreadingList(TQPtrList< SortCacheItem > *list)
Set the list of mails with a certain subject that this item is on.
Definition: headeritem.h:138
HeaderItem * item() const
The corresponding KMail::HeaderItem.
Definition: headeritem.h:116
void setItem(HeaderItem *i)
Set the corresponding KMail::HeaderItem.
Definition: headeritem.h:118
SortCacheItem ** unsortedChildren(int &count) const
The unsorted children are an array of sortcache items we know are below the current one,...
Definition: headeritem.h:89
void setKey(const TQString &key)
Set the sort key used by the list view.
Definition: headeritem.h:123