Back to index

lightning-sunbird  0.9+nobinonly
nsMsgGroupThread.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * David Bienvenu.
00019  * Portions created by the Initial Developer are Copyright (C) 2004
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   David Bienvenu <bienvenu@nventure.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "msgCore.h"
00040 #include "nsMsgGroupThread.h"
00041 #include "nsMsgDBView.h"
00042 
00043 NS_IMPL_ISUPPORTS1(nsMsgGroupThread, nsIMsgThread)
00044 
00045 nsMsgGroupThread::nsMsgGroupThread()
00046 {
00047   Init();
00048 }
00049 nsMsgGroupThread::nsMsgGroupThread(nsIMsgDatabase *db)
00050 {
00051   m_db = db;
00052   Init();
00053 }
00054 
00055 void nsMsgGroupThread::Init()
00056 {
00057   m_threadKey = nsMsgKey_None; 
00058   m_threadRootKey = nsMsgKey_None;
00059   m_numUnreadChildren = 0;  
00060   m_flags = 0;
00061   m_newestMsgDate = 0;
00062   m_dummy = PR_FALSE;
00063 }
00064 
00065 
00066 nsMsgGroupThread::~nsMsgGroupThread()
00067 {
00068 }
00069 
00070 
00071 NS_IMETHODIMP nsMsgGroupThread::SetThreadKey(nsMsgKey threadKey)
00072 {
00073   m_threadKey = threadKey;
00074   // by definition, the initial thread key is also the thread root key.
00075   m_threadRootKey = threadKey;
00076   return NS_OK;
00077 }
00078 
00079 NS_IMETHODIMP nsMsgGroupThread::GetThreadKey(nsMsgKey *aResult)
00080 {
00081   NS_ENSURE_ARG_POINTER(aResult);
00082   *aResult = m_threadKey;
00083   return NS_OK;
00084 }
00085 
00086 NS_IMETHODIMP nsMsgGroupThread::GetFlags(PRUint32 *aFlags)
00087 {
00088   NS_ENSURE_ARG_POINTER(aFlags);
00089   *aFlags = m_flags;
00090   return NS_OK;
00091 }
00092 
00093 NS_IMETHODIMP nsMsgGroupThread::SetFlags(PRUint32 aFlags)
00094 {
00095   m_flags = aFlags;
00096   return NS_OK;
00097 }
00098 
00099 NS_IMETHODIMP nsMsgGroupThread::SetSubject(const char *subject)
00100 {
00101   NS_ASSERTION(PR_FALSE, "shouldn't call this");
00102   return NS_ERROR_NOT_IMPLEMENTED;
00103 }
00104 
00105 NS_IMETHODIMP nsMsgGroupThread::GetSubject(char **result)
00106 {
00107   NS_ASSERTION(PR_FALSE, "shouldn't call this");
00108   return NS_ERROR_NOT_IMPLEMENTED;
00109 }
00110 
00111 NS_IMETHODIMP nsMsgGroupThread::GetNumChildren(PRUint32 *aNumChildren)
00112 {
00113   NS_ENSURE_ARG_POINTER(aNumChildren);
00114   *aNumChildren = m_keys.GetSize(); // - ((m_dummy) ? 1 : 0);
00115   return NS_OK;
00116 }
00117 
00118 PRUint32 nsMsgGroupThread::NumRealChildren()
00119 {
00120   return m_keys.GetSize() - ((m_dummy) ? 1 : 0);
00121 }
00122 
00123 NS_IMETHODIMP nsMsgGroupThread::GetNumUnreadChildren (PRUint32 *aNumUnreadChildren)
00124 {
00125   NS_ENSURE_ARG_POINTER(aNumUnreadChildren);
00126   *aNumUnreadChildren = m_numUnreadChildren;
00127   return NS_OK;
00128 }
00129 #if 0
00130 nsresult nsMsgGroupThread::RerootThread(nsIMsgDBHdr *newParentOfOldRoot, nsIMsgDBHdr *oldRoot, nsIDBChangeAnnouncer *announcer)
00131 {
00132   nsCOMPtr <nsIMsgDBHdr> ancestorHdr = newParentOfOldRoot;
00133   nsMsgKey newRoot;
00134   newParentOfOldRoot->GetMessageKey(&newRoot);
00135 
00136   nsMsgKey newHdrAncestor;
00137   nsresult rv = NS_OK;
00138   // loop trying to find the oldest ancestor of this msg
00139   // that is a parent of the root. The oldest ancestor will
00140   // become the root of the thread.
00141   do 
00142   {
00143     ancestorHdr->GetThreadParent(&newHdrAncestor);
00144     if (newHdrAncestor != nsMsgKey_None && newHdrAncestor != m_threadRootKey && newHdrAncestor != newRoot)
00145     {
00146       newRoot = newHdrAncestor;
00147       rv = m_db->GetMsgHdrForKey(newRoot, getter_AddRefs(ancestorHdr));
00148     }
00149   }
00150   while (NS_SUCCEEDED(rv) && ancestorHdr && newHdrAncestor != nsMsgKey_None && newHdrAncestor != m_threadRootKey
00151     && newHdrAncestor != newRoot);
00152   m_threadRootKey = newRoot;
00153 //  ReparentNonReferenceChildrenOf(oldRoot, newRoot, announcer);
00154   if (ancestorHdr)
00155   {
00156     // move the  root hdr to pos 0 by removing it and adding it at 0.
00157     m_keys.RemoveElement(newRoot);
00158     m_keys.InsertAt(0, newRoot);
00159     ancestorHdr->SetThreadParent(nsMsgKey_None);
00160   }
00161   return rv;
00162 }
00163 #endif
00164 
00165 NS_IMETHODIMP nsMsgGroupThread::AddChild(nsIMsgDBHdr *child, nsIMsgDBHdr *inReplyTo, PRBool threadInThread, 
00166                                     nsIDBChangeAnnouncer *announcer)
00167 {
00168   NS_ASSERTION(PR_FALSE, "shouldn't call this");
00169   return NS_ERROR_NOT_IMPLEMENTED;
00170 }
00171 
00172 nsresult nsMsgGroupThread::AddMsgHdrInDateOrder(nsIMsgDBHdr *child, nsMsgDBView *view)
00173 {
00174   nsresult ret = NS_OK;
00175   nsMsgKey newHdrKey;
00176   child->GetMessageKey(&newHdrKey);
00177   PRUint32 insertIndex = 0;
00178   // since we're sorted by date, we could do a binary search for the 
00179   // insert point. Or, we could start at the end...
00180   if (m_keys.GetSize() > 0)
00181   {
00182     nsMsgViewSortTypeValue  sortType;
00183     nsMsgViewSortOrderValue sortOrder;
00184     (void) view->GetSortType(&sortType);
00185     (void) view->GetSortOrder(&sortOrder);
00186     nsMsgViewSortOrderValue threadSortOrder = 
00187       (sortType == nsMsgViewSortType::byDate
00188         && sortOrder == nsMsgViewSortOrder::descending) ? 
00189           nsMsgViewSortOrder::descending : nsMsgViewSortOrder::ascending;
00190     // sort by date within group
00191     insertIndex = view->GetInsertIndexHelper(child, &m_keys, threadSortOrder, nsMsgViewSortType::byDate);
00192   }
00193   m_keys.InsertAt(insertIndex, newHdrKey);
00194   if (!insertIndex)
00195     m_threadRootKey = newHdrKey;
00196   return ret;
00197 }
00198 
00199 nsresult nsMsgGroupThread::AddChildFromGroupView(nsIMsgDBHdr *child, nsMsgDBView *view)
00200 {
00201   PRUint32 newHdrFlags = 0;
00202   PRUint32 msgDate;
00203   nsMsgKey newHdrKey = 0;
00204   
00205   child->GetFlags(&newHdrFlags);
00206   child->GetMessageKey(&newHdrKey);
00207   child->GetDateInSeconds(&msgDate);
00208   if (msgDate > m_newestMsgDate)
00209     SetNewestMsgDate(msgDate);
00210 
00211   child->AndFlags(~(MSG_FLAG_WATCHED | MSG_FLAG_IGNORED), &newHdrFlags);
00212   PRUint32 numChildren;
00213   
00214   // get the num children before we add the new header.
00215   GetNumChildren(&numChildren);
00216   
00217   // if this is an empty thread, set the root key to this header's key
00218   if (numChildren == 0)
00219     m_threadRootKey = newHdrKey;
00220   
00221   if (! (newHdrFlags & MSG_FLAG_READ))
00222     ChangeUnreadChildCount(1);
00223 
00224   return AddMsgHdrInDateOrder(child, view);
00225 }
00226 
00227 nsresult nsMsgGroupThread::ReparentNonReferenceChildrenOf(nsIMsgDBHdr *topLevelHdr, nsMsgKey newParentKey,
00228                                                             nsIDBChangeAnnouncer *announcer)
00229 {
00230 #if 0
00231   nsCOMPtr <nsIMsgDBHdr> curHdr;
00232   PRUint32 numChildren;
00233   PRUint32 childIndex = 0;
00234   
00235   GetNumChildren(&numChildren);
00236   for (childIndex = 0; childIndex < numChildren; childIndex++)
00237   {
00238     nsMsgKey msgKey;
00239     
00240     topLevelHdr->GetMessageKey(&msgKey);
00241     nsresult ret = GetChildHdrAt(childIndex, getter_AddRefs(curHdr));
00242     if (NS_SUCCEEDED(ret) && curHdr)
00243     {
00244       nsMsgKey oldThreadParent, curHdrKey;
00245       nsIMsgDBHdr *curMsgHdr = curHdr;
00246       curHdr->GetThreadParent(&oldThreadParent);
00247       curHdr->GetMessageKey(&curHdrKey);
00248       if (oldThreadParent == msgKey && curHdrKey != newParentKey && topLevelMsgHdr->IsParentOf(curHdr))
00249       {
00250         curHdr->GetThreadParent(&oldThreadParent);
00251         curHdr->SetThreadParent(newParentKey);
00252         // OK, this is a reparenting - need to send notification
00253         if (announcer)
00254           announcer->NotifyParentChangedAll(curHdrKey, oldThreadParent, newParentKey, nsnull);
00255       }
00256     }
00257   }
00258 #endif
00259   return NS_OK;
00260 }
00261 
00262 NS_IMETHODIMP nsMsgGroupThread::GetChildKeyAt(PRInt32 aIndex, nsMsgKey *aResult)
00263 {
00264   NS_ENSURE_ARG_POINTER(aResult);
00265   if (aIndex >= m_keys.GetSize())
00266     return NS_ERROR_INVALID_ARG;
00267   *aResult = m_keys[aIndex];
00268   return NS_OK;
00269 }
00270 
00271 NS_IMETHODIMP nsMsgGroupThread::GetChildAt(PRInt32 aIndex, nsIMsgDBHdr **aResult)
00272 {
00273   if (aIndex >= m_keys.GetSize())
00274     return NS_MSG_MESSAGE_NOT_FOUND;
00275   return m_db->GetMsgHdrForKey(m_keys[aIndex], aResult);
00276 }
00277 
00278 
00279 NS_IMETHODIMP nsMsgGroupThread::GetChild(nsMsgKey msgKey, nsIMsgDBHdr **aResult)
00280 {
00281   PRUint32 childIndex = m_keys.IndexOf(msgKey);
00282   return (childIndex != kNotFound) ? GetChildAt(childIndex, aResult) : NS_MSG_MESSAGE_NOT_FOUND;
00283 }
00284 
00285 
00286 NS_IMETHODIMP nsMsgGroupThread::GetChildHdrAt(PRInt32 aIndex, nsIMsgDBHdr **aResult)
00287 {
00288   return GetChildAt(aIndex, aResult);
00289 }
00290 
00291 
00292 NS_IMETHODIMP nsMsgGroupThread::RemoveChildAt(PRInt32 aIndex)
00293 {
00294   m_keys.RemoveAt(aIndex);
00295   return NS_OK;
00296 }
00297 
00298 
00299 nsresult nsMsgGroupThread::RemoveChild(nsMsgKey msgKey)
00300 {
00301   PRUint32 childIndex = m_keys.IndexOf(msgKey);
00302   if (childIndex != kNotFound)
00303     m_keys.RemoveAt(childIndex);
00304   return NS_OK;
00305 }
00306 
00307 NS_IMETHODIMP nsMsgGroupThread::RemoveChildHdr(nsIMsgDBHdr *child, nsIDBChangeAnnouncer *announcer)
00308 {
00309   PRUint32 flags;
00310   nsMsgKey key;
00311   nsMsgKey threadParent;
00312   
00313   if (!child)
00314     return NS_ERROR_NULL_POINTER;
00315   
00316   child->GetFlags(&flags);
00317   child->GetMessageKey(&key);
00318   
00319   child->GetThreadParent(&threadParent);
00320 //  ReparentChildrenOf(key, threadParent, announcer);
00321   
00322   // if this was the newest msg, clear the newest msg date so we'll recalc.
00323   PRUint32 date;
00324   child->GetDateInSeconds(&date);
00325   if (date == m_newestMsgDate)
00326     SetNewestMsgDate(0);
00327 
00328   if (!(flags & MSG_FLAG_READ))
00329     ChangeUnreadChildCount(-1);
00330   PRBool keyWasFirstKey = (m_keys.GetAt(0) == key);
00331   nsresult rv = RemoveChild(key);
00332  // if we're deleting the root of a dummy thread, need to update the threadKey
00333  // and the dummy header at position 0
00334  if (m_dummy && keyWasFirstKey && m_keys.GetSize() > 1)
00335     m_keys.SetAt(0, m_keys.GetAt(1));
00336 
00337  return rv;
00338 }
00339 
00340 nsresult nsMsgGroupThread::ReparentChildrenOf(nsMsgKey oldParent, nsMsgKey newParent, nsIDBChangeAnnouncer *announcer)
00341 {
00342   nsresult rv = NS_OK;
00343   
00344   PRUint32 numChildren;
00345   PRUint32 childIndex = 0;
00346   
00347   GetNumChildren(&numChildren);
00348   
00349   nsCOMPtr <nsIMsgDBHdr> curHdr;
00350   if (numChildren > 0)
00351   {
00352     for (childIndex = 0; childIndex < numChildren; childIndex++)
00353     {
00354       rv = GetChildHdrAt(childIndex, getter_AddRefs(curHdr));
00355       if (NS_SUCCEEDED(rv) && curHdr)
00356       {
00357         nsMsgKey threadParent;
00358         
00359         curHdr->GetThreadParent(&threadParent);
00360         if (threadParent == oldParent)
00361         {
00362           nsMsgKey curKey;
00363           
00364           curHdr->SetThreadParent(newParent);
00365           curHdr->GetMessageKey(&curKey);
00366           if (announcer)
00367             announcer->NotifyParentChangedAll(curKey, oldParent, newParent, nsnull);
00368           // if the old parent was the root of the thread, then only the first child gets 
00369           // promoted to root, and other children become children of the new root.
00370           if (newParent == nsMsgKey_None)
00371           {
00372             m_threadRootKey = curKey;
00373             newParent = curKey;
00374           }
00375         }
00376       }
00377     }
00378   }
00379   return rv;
00380 }
00381 
00382 NS_IMETHODIMP nsMsgGroupThread::MarkChildRead(PRBool bRead)
00383 {
00384   ChangeUnreadChildCount(bRead ? -1 : 1);
00385   return NS_OK;
00386 }
00387 
00388 // this could be moved into utils, because I think it's the same as the db impl.
00389 class nsMsgGroupThreadEnumerator : public nsISimpleEnumerator {
00390 public:
00391   NS_DECL_ISUPPORTS
00392     
00393   // nsISimpleEnumerator methods:
00394   NS_DECL_NSISIMPLEENUMERATOR
00395     
00396   // nsMsgGroupThreadEnumerator methods:
00397   typedef nsresult (*nsMsgGroupThreadEnumeratorFilter)(nsIMsgDBHdr* hdr, void* closure);
00398   
00399   nsMsgGroupThreadEnumerator(nsMsgGroupThread *thread, nsMsgKey startKey,
00400   nsMsgGroupThreadEnumeratorFilter filter, void* closure);
00401   PRInt32 MsgKeyFirstChildIndex(nsMsgKey inMsgKey);
00402   virtual ~nsMsgGroupThreadEnumerator();
00403   
00404 protected:
00405   
00406   nsresult                Prefetch();
00407   
00408   nsCOMPtr <nsIMsgDBHdr>  mResultHdr;
00409   nsMsgGroupThread*       mThread;
00410   nsMsgKey                mThreadParentKey;
00411   nsMsgKey                mFirstMsgKey;
00412   PRInt32                 mChildIndex;
00413   PRBool                  mDone;
00414   PRBool                  mNeedToPrefetch;
00415   nsMsgGroupThreadEnumeratorFilter     mFilter;
00416   void*                   mClosure;
00417   PRBool                  mFoundChildren;
00418 };
00419 
00420 nsMsgGroupThreadEnumerator::nsMsgGroupThreadEnumerator(nsMsgGroupThread *thread, nsMsgKey startKey,
00421                                              nsMsgGroupThreadEnumeratorFilter filter, void* closure)
00422                                              : mDone(PR_FALSE),
00423                                              mFilter(filter), mClosure(closure), mFoundChildren(PR_FALSE)
00424 {
00425   mThreadParentKey = startKey;
00426   mChildIndex = 0;
00427   mThread = thread;
00428   mNeedToPrefetch = PR_TRUE;
00429   mFirstMsgKey = nsMsgKey_None;
00430   
00431   nsresult rv = mThread->GetRootHdr(nsnull, getter_AddRefs(mResultHdr));
00432   
00433   if (NS_SUCCEEDED(rv) && mResultHdr)
00434     mResultHdr->GetMessageKey(&mFirstMsgKey);
00435   
00436   PRUint32 numChildren;
00437   mThread->GetNumChildren(&numChildren);
00438   
00439   if (mThreadParentKey != nsMsgKey_None)
00440   {
00441     nsMsgKey msgKey = nsMsgKey_None;
00442     PRUint32 childIndex = 0;
00443     
00444     
00445     for (childIndex = 0; childIndex < numChildren; childIndex++)
00446     {
00447       rv = mThread->GetChildHdrAt(childIndex, getter_AddRefs(mResultHdr));
00448       if (NS_SUCCEEDED(rv) && mResultHdr)
00449       {
00450         mResultHdr->GetMessageKey(&msgKey);
00451         
00452         if (msgKey == startKey)
00453         {
00454           mChildIndex = MsgKeyFirstChildIndex(msgKey);
00455           mDone = (mChildIndex < 0);
00456           break;
00457         }
00458         
00459         if (mDone)
00460           break;
00461         
00462       }
00463       else
00464         NS_ASSERTION(PR_FALSE, "couldn't get child from thread");
00465     }
00466   }
00467   
00468 #ifdef DEBUG_bienvenu1
00469   nsCOMPtr <nsIMsgDBHdr> child;
00470   for (PRUint32 childIndex = 0; childIndex < numChildren; childIndex++)
00471   {
00472     rv = mThread->GetChildHdrAt(childIndex, getter_AddRefs(child));
00473     if (NS_SUCCEEDED(rv) && child)
00474     {
00475       nsMsgKey threadParent;
00476       nsMsgKey msgKey;
00477       // we're only doing one level of threading, so check if caller is
00478       // asking for children of the first message in the thread or not.
00479       // if not, we will tell him there are no children.
00480       child->GetMessageKey(&msgKey);
00481       child->GetThreadParent(&threadParent);
00482       
00483       printf("index = %ld key = %ld parent = %lx\n", childIndex, msgKey, threadParent);
00484     }
00485   }
00486 #endif
00487   NS_ADDREF(thread);
00488 }
00489 
00490 nsMsgGroupThreadEnumerator::~nsMsgGroupThreadEnumerator()
00491 {
00492     NS_RELEASE(mThread);
00493 }
00494 
00495 NS_IMPL_ISUPPORTS1(nsMsgGroupThreadEnumerator, nsISimpleEnumerator)
00496 
00497 
00498 PRInt32 nsMsgGroupThreadEnumerator::MsgKeyFirstChildIndex(nsMsgKey inMsgKey)
00499 {
00500   //   if (msgKey != mThreadParentKey)
00501   //          mDone = PR_TRUE;
00502   // look through rest of thread looking for a child of this message.
00503   // If the inMsgKey is the first message in the thread, then all children
00504   // without parents are considered to be children of inMsgKey.
00505   // Otherwise, only true children qualify.
00506   PRUint32 numChildren;
00507   nsCOMPtr <nsIMsgDBHdr> curHdr;
00508   PRInt32 firstChildIndex = -1;
00509   
00510   mThread->GetNumChildren(&numChildren);
00511   
00512   // if this is the first message in the thread, just check if there's more than
00513   // one message in the thread.
00514   //   if (inMsgKey == mThread->m_threadRootKey)
00515   //          return (numChildren > 1) ? 1 : -1;
00516   
00517   for (PRUint32 curChildIndex = 0; curChildIndex < numChildren; curChildIndex++)
00518   {
00519     nsresult rv = mThread->GetChildHdrAt(curChildIndex, getter_AddRefs(curHdr));
00520     if (NS_SUCCEEDED(rv) && curHdr)
00521     {
00522       nsMsgKey parentKey;
00523       
00524       curHdr->GetThreadParent(&parentKey);
00525       if (parentKey == inMsgKey)
00526       {
00527         firstChildIndex = curChildIndex;
00528         break;
00529       }
00530     }
00531   }
00532 #ifdef DEBUG_bienvenu1
00533   printf("first child index of %ld = %ld\n", inMsgKey, firstChildIndex);
00534 #endif
00535   return firstChildIndex;
00536 }
00537 
00538 NS_IMETHODIMP nsMsgGroupThreadEnumerator::GetNext(nsISupports **aItem)
00539 {
00540   if (!aItem)
00541     return NS_ERROR_NULL_POINTER;
00542   nsresult rv = NS_OK;
00543   
00544   if (mNeedToPrefetch)
00545     rv = Prefetch();
00546   
00547   if (NS_SUCCEEDED(rv) && mResultHdr) 
00548   {
00549     *aItem = mResultHdr;
00550     NS_ADDREF(*aItem);
00551     mNeedToPrefetch = PR_TRUE;
00552   }
00553   return rv;
00554 }
00555 
00556 nsresult nsMsgGroupThreadEnumerator::Prefetch()
00557 {
00558   nsresult rv=NS_OK;          // XXX or should this default to an error?
00559   mResultHdr = nsnull;
00560   if (mThreadParentKey == nsMsgKey_None)
00561   {
00562     rv = mThread->GetRootHdr(&mChildIndex, getter_AddRefs(mResultHdr));
00563     NS_ASSERTION(NS_SUCCEEDED(rv) && mResultHdr, "better be able to get root hdr");
00564     mChildIndex = 0; // since root can be anywhere, set mChildIndex to 0.
00565   }
00566   else if (!mDone)
00567   {
00568     PRUint32 numChildren;
00569     mThread->GetNumChildren(&numChildren);
00570     
00571     while (mChildIndex < (PRInt32) numChildren)
00572     {
00573       rv  = mThread->GetChildHdrAt(mChildIndex++, getter_AddRefs(mResultHdr));
00574       if (NS_SUCCEEDED(rv) && mResultHdr)
00575       {
00576         nsMsgKey parentKey;
00577         nsMsgKey curKey;
00578         
00579         if (mFilter && NS_FAILED(mFilter(mResultHdr, mClosure))) {
00580           mResultHdr = nsnull;
00581           continue;
00582         }
00583         
00584         mResultHdr->GetThreadParent(&parentKey);
00585         mResultHdr->GetMessageKey(&curKey);
00586         // if the parent is the same as the msg we're enumerating over,
00587         // or the parentKey isn't set, and we're iterating over the top
00588         // level message in the thread, then leave mResultHdr set to cur msg.
00589         if (parentKey == mThreadParentKey || 
00590           (parentKey == nsMsgKey_None 
00591           && mThreadParentKey == mFirstMsgKey && curKey != mThreadParentKey))
00592           break;
00593         mResultHdr = nsnull;
00594       }
00595       else
00596         NS_ASSERTION(PR_FALSE, "better be able to get child");
00597     }
00598     if (!mResultHdr && mThreadParentKey == mFirstMsgKey && !mFoundChildren && numChildren > 1)
00599     {
00600 //      mThread->ReparentMsgsWithInvalidParent(numChildren, mThreadParentKey);
00601     }
00602   }
00603   if (!mResultHdr) 
00604   {
00605     mDone = PR_TRUE;
00606     return NS_ERROR_FAILURE;
00607   }
00608   if (NS_FAILED(rv)) 
00609   {
00610     mDone = PR_TRUE;
00611     return rv;
00612   }
00613   else
00614     mNeedToPrefetch = PR_FALSE;
00615   mFoundChildren = PR_TRUE;
00616 
00617 #ifdef DEBUG_bienvenu1
00618        nsMsgKey debugMsgKey;
00619        mResultHdr->GetMessageKey(&debugMsgKey);
00620        printf("next for %ld = %ld\n", mThreadParentKey, debugMsgKey);
00621 #endif
00622 
00623     return rv;
00624 }
00625 
00626 NS_IMETHODIMP nsMsgGroupThreadEnumerator::HasMoreElements(PRBool *aResult)
00627 {
00628   if (!aResult)
00629     return NS_ERROR_NULL_POINTER;
00630   if (mNeedToPrefetch)
00631     Prefetch();
00632   *aResult = !mDone;
00633   return NS_OK;
00634 }
00635 
00636 NS_IMETHODIMP nsMsgGroupThread::EnumerateMessages(nsMsgKey parentKey, nsISimpleEnumerator* *result)
00637 {
00638     nsMsgGroupThreadEnumerator* e = new nsMsgGroupThreadEnumerator(this, parentKey, nsnull, nsnull);
00639     if (e == nsnull)
00640         return NS_ERROR_OUT_OF_MEMORY;
00641     NS_ADDREF(e);
00642     *result = e;
00643 
00644     return NS_OK;
00645 }
00646 #if 0
00647 nsresult nsMsgGroupThread::ReparentMsgsWithInvalidParent(PRUint32 numChildren, nsMsgKey threadParentKey)
00648 {
00649   nsresult ret = NS_OK;
00650   // run through looking for messages that don't have a correct parent, 
00651   // i.e., a parent that's in the thread!
00652   for (PRInt32 childIndex = 0; childIndex < (PRInt32) numChildren; childIndex++)
00653   {
00654     nsCOMPtr <nsIMsgDBHdr> curChild;
00655     ret  = GetChildHdrAt(childIndex, getter_AddRefs(curChild));
00656     if (NS_SUCCEEDED(ret) && curChild)
00657     {
00658       nsMsgKey parentKey;
00659       nsCOMPtr <nsIMsgDBHdr> parent;
00660       
00661       curChild->GetThreadParent(&parentKey);
00662       
00663       if (parentKey != nsMsgKey_None)
00664       {
00665         GetChild(parentKey, getter_AddRefs(parent));
00666         if (!parent)
00667           curChild->SetThreadParent(threadParentKey);
00668       }
00669     }
00670   }
00671   return ret;
00672 }
00673 #endif
00674 NS_IMETHODIMP nsMsgGroupThread::GetRootHdr(PRInt32 *resultIndex, nsIMsgDBHdr **result)
00675 {
00676   if (!result)
00677     return NS_ERROR_NULL_POINTER;
00678   
00679   *result = nsnull;
00680   
00681   if (m_threadRootKey != nsMsgKey_None)
00682   {
00683     nsresult ret = GetChildHdrForKey(m_threadRootKey, result, resultIndex);
00684     if (NS_SUCCEEDED(ret) && *result)
00685       return ret;
00686     else
00687     {
00688       printf("need to reset thread root key\n");
00689       PRUint32 numChildren;
00690       nsMsgKey threadParentKey = nsMsgKey_None;
00691       GetNumChildren(&numChildren);
00692       
00693       for (PRInt32 childIndex = 0; childIndex < (PRInt32) numChildren; childIndex++)
00694       {
00695         nsCOMPtr <nsIMsgDBHdr> curChild;
00696         ret  = GetChildHdrAt(childIndex, getter_AddRefs(curChild));
00697         if (NS_SUCCEEDED(ret) && curChild)
00698         {
00699           nsMsgKey parentKey;
00700           
00701           curChild->GetThreadParent(&parentKey);
00702           if (parentKey == nsMsgKey_None)
00703           {
00704             NS_ASSERTION(!(*result), "two top level msgs, not good");
00705             curChild->GetMessageKey(&threadParentKey);
00706             m_threadRootKey = threadParentKey;
00707             if (resultIndex)
00708               *resultIndex = childIndex;
00709             *result = curChild;
00710             NS_ADDREF(*result);
00711 //            ReparentMsgsWithInvalidParent(numChildren, threadParentKey);
00712             //            return NS_OK;
00713           }
00714         }
00715       }
00716       if (*result)
00717       {
00718         return NS_OK;
00719       }
00720     }
00721     // if we can't get the thread root key, we'll just get the first hdr.
00722     // there's a bug where sometimes we weren't resetting the thread root key 
00723     // when removing the thread root key.
00724   }
00725   if (resultIndex)
00726     *resultIndex = 0;
00727   return GetChildHdrAt(0, result);
00728 }
00729 
00730 nsresult nsMsgGroupThread::ChangeUnreadChildCount(PRInt32 delta)
00731 {
00732   m_numUnreadChildren += delta;
00733   return NS_OK;
00734 }
00735 
00736 nsresult nsMsgGroupThread::GetChildHdrForKey(nsMsgKey desiredKey, nsIMsgDBHdr **result, PRInt32 *resultIndex)
00737 {
00738   PRUint32 numChildren;
00739   PRUint32 childIndex = 0;
00740   nsresult rv = NS_OK;        // XXX or should this default to an error?
00741   
00742   if (!result)
00743     return NS_ERROR_NULL_POINTER;
00744   
00745   GetNumChildren(&numChildren);
00746   
00747   if ((PRInt32) numChildren < 0)
00748     numChildren = 0;
00749   
00750   for (childIndex = 0; childIndex < numChildren; childIndex++)
00751   {
00752     rv = GetChildHdrAt(childIndex, result);
00753     if (NS_SUCCEEDED(rv) && *result)
00754     {
00755       nsMsgKey msgKey;
00756       // we're only doing one level of threading, so check if caller is
00757       // asking for children of the first message in the thread or not.
00758       // if not, we will tell him there are no children.
00759       (*result)->GetMessageKey(&msgKey);
00760       
00761       if (msgKey == desiredKey)
00762         break;
00763       NS_RELEASE(*result);
00764     }
00765   }
00766   if (resultIndex)
00767     *resultIndex = childIndex;
00768   
00769   return rv;
00770 }
00771 
00772 NS_IMETHODIMP nsMsgGroupThread::GetFirstUnreadChild(nsIMsgDBHdr **result)
00773 {
00774   NS_ENSURE_ARG(result);
00775   PRUint32 numChildren;
00776   nsresult rv = NS_OK;
00777   
00778   GetNumChildren(&numChildren);
00779   
00780   if ((PRInt32) numChildren < 0)
00781     numChildren = 0;
00782   
00783   for (PRUint32 childIndex = 0; childIndex < numChildren; childIndex++)
00784   {
00785     nsCOMPtr <nsIMsgDBHdr> child;
00786     rv = GetChildHdrAt(childIndex, getter_AddRefs(child));
00787     if (NS_SUCCEEDED(rv) && child)
00788     {
00789       nsMsgKey msgKey;
00790       child->GetMessageKey(&msgKey);
00791       
00792       PRBool isRead;
00793       rv = m_db->IsRead(msgKey, &isRead);
00794       if (NS_SUCCEEDED(rv) && !isRead)
00795       {
00796         *result = child;
00797         NS_ADDREF(*result);
00798         break;
00799       }
00800     }
00801   }
00802   
00803   return rv;
00804 }
00805 
00806 NS_IMETHODIMP nsMsgGroupThread::GetNewestMsgDate(PRUint32 *aResult) 
00807 {
00808   // if this hasn't been set, figure it out by enumerating the msgs in the thread.
00809   if (!m_newestMsgDate)
00810   {
00811     PRUint32 numChildren;
00812     nsresult rv = NS_OK;
00813   
00814     GetNumChildren(&numChildren);
00815   
00816     if ((PRInt32) numChildren < 0)
00817       numChildren = 0;
00818   
00819     for (PRUint32 childIndex = 0; childIndex < numChildren; childIndex++)
00820     {
00821       nsCOMPtr <nsIMsgDBHdr> child;
00822       rv = GetChildHdrAt(childIndex, getter_AddRefs(child));
00823       if (NS_SUCCEEDED(rv) && child)
00824       {
00825         PRUint32 msgDate;
00826         child->GetDateInSeconds(&msgDate);
00827         if (msgDate > m_newestMsgDate)
00828           m_newestMsgDate = msgDate;
00829       }
00830     }
00831   
00832   }
00833   *aResult = m_newestMsgDate;
00834   return NS_OK;
00835 }
00836 
00837 
00838 NS_IMETHODIMP nsMsgGroupThread::SetNewestMsgDate(PRUint32 aNewestMsgDate) 
00839 {
00840   m_newestMsgDate = aNewestMsgDate;
00841   return NS_OK;
00842 }
00843 
00844 nsMsgXFGroupThread::nsMsgXFGroupThread()
00845 {
00846 }
00847 
00848 nsMsgXFGroupThread::~nsMsgXFGroupThread()
00849 {
00850 }
00851 
00852 NS_IMETHODIMP nsMsgXFGroupThread::GetNumChildren(PRUint32 *aNumChildren)
00853 {
00854   NS_ENSURE_ARG_POINTER(aNumChildren);
00855   *aNumChildren = m_hdrs.Count();
00856   return NS_OK;
00857 }
00858 
00859 NS_IMETHODIMP nsMsgXFGroupThread::GetChildKeyAt(PRInt32 aIndex, nsMsgKey *aResult)
00860 {
00861   NS_ASSERTION(PR_FALSE, "shouldn't call this");
00862   return NS_ERROR_NOT_IMPLEMENTED;
00863 }
00864