[prev in list] [next in list] [prev in thread] [next in thread]
List: kmail-devel
Subject: Re: threading by subject
From: Philippe Fremy <pfremy () chez ! com>
Date: 2001-03-09 18:48:27
[Download RAW message or body]
Forgot the patch.
By the way, I just noticed that I had messages in a parent folder (with
subfolders). Is it normal behaviour ?
I guess this is why closed parent folders don't display the sum of the unread
messages they have in their subfolders. Or is it just a cool missing feature ?
And if a parent folder has subfolders with unread messages, its icon is still
an empty folder. Same kind of things.
regards,
Philippe
["thread-patch-2.diff" (text/x-c++)]
Index: kmheaders.cpp
===================================================================
RCS file: /home/kde/kdenetwork/kmail/kmheaders.cpp,v
retrieving revision 1.237
diff -u -3 -p -r1.237 kmheaders.cpp
--- kmheaders.cpp 2001/03/01 20:08:36 1.237
+++ kmheaders.cpp 2001/03/09 18:19:58
@@ -307,6 +307,7 @@ KMHeaders::KMHeaders(KMMainWin *aOwner,
mNested = false;
nestingPolicy = 3;
mNestedOverride = false;
+ mThreadByMsgId = false;
mousePressed = FALSE;
// Espen 2000-05-14: Getting rid of thick ugly frames
@@ -747,21 +748,53 @@ void KMHeaders::msgAdded(int id)
assert(mb != NULL); // otherwise using count() above is wrong
if ((mNested && !mNestedOverride) || (!mNested && mNestedOverride)) {
- QString msgId = mb->msgIdMD5();
- if (msgId.isNull())
- msgId = "";
- QString replyToId = mb->replyToIdMD5();
-
- if (replyToId.isEmpty() || !mIdTree[replyToId])
- hi = new KMHeaderItem( this, mFolder, id, &mPaintInfo );
- else {
-
- KMHeaderItem *parent = mIdTree[replyToId];
- assert(parent);
- hi = new KMHeaderItem( parent, mFolder, id, &mPaintInfo );
+ // phil add on
+ if ( mThreadByMsgId ) {
+ QString msgId = mb->msgIdMD5();
+ if (msgId.isNull())
+ msgId = "";
+ QString replyToId = mb->replyToIdMD5();
+
+ if (replyToId.isEmpty() || !mIdTree[replyToId])
+ hi = new KMHeaderItem( this, mFolder, id, &mPaintInfo );
+ else {
+
+ KMHeaderItem *parent = mIdTree[replyToId];
+ assert(parent);
+ hi = new KMHeaderItem( parent, mFolder, id, &mPaintInfo );
+ }
+ if (!mIdTree[msgId])
+ mIdTree.replace( msgId, hi );
+ } else {
+ // Thread by subject
+ QString subject = mb->subject();
+ QString itemSubject;
+ // Remove "Re: "
+ if (! stricmp( subject.left(4).latin1(), "re: ")) {
+ subject = subject.mid(4, subject.length() );
+ }
+
+ QListViewItem * item = firstChild();
+ while( item ) {
+ itemSubject = item->text( mPaintInfo.subCol );
+ if (! stricmp( itemSubject.left(4).latin1(), "re: ")) {
+ itemSubject = itemSubject.mid(4, itemSubject.length() );
+ }
+ if (itemSubject == subject
+ || (! stricmp( itemSubject.left(4).latin1(), "re: ")
+ && itemSubject.mid(4, itemSubject.length()) == subject ) ) {
+ // This item belongs to this thread
+ hi = new KMHeaderItem( item, mFolder, id, &mPaintInfo );
+ }
+ item = item->nextSibling();
+ }
+
+ // No item with matching subject, new thread
+ if (!item) {
+ hi = new KMHeaderItem( this, mFolder, id, &mPaintInfo );
+ }
+
}
- if (!mIdTree[msgId])
- mIdTree.replace( msgId, hi );
}
else
hi = new KMHeaderItem( this, mFolder, id, &mPaintInfo );
@@ -785,15 +818,49 @@ void KMHeaders::msgRemoved(int id, QStri
return;
mIdTree.remove(msgId);
+
+ if (mThreadByMsgId) {
+ // Reparent children of item into top level
+ QListViewItem *myParent = mItems[id];
+ QListViewItem *myChild = myParent->firstChild();
+ while (myChild) {
+ QListViewItem *lastChild = myChild;
+ myChild = myChild->nextSibling();
+ myParent->takeItem(lastChild);
+ insertItem(lastChild);
+ }
+ } else if (mItems[id]->childCount()) {
+ kdDebug() << "Reparenting" << endl;
+
+ // Find new top item
+ QListViewItem * item = mItems[id]->firstChild();
+ QListViewItem * topItem = item;
+ QList<QListViewItem> childItemList;
+ while( item ) {
+ childItemList.append( item );
+ if (item->key( mSortCol, !mSortDescending ) <= topItem->key( mSortCol, \
!mSortDescending ) ) { + topItem = item;
+ }
+ item = item->nextSibling();
+ }
+ kdDebug() << "Top topItem is : " << topItem->text( mPaintInfo.subCol ) << " - " \
<< topItem->text( mPaintInfo.senderCol ) << endl;
- // Reparent children of item into top level
- QListViewItem *myParent = mItems[id];
- QListViewItem *myChild = myParent->firstChild();
- while (myChild) {
- QListViewItem *lastChild = myChild;
- myChild = myChild->nextSibling();
- myParent->takeItem(lastChild);
- insertItem(lastChild);
+ // reparent top item to listview
+ mItems[id]->takeItem( topItem );
+ insertItem( topItem );
+ topItem->setOpen( true );
+ childItemList.remove( topItem );
+
+ // reparent all item child to new top item
+ QListIterator<QListViewItem> it(childItemList);
+ for(; it.current(); ++it) {
+ item = it.current();
+ kdDebug() << "\t\tMoving : " << item->text( mPaintInfo.subCol ) << " - " << \
item->text( mPaintInfo.senderCol ) << endl; + if (item == topItem) continue;
+ mItems[id]->takeItem(item);
+ topItem->insertItem(item);
+ }
+
}
if (currentItem() == mItems[id])
@@ -1392,7 +1459,7 @@ void KMHeaders::copyMsgToFolder (KMFolde
KMMessageList* msgList;
KMMsgBase *msgBase;
KMMessage *msg, *newMsg;
- int top, rc, idx = -1;
+ int top, rc, idx;
bool isMessage;
if (!destFolder) return;
@@ -1776,7 +1843,38 @@ void KMHeaders::recursivelyAddChildren(
}
+//-----------------------------------------------------------------------------
+int KMHeaders::findTopItemIndex( QValueList<int> & thread )
+{
+ QString topItemKey="";
+ KMMsgBase* mb;
+ int topItemIndex=-1;
+
+ // We need a tmp item to access its sorting key.
+ // This is why updates are disabled
+ KMHeaderItem* tmpItem = new KMHeaderItem( this, mFolder, 0, &mPaintInfo );
+
+ // Find the top item, with respect to sorting order
+ QValueListIterator<int> threadIt;
+ for( threadIt = thread.begin(); threadIt != thread.end(); threadIt++) {
+ mb = mFolder->getMsgBase( *threadIt );
+ assert(mb);
+
+ tmpItem->reset( mFolder, *threadIt );
+ //kdDebug() << "\t\tId : " << *threadIt << "\tkey : " << tmpItem->key( \
mSortCol, 0 ) << "\t" << mb->fromStrip() << endl; + if ( topItemKey.isEmpty() \
|| topItemKey >= tmpItem->key( mSortCol, !mSortDescending ) ) { + \
topItemKey = tmpItem->key(mSortCol, !mSortDescending ); + topItemIndex = \
*threadIt; + }
+ }
+ assert( topItemIndex != -1);
+ delete tmpItem;
+
+ return topItemIndex;
+}
+
+
//-----------------------------------------------------------------------------
void KMHeaders::updateMessageList(void)
{
@@ -1834,106 +1932,182 @@ kdDebug() << "mFolder->count() = " << mF
mItems[i] = 0;
clear();
- mIdTree.clear();
- mTree.setAutoDelete( true );
- if (mTree.size() < 2*(unsigned)mFolder->count()) {
- mTree.resize( 2*mFolder->count() );
- mTreeSeen.resize( 2*mFolder->count() );
- mTreeToplevel.resize( 2*mFolder->count() );
- mIdTree.resize( 2*mFolder->count() );
- }
-
- // Create an entry in mTree (the msgId -> list of children map)
- // for each message
- // To begin with each entry in mTree is a list of messages with
- // a certain msgId. (Normally there's just one message in each list as
- // it should be unusual to get duplicate msgIds)
- for (i=0; i<mFolder->count(); i++) {
- mb = mFolder->getMsgBase(i);
- assert(mb);
- QString msgId = mb->msgIdMD5();
- if (msgId.isEmpty()) {
- // pathological case, message with no id
- kdDebug() << "Message without id detected" << endl;
- msgId = "";
- }
- if (mTree[msgId])
- ; // pathological case, duplicate ids
- //kdDebug() << "duplicate msgIds detected: Id " << endl;
- else
- mTree.replace( msgId, new QValueList< int > );
- mTree[msgId]->append( i ); // head of list is parent, rest children
- mTreeSeen.replace(msgId, &mFalse);
- mTreeToplevel.replace(msgId, &mTrue);
- }
+ mThreadByMsgId = false;
+
+
+
// For each message if the parent message exists add the message
// to the list of messages owned by the parent and remove the list
// of msgs with a given msgId
- for (i=0; i<mFolder->count(); i++) {
- mb = mFolder->getMsgBase(i);
- assert(mb);
- QString msgId = mb->msgIdMD5();
- QString replyToId = mb->replyToIdMD5();
- if (replyToId.isEmpty())
- continue;
- if (replyToId == msgId) //xxx
- continue;
-
- QValueList< int > *parentList = mTree[replyToId];
- if (parentList)
- parentList->append( i );
- else
- continue;
- if (msgId.isNull())
- msgId = "";
- QValueList< int > *thisList = mTree[msgId];
- assert( thisList );
- thisList->remove( i );
- mTreeToplevel.replace( msgId, &mFalse );
- }
+ if (mThreadByMsgId) {
- // Create new list view items for each top level message (one
- // with no parent) and recusively create list view items for
- // each of its children
- for (i=0; i<mFolder->count(); i++) {
- mb = mFolder->getMsgBase(i);
- assert(mb != NULL); // otherwise using count() above is wrong
- QString msgId = mb->msgIdMD5();
- if (msgId.isNull())
- msgId = "";
- assert(mTreeToplevel[msgId]);
- if (*mTreeToplevel[msgId] && !mItems[i]) {
- KMHeaderItem* hi = new KMHeaderItem( this, mFolder, i, &mPaintInfo );
- mItems[i] = hi;
- if (!msgId.isEmpty()) recursivelyAddChildren( i, hi );
+ // -- Thread by MsgId --
+
+ mIdTree.clear();
+ mTree.setAutoDelete( true );
+ if (mTree.size() < 2*(unsigned)mFolder->count()) {
+ mTree.resize( 2*mFolder->count() );
+ mTreeSeen.resize( 2*mFolder->count() );
+ mTreeToplevel.resize( 2*mFolder->count() );
+ mIdTree.resize( 2*mFolder->count() );
}
- }
+
+ // Create an entry in mTree (the msgId -> list of children map)
+ // for each message
+ // To begin with each entry in mTree is a list of messages with
+ // a certain msgId. (Normally there's just one message in each list as
+ // it should be unusual to get duplicate msgIds)
+
+ for (i=0; i<mFolder->count(); i++) {
+ mb = mFolder->getMsgBase(i);
+ assert(mb);
+ QString msgId = mb->msgIdMD5();
+ if (msgId.isEmpty()) {
+ // pathological case, message with no id
+ kdDebug() << "Message without id detected" << endl;
+ msgId = "";
+ }
+ if (mTree[msgId])
+ ; // pathological case, duplicate ids
+ //kdDebug() << "duplicate msgIds detected: Id " << endl;
+ else
+ mTree.replace( msgId, new QValueList< int > );
+ mTree[msgId]->append( i ); // head of list is parent, rest children
+ mTreeSeen.replace(msgId, &mFalse);
+ mTreeToplevel.replace(msgId, &mTrue);
+ }
+
+ for (i=0; i<mFolder->count(); i++) {
+ mb = mFolder->getMsgBase(i);
+ assert(mb);
+ QString msgId = mb->msgIdMD5();
+ QString replyToId = mb->replyToIdMD5();
+ if (replyToId.isEmpty())
+ continue;
+ if (replyToId == msgId) //xxx
+ continue;
+
+ QValueList< int > *parentList = mTree[replyToId];
+ if (parentList)
+ parentList->append( i );
+ else
+ continue;
+
+ if (msgId.isNull())
+ msgId = "";
+ QValueList< int > *thisList = mTree[msgId];
+ assert( thisList );
+ thisList->remove( i );
+ mTreeToplevel.replace( msgId, &mFalse );
+
+ }
+
+ // Create new list view items for each top level message (one
+ // with no parent) and recusively create list view items for
+ // each of its children
+ for (i=0; i<mFolder->count(); i++) {
+ mb = mFolder->getMsgBase(i);
+ assert(mb != NULL); // otherwise using count() above is wrong
+ QString msgId = mb->msgIdMD5();
+ if (msgId.isNull())
+ msgId = "";
+ assert(mTreeToplevel[msgId]);
+ if (*mTreeToplevel[msgId] && !mItems[i]) {
+ KMHeaderItem* hi = new KMHeaderItem( this, mFolder, i, &mPaintInfo );
+ mItems[i] = hi;
+ recursivelyAddChildren( i, hi );
+ }
+ }
+
+ mTree.clear();
+ mTreeSeen.clear();
+ mTreeToplevel.clear();
+
+ } else {
+ // Phil add on
+ // -- Thread by subject --
+
+ // messages that share a common subject line are put togegher
+ QDict< QValueList<int> > subjectThread;
+ subjectThread.setAutoDelete(true);
+
+ for (i=0; i<mFolder->count(); i++) {
+ mb = mFolder->getMsgBase(i);
+ assert(mb);
+ QString subject = mb->subject();
+
+ // Remove "Re: "
+ if (! stricmp( subject.left(4).latin1(), "re: ")) {
+ subject = subject.mid(4, subject.length() );
+ }
+
+ if (! subjectThread[ subject ]) {
+ subjectThread.insert(subject, new QValueList<int> );
+ }
+ subjectThread[ subject ]->append(i);
+ }
+
+ // disable updates during the item generation
+ //setUpdatesEnabled( false );
+
+ QDictIterator< QValueList<int> > it( subjectThread );
+ for( it.toFirst(); it.current(); ++it) {
+
+ QValueList<int> * thread = it.current();
+ //kdDebug() << thread->count() << " - subject : '" << it.currentKey() << \
endl; +
+ int topItemIndex= findTopItemIndex( *thread );
+
+ QValueListIterator<int> threadIt;
+ // Now, we know which item should be the first one.
+ // put it at first place in the thread list
+ thread->remove( topItemIndex );
+ thread->prepend( topItemIndex );
+
+
+ // Generate all the items.
+ threadIt = thread->begin();
+ KMHeaderItem* topItem = new KMHeaderItem( this, mFolder, *threadIt, \
&mPaintInfo ); + topItem->setOpen( true );
+ mItems[*threadIt] = topItem;
+ //kdDebug() << "\t\t\t" << *threadIt << endl;
+ threadIt++;
+
+ for(; threadIt != thread->end(); threadIt++) {
+ KMHeaderItem* hi = new KMHeaderItem( topItem, mFolder, *threadIt, \
&mPaintInfo ); + mItems[*threadIt] = hi;
+ //kdDebug() << "\t\t\t" << *threadIt << endl;
+ }
+ }
+
+ setUpdatesEnabled( true );
+
+ } // End of 'if (mThreadByMsgId)'
+
+
for (i=0; i<mFolder->count(); i++)
if (mItems[i] == 0) {
- // It turns out this can happen when different messages have the same ids;
- KMHeaderItem* hi = new KMHeaderItem( this, mFolder, i, &mPaintInfo );
- mItems[i] = hi;
- kdDebug() << QString("%1 ").arg(i) + mFolder->getMsgBase(i)->subject() + " " + \
mFolder->getMsgBase(i)->fromStrip() << endl;
- // assert(mItems[i] != 0);
+ // It turns out this can happen when different messages have the same ids;
+ KMHeaderItem* hi = new KMHeaderItem( this, mFolder, i, &mPaintInfo );
+ mItems[i] = hi;
+ kdDebug() << QString("%1 ").arg(i) + mFolder->getMsgBase(i)->subject() + " " \
+ mFolder->getMsgBase(i)->fromStrip() << endl; + // assert(mItems[i] != 0);
}
- mTree.clear();
- mTreeSeen.clear();
- mTreeToplevel.clear();
}
else { // mNested == false
for (i=0; i<mFolder->count(); i++)
{
- mb = mFolder->getMsgBase(i);
- assert(mb != NULL); // otherwise using count() above is wrong
-
- if (i >= oldSize)
- mItems[i] = new KMHeaderItem( this, mFolder, i, &mPaintInfo );
- else
- mItems[i]->reset( mFolder, i );
+ mb = mFolder->getMsgBase(i);
+ assert(mb != NULL); // otherwise using count() above is wrong
+
+ if (i >= oldSize)
+ mItems[i] = new KMHeaderItem( this, mFolder, i, &mPaintInfo );
+ else
+ mItems[i]->reset( mFolder, i );
}
}
@@ -2257,6 +2431,7 @@ void KMHeaders::setOpen( QListViewItem *
//-----------------------------------------------------------------------------
void KMHeaders::setSorting( int column, bool ascending )
{
+ kdDebug() << "setSorting() " << column << " - " << ascending << endl;
if (column != -1) {
if (column != mSortCol)
setColumnText( mSortCol, QIconSet( QPixmap()), columnText( mSortCol ));
@@ -2284,6 +2459,4 @@ void KMHeaders::setSorting( int column,
//-----------------------------------------------------------------------------
#include "kmheaders.moc"
-
-
Index: kmheaders.h
===================================================================
RCS file: /home/kde/kdenetwork/kmail/kmheaders.h,v
retrieving revision 1.64
diff -u -3 -p -r1.64 kmheaders.h
--- kmheaders.h 2001/02/24 15:35:55 1.64
+++ kmheaders.h 2001/03/09 18:20:05
@@ -199,6 +199,9 @@ protected:
/** Auxillary method to findUnread */
void findUnreadAux( KMHeaderItem*&, bool &, bool, bool );
+ /** Find out of a list the item with the lowest key, respecting sort order */
+ int findTopItemIndex( QValueList<int> & thread );
+
/** Find next/prev unread message. Starts at currentItem() if startAt
is unset. */
virtual int findUnread(bool findNext, int startAt=-1, bool onlyNew = FALSE);
@@ -258,6 +261,8 @@ private:
QDict< bool > mTreeToplevel;
bool mNested, mNestedOverride;
int nestingPolicy;
+
+ bool mThreadByMsgId;
static bool mTrue, mFalse; // These must replaced by something better!
_______________________________________________
Kmail Developers mailing list
Kmail@master.kde.org
http://master.kde.org/mailman/listinfo/kmail
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic