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"
18using KMail::FolderJob;
19#include "actionscheduler.h"
20using KMail::ActionScheduler;
21#include "messagecopyhelper.h"
23#include "broadcaststatus.h"
24using KPIM::BroadcastStatus;
25#include "progressmanager.h"
26using KPIM::ProgressManager;
27using KPIM::ProgressItem;
28#include <maillistdrag.h>
29#include "globalsettings.h"
30using 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
64TQPixmap* KMHeaders::pixNew = 0;
65TQPixmap* KMHeaders::pixUns = 0;
66TQPixmap* KMHeaders::pixDel = 0;
67TQPixmap* KMHeaders::pixRead = 0;
68TQPixmap* KMHeaders::pixRep = 0;
69TQPixmap* KMHeaders::pixQueued = 0;
70TQPixmap* KMHeaders::pixTodo = 0;
71TQPixmap* KMHeaders::pixSent = 0;
72TQPixmap* KMHeaders::pixFwd = 0;
73TQPixmap* KMHeaders::pixFlag = 0;
74TQPixmap* KMHeaders::pixWatched = 0;
75TQPixmap* KMHeaders::pixIgnored = 0;
76TQPixmap* KMHeaders::pixSpam = 0;
77TQPixmap* KMHeaders::pixHam = 0;
78TQPixmap* KMHeaders::pixFullySigned = 0;
79TQPixmap* KMHeaders::pixPartiallySigned = 0;
80TQPixmap* KMHeaders::pixUndefinedSigned = 0;
81TQPixmap* KMHeaders::pixFullyEncrypted = 0;
82TQPixmap* KMHeaders::pixPartiallyEncrypted = 0;
83TQPixmap* KMHeaders::pixUndefinedEncrypted = 0;
84TQPixmap* KMHeaders::pixEncryptionProblematic = 0;
85TQPixmap* KMHeaders::pixSignatureProblematic = 0;
86TQPixmap* KMHeaders::pixAttachment = 0;
87TQPixmap* KMHeaders::pixInvitation = 0;
88TQPixmap* KMHeaders::pixReadFwd = 0;
89TQPixmap* KMHeaders::pixReadReplied = 0;
90TQPixmap* KMHeaders::pixReadFwdReplied = 0;
91
92
93//-----------------------------------------------------------------------------
94KMHeaders::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//-----------------------------------------------------------------------------
222KMHeaders::~KMHeaders ()
223{
224 if (mFolder)
225 {
227 writeSortOrder();
228 mFolder->close("kmheaders");
229 }
230 writeConfig();
231 delete mRoot;
232}
233
234//-----------------------------------------------------------------------------
235bool 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
259void 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
403void 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
414bool KMHeaders::event(TQEvent *e)
415{
416 bool result = TDEListView::event(e);
417 if (e->type() == TQEvent::ApplicationPaletteChange)
418 {
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(tdeApp->palette().active().text());
432 TQColor c2=TQColor("red");
433 TQColor c3=TQColor("blue");
434 TQColor c4=TQColor(tdeApp->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 = tdeApp->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 = tdeApp->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
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//-----------------------------------------------------------------------------
559void 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);
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//-----------------------------------------------------------------------------
678void 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);
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 );
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//-----------------------------------------------------------------------------
1081void 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//-----------------------------------------------------------------------------
1222void 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
1234TQPtrList<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
1256void 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//-----------------------------------------------------------------------------
1384void 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//-----------------------------------------------------------------------------
1409void 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 tdeApp->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//-----------------------------------------------------------------------------
1492void 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//-----------------------------------------------------------------------------
1509void 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//-----------------------------------------------------------------------------
1538HeaderItem* 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//-----------------------------------------------------------------------------
1565void 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//-----------------------------------------------------------------------------
1586void 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
1615void 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
1654bool KMHeaders::canUndo() const
1655{
1656 return ( kmkernel->undoStack()->size() > 0 );
1657}
1658
1659//-----------------------------------------------------------------------------
1660void 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//-----------------------------------------------------------------------------
1707void 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
1727void 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//-----------------------------------------------------------------------------
1755KMMessageList* 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//-----------------------------------------------------------------------------
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//-----------------------------------------------------------------------------
1960int 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//-----------------------------------------------------------------------------
2040bool 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
2058void 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//-----------------------------------------------------------------------------
2105void 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
2145void 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//-----------------------------------------------------------------------------
2166void 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//-----------------------------------------------------------------------------
2185void 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.
2218void 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
2263void 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//-----------------------------------------------------------------------------
2366void KMHeaders::contentsMouseReleaseEvent(TQMouseEvent* e)
2367{
2368 if (e->button() != TQt::RightButton)
2369 TDEListView::contentsMouseReleaseEvent(e);
2370
2371 mMousePressed = false;
2372}
2373
2374//-----------------------------------------------------------------------------
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
2418void 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{
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//-----------------------------------------------------------------------------
2580void 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//-----------------------------------------------------------------------------
2592void 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//-----------------------------------------------------------------------------
2602void KMHeaders::setOpen( TQListViewItem *item, bool open )
2603{
2604 if ((nestingPolicy != AlwaysOpen)|| open)
2605 ((HeaderItem*)item)->setOpenRecursive( open );
2606}
2607
2608//-----------------------------------------------------------------------------
2609const 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//-----------------------------------------------------------------------------
2616void 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
2661static 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
2709bool 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
2826void 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// -----------------
2873void 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
2891static bool compare_ascending = false;
2892static bool compare_toplevel = true;
2893static 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
2906void 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
2922void 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
2942void 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
2957void 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
2996SortCacheItem* 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
3024SortCacheItem* 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
3062bool 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//-----------------------------------------------------------------------------
3513void 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
3535void 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
3546void 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
3557void KMHeaders::pasteMessages()
3558{
3559 new MessageCopyHelper( mCopiedMessages, folder(), mMoveMessages, this );
3560 if ( mMoveMessages ) {
3561 mCopiedMessages.clear();
3562 updateActions();
3563 }
3564}
3565
3566void 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
3589void KMHeaders::setCopiedMessages(const TQValueList< TQ_UINT32 > & msgs, bool move)
3590{
3591 mCopiedMessages = msgs;
3592 mMoveMessages = move;
3593 updateActions();
3594}
3595
3596bool KMHeaders::isMessageCut(TQ_UINT32 serNum) const
3597{
3598 return mMoveMessages && mCopiedMessages.contains( serNum );
3599}
3600
3601TQValueList< 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
3616TQValueList< 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
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
KMFolder * folder(void)
Return the folder whose message headers are being displayed.
Definition: kmheaders.h:64
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
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
SortCacheItem * sortCacheItem() const
Returns the KMail::SortCacheItem associated with this display item.
Definition: headeritem.h:231
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
SortCacheItem * parent() const
The parent node of the item in the threading hierarchy.
Definition: headeritem.h:68
bool isImperfectlyThreaded() const
Returs whether the item is so far imperfectly threaded.
Definition: headeritem.h:74
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 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 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
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 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
const TQString & key() const
sort key as used by the listview
Definition: headeritem.h:121
void setItem(HeaderItem *i)
Set the corresponding KMail::HeaderItem.
Definition: headeritem.h:118
void setKey(const TQString &key)
Set the sort key used by the list view.
Definition: headeritem.h:123