[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