mirror of
https://github.com/rn10950/RetroZilla.git
synced 2024-09-18 03:00:17 +02:00
865 lines
24 KiB
C++
865 lines
24 KiB
C++
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||
|
*
|
||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||
|
* the License. You may obtain a copy of the License at
|
||
|
* http://www.mozilla.org/MPL/
|
||
|
*
|
||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||
|
* for the specific language governing rights and limitations under the
|
||
|
* License.
|
||
|
*
|
||
|
* The Original Code is mozilla.org code.
|
||
|
*
|
||
|
* The Initial Developer of the Original Code is
|
||
|
* David Bienvenu.
|
||
|
* Portions created by the Initial Developer are Copyright (C) 2004
|
||
|
* the Initial Developer. All Rights Reserved.
|
||
|
*
|
||
|
* Contributor(s):
|
||
|
* David Bienvenu <bienvenu@nventure.com>
|
||
|
*
|
||
|
* Alternatively, the contents of this file may be used under the terms of
|
||
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||
|
* of those above. If you wish to allow use of your version of this file only
|
||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||
|
* use your version of this file under the terms of the MPL, indicate your
|
||
|
* decision by deleting the provisions above and replace them with the notice
|
||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||
|
* the provisions above, a recipient may use your version of this file under
|
||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||
|
*
|
||
|
* ***** END LICENSE BLOCK ***** */
|
||
|
|
||
|
#include "msgCore.h"
|
||
|
#include "nsMsgGroupThread.h"
|
||
|
#include "nsMsgDBView.h"
|
||
|
|
||
|
NS_IMPL_ISUPPORTS1(nsMsgGroupThread, nsIMsgThread)
|
||
|
|
||
|
nsMsgGroupThread::nsMsgGroupThread()
|
||
|
{
|
||
|
Init();
|
||
|
}
|
||
|
nsMsgGroupThread::nsMsgGroupThread(nsIMsgDatabase *db)
|
||
|
{
|
||
|
m_db = db;
|
||
|
Init();
|
||
|
}
|
||
|
|
||
|
void nsMsgGroupThread::Init()
|
||
|
{
|
||
|
m_threadKey = nsMsgKey_None;
|
||
|
m_threadRootKey = nsMsgKey_None;
|
||
|
m_numUnreadChildren = 0;
|
||
|
m_flags = 0;
|
||
|
m_newestMsgDate = 0;
|
||
|
m_dummy = PR_FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
nsMsgGroupThread::~nsMsgGroupThread()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
|
||
|
NS_IMETHODIMP nsMsgGroupThread::SetThreadKey(nsMsgKey threadKey)
|
||
|
{
|
||
|
m_threadKey = threadKey;
|
||
|
// by definition, the initial thread key is also the thread root key.
|
||
|
m_threadRootKey = threadKey;
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
NS_IMETHODIMP nsMsgGroupThread::GetThreadKey(nsMsgKey *aResult)
|
||
|
{
|
||
|
NS_ENSURE_ARG_POINTER(aResult);
|
||
|
*aResult = m_threadKey;
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
NS_IMETHODIMP nsMsgGroupThread::GetFlags(PRUint32 *aFlags)
|
||
|
{
|
||
|
NS_ENSURE_ARG_POINTER(aFlags);
|
||
|
*aFlags = m_flags;
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
NS_IMETHODIMP nsMsgGroupThread::SetFlags(PRUint32 aFlags)
|
||
|
{
|
||
|
m_flags = aFlags;
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
NS_IMETHODIMP nsMsgGroupThread::SetSubject(const char *subject)
|
||
|
{
|
||
|
NS_ASSERTION(PR_FALSE, "shouldn't call this");
|
||
|
return NS_ERROR_NOT_IMPLEMENTED;
|
||
|
}
|
||
|
|
||
|
NS_IMETHODIMP nsMsgGroupThread::GetSubject(char **result)
|
||
|
{
|
||
|
NS_ASSERTION(PR_FALSE, "shouldn't call this");
|
||
|
return NS_ERROR_NOT_IMPLEMENTED;
|
||
|
}
|
||
|
|
||
|
NS_IMETHODIMP nsMsgGroupThread::GetNumChildren(PRUint32 *aNumChildren)
|
||
|
{
|
||
|
NS_ENSURE_ARG_POINTER(aNumChildren);
|
||
|
*aNumChildren = m_keys.GetSize(); // - ((m_dummy) ? 1 : 0);
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
PRUint32 nsMsgGroupThread::NumRealChildren()
|
||
|
{
|
||
|
return m_keys.GetSize() - ((m_dummy) ? 1 : 0);
|
||
|
}
|
||
|
|
||
|
NS_IMETHODIMP nsMsgGroupThread::GetNumUnreadChildren (PRUint32 *aNumUnreadChildren)
|
||
|
{
|
||
|
NS_ENSURE_ARG_POINTER(aNumUnreadChildren);
|
||
|
*aNumUnreadChildren = m_numUnreadChildren;
|
||
|
return NS_OK;
|
||
|
}
|
||
|
#if 0
|
||
|
nsresult nsMsgGroupThread::RerootThread(nsIMsgDBHdr *newParentOfOldRoot, nsIMsgDBHdr *oldRoot, nsIDBChangeAnnouncer *announcer)
|
||
|
{
|
||
|
nsCOMPtr <nsIMsgDBHdr> ancestorHdr = newParentOfOldRoot;
|
||
|
nsMsgKey newRoot;
|
||
|
newParentOfOldRoot->GetMessageKey(&newRoot);
|
||
|
|
||
|
nsMsgKey newHdrAncestor;
|
||
|
nsresult rv = NS_OK;
|
||
|
// loop trying to find the oldest ancestor of this msg
|
||
|
// that is a parent of the root. The oldest ancestor will
|
||
|
// become the root of the thread.
|
||
|
do
|
||
|
{
|
||
|
ancestorHdr->GetThreadParent(&newHdrAncestor);
|
||
|
if (newHdrAncestor != nsMsgKey_None && newHdrAncestor != m_threadRootKey && newHdrAncestor != newRoot)
|
||
|
{
|
||
|
newRoot = newHdrAncestor;
|
||
|
rv = m_db->GetMsgHdrForKey(newRoot, getter_AddRefs(ancestorHdr));
|
||
|
}
|
||
|
}
|
||
|
while (NS_SUCCEEDED(rv) && ancestorHdr && newHdrAncestor != nsMsgKey_None && newHdrAncestor != m_threadRootKey
|
||
|
&& newHdrAncestor != newRoot);
|
||
|
m_threadRootKey = newRoot;
|
||
|
// ReparentNonReferenceChildrenOf(oldRoot, newRoot, announcer);
|
||
|
if (ancestorHdr)
|
||
|
{
|
||
|
// move the root hdr to pos 0 by removing it and adding it at 0.
|
||
|
m_keys.RemoveElement(newRoot);
|
||
|
m_keys.InsertAt(0, newRoot);
|
||
|
ancestorHdr->SetThreadParent(nsMsgKey_None);
|
||
|
}
|
||
|
return rv;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
NS_IMETHODIMP nsMsgGroupThread::AddChild(nsIMsgDBHdr *child, nsIMsgDBHdr *inReplyTo, PRBool threadInThread,
|
||
|
nsIDBChangeAnnouncer *announcer)
|
||
|
{
|
||
|
NS_ASSERTION(PR_FALSE, "shouldn't call this");
|
||
|
return NS_ERROR_NOT_IMPLEMENTED;
|
||
|
}
|
||
|
|
||
|
nsresult nsMsgGroupThread::AddMsgHdrInDateOrder(nsIMsgDBHdr *child, nsMsgDBView *view)
|
||
|
{
|
||
|
nsresult ret = NS_OK;
|
||
|
nsMsgKey newHdrKey;
|
||
|
child->GetMessageKey(&newHdrKey);
|
||
|
PRUint32 insertIndex = 0;
|
||
|
// since we're sorted by date, we could do a binary search for the
|
||
|
// insert point. Or, we could start at the end...
|
||
|
if (m_keys.GetSize() > 0)
|
||
|
{
|
||
|
nsMsgViewSortTypeValue sortType;
|
||
|
nsMsgViewSortOrderValue sortOrder;
|
||
|
(void) view->GetSortType(&sortType);
|
||
|
(void) view->GetSortOrder(&sortOrder);
|
||
|
nsMsgViewSortOrderValue threadSortOrder =
|
||
|
(sortType == nsMsgViewSortType::byDate
|
||
|
&& sortOrder == nsMsgViewSortOrder::descending) ?
|
||
|
nsMsgViewSortOrder::descending : nsMsgViewSortOrder::ascending;
|
||
|
// sort by date within group
|
||
|
insertIndex = view->GetInsertIndexHelper(child, &m_keys, threadSortOrder, nsMsgViewSortType::byDate);
|
||
|
}
|
||
|
m_keys.InsertAt(insertIndex, newHdrKey);
|
||
|
if (!insertIndex)
|
||
|
m_threadRootKey = newHdrKey;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
nsresult nsMsgGroupThread::AddChildFromGroupView(nsIMsgDBHdr *child, nsMsgDBView *view)
|
||
|
{
|
||
|
PRUint32 newHdrFlags = 0;
|
||
|
PRUint32 msgDate;
|
||
|
nsMsgKey newHdrKey = 0;
|
||
|
|
||
|
child->GetFlags(&newHdrFlags);
|
||
|
child->GetMessageKey(&newHdrKey);
|
||
|
child->GetDateInSeconds(&msgDate);
|
||
|
if (msgDate > m_newestMsgDate)
|
||
|
SetNewestMsgDate(msgDate);
|
||
|
|
||
|
child->AndFlags(~(MSG_FLAG_WATCHED | MSG_FLAG_IGNORED), &newHdrFlags);
|
||
|
PRUint32 numChildren;
|
||
|
|
||
|
// get the num children before we add the new header.
|
||
|
GetNumChildren(&numChildren);
|
||
|
|
||
|
// if this is an empty thread, set the root key to this header's key
|
||
|
if (numChildren == 0)
|
||
|
m_threadRootKey = newHdrKey;
|
||
|
|
||
|
if (! (newHdrFlags & MSG_FLAG_READ))
|
||
|
ChangeUnreadChildCount(1);
|
||
|
|
||
|
return AddMsgHdrInDateOrder(child, view);
|
||
|
}
|
||
|
|
||
|
nsresult nsMsgGroupThread::ReparentNonReferenceChildrenOf(nsIMsgDBHdr *topLevelHdr, nsMsgKey newParentKey,
|
||
|
nsIDBChangeAnnouncer *announcer)
|
||
|
{
|
||
|
#if 0
|
||
|
nsCOMPtr <nsIMsgDBHdr> curHdr;
|
||
|
PRUint32 numChildren;
|
||
|
PRUint32 childIndex = 0;
|
||
|
|
||
|
GetNumChildren(&numChildren);
|
||
|
for (childIndex = 0; childIndex < numChildren; childIndex++)
|
||
|
{
|
||
|
nsMsgKey msgKey;
|
||
|
|
||
|
topLevelHdr->GetMessageKey(&msgKey);
|
||
|
nsresult ret = GetChildHdrAt(childIndex, getter_AddRefs(curHdr));
|
||
|
if (NS_SUCCEEDED(ret) && curHdr)
|
||
|
{
|
||
|
nsMsgKey oldThreadParent, curHdrKey;
|
||
|
nsIMsgDBHdr *curMsgHdr = curHdr;
|
||
|
curHdr->GetThreadParent(&oldThreadParent);
|
||
|
curHdr->GetMessageKey(&curHdrKey);
|
||
|
if (oldThreadParent == msgKey && curHdrKey != newParentKey && topLevelMsgHdr->IsParentOf(curHdr))
|
||
|
{
|
||
|
curHdr->GetThreadParent(&oldThreadParent);
|
||
|
curHdr->SetThreadParent(newParentKey);
|
||
|
// OK, this is a reparenting - need to send notification
|
||
|
if (announcer)
|
||
|
announcer->NotifyParentChangedAll(curHdrKey, oldThreadParent, newParentKey, nsnull);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
NS_IMETHODIMP nsMsgGroupThread::GetChildKeyAt(PRInt32 aIndex, nsMsgKey *aResult)
|
||
|
{
|
||
|
NS_ENSURE_ARG_POINTER(aResult);
|
||
|
if (aIndex >= m_keys.GetSize())
|
||
|
return NS_ERROR_INVALID_ARG;
|
||
|
*aResult = m_keys[aIndex];
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
NS_IMETHODIMP nsMsgGroupThread::GetChildAt(PRInt32 aIndex, nsIMsgDBHdr **aResult)
|
||
|
{
|
||
|
if (aIndex >= m_keys.GetSize())
|
||
|
return NS_MSG_MESSAGE_NOT_FOUND;
|
||
|
return m_db->GetMsgHdrForKey(m_keys[aIndex], aResult);
|
||
|
}
|
||
|
|
||
|
|
||
|
NS_IMETHODIMP nsMsgGroupThread::GetChild(nsMsgKey msgKey, nsIMsgDBHdr **aResult)
|
||
|
{
|
||
|
PRUint32 childIndex = m_keys.IndexOf(msgKey);
|
||
|
return (childIndex != kNotFound) ? GetChildAt(childIndex, aResult) : NS_MSG_MESSAGE_NOT_FOUND;
|
||
|
}
|
||
|
|
||
|
|
||
|
NS_IMETHODIMP nsMsgGroupThread::GetChildHdrAt(PRInt32 aIndex, nsIMsgDBHdr **aResult)
|
||
|
{
|
||
|
return GetChildAt(aIndex, aResult);
|
||
|
}
|
||
|
|
||
|
|
||
|
NS_IMETHODIMP nsMsgGroupThread::RemoveChildAt(PRInt32 aIndex)
|
||
|
{
|
||
|
m_keys.RemoveAt(aIndex);
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
nsresult nsMsgGroupThread::RemoveChild(nsMsgKey msgKey)
|
||
|
{
|
||
|
PRUint32 childIndex = m_keys.IndexOf(msgKey);
|
||
|
if (childIndex != kNotFound)
|
||
|
m_keys.RemoveAt(childIndex);
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
NS_IMETHODIMP nsMsgGroupThread::RemoveChildHdr(nsIMsgDBHdr *child, nsIDBChangeAnnouncer *announcer)
|
||
|
{
|
||
|
PRUint32 flags;
|
||
|
nsMsgKey key;
|
||
|
nsMsgKey threadParent;
|
||
|
|
||
|
if (!child)
|
||
|
return NS_ERROR_NULL_POINTER;
|
||
|
|
||
|
child->GetFlags(&flags);
|
||
|
child->GetMessageKey(&key);
|
||
|
|
||
|
child->GetThreadParent(&threadParent);
|
||
|
// ReparentChildrenOf(key, threadParent, announcer);
|
||
|
|
||
|
// if this was the newest msg, clear the newest msg date so we'll recalc.
|
||
|
PRUint32 date;
|
||
|
child->GetDateInSeconds(&date);
|
||
|
if (date == m_newestMsgDate)
|
||
|
SetNewestMsgDate(0);
|
||
|
|
||
|
if (!(flags & MSG_FLAG_READ))
|
||
|
ChangeUnreadChildCount(-1);
|
||
|
PRBool keyWasFirstKey = (m_keys.GetAt(0) == key);
|
||
|
nsresult rv = RemoveChild(key);
|
||
|
// if we're deleting the root of a dummy thread, need to update the threadKey
|
||
|
// and the dummy header at position 0
|
||
|
if (m_dummy && keyWasFirstKey && m_keys.GetSize() > 1)
|
||
|
m_keys.SetAt(0, m_keys.GetAt(1));
|
||
|
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
nsresult nsMsgGroupThread::ReparentChildrenOf(nsMsgKey oldParent, nsMsgKey newParent, nsIDBChangeAnnouncer *announcer)
|
||
|
{
|
||
|
nsresult rv = NS_OK;
|
||
|
|
||
|
PRUint32 numChildren;
|
||
|
PRUint32 childIndex = 0;
|
||
|
|
||
|
GetNumChildren(&numChildren);
|
||
|
|
||
|
nsCOMPtr <nsIMsgDBHdr> curHdr;
|
||
|
if (numChildren > 0)
|
||
|
{
|
||
|
for (childIndex = 0; childIndex < numChildren; childIndex++)
|
||
|
{
|
||
|
rv = GetChildHdrAt(childIndex, getter_AddRefs(curHdr));
|
||
|
if (NS_SUCCEEDED(rv) && curHdr)
|
||
|
{
|
||
|
nsMsgKey threadParent;
|
||
|
|
||
|
curHdr->GetThreadParent(&threadParent);
|
||
|
if (threadParent == oldParent)
|
||
|
{
|
||
|
nsMsgKey curKey;
|
||
|
|
||
|
curHdr->SetThreadParent(newParent);
|
||
|
curHdr->GetMessageKey(&curKey);
|
||
|
if (announcer)
|
||
|
announcer->NotifyParentChangedAll(curKey, oldParent, newParent, nsnull);
|
||
|
// if the old parent was the root of the thread, then only the first child gets
|
||
|
// promoted to root, and other children become children of the new root.
|
||
|
if (newParent == nsMsgKey_None)
|
||
|
{
|
||
|
m_threadRootKey = curKey;
|
||
|
newParent = curKey;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
NS_IMETHODIMP nsMsgGroupThread::MarkChildRead(PRBool bRead)
|
||
|
{
|
||
|
ChangeUnreadChildCount(bRead ? -1 : 1);
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
// this could be moved into utils, because I think it's the same as the db impl.
|
||
|
class nsMsgGroupThreadEnumerator : public nsISimpleEnumerator {
|
||
|
public:
|
||
|
NS_DECL_ISUPPORTS
|
||
|
|
||
|
// nsISimpleEnumerator methods:
|
||
|
NS_DECL_NSISIMPLEENUMERATOR
|
||
|
|
||
|
// nsMsgGroupThreadEnumerator methods:
|
||
|
typedef nsresult (*nsMsgGroupThreadEnumeratorFilter)(nsIMsgDBHdr* hdr, void* closure);
|
||
|
|
||
|
nsMsgGroupThreadEnumerator(nsMsgGroupThread *thread, nsMsgKey startKey,
|
||
|
nsMsgGroupThreadEnumeratorFilter filter, void* closure);
|
||
|
PRInt32 MsgKeyFirstChildIndex(nsMsgKey inMsgKey);
|
||
|
virtual ~nsMsgGroupThreadEnumerator();
|
||
|
|
||
|
protected:
|
||
|
|
||
|
nsresult Prefetch();
|
||
|
|
||
|
nsCOMPtr <nsIMsgDBHdr> mResultHdr;
|
||
|
nsMsgGroupThread* mThread;
|
||
|
nsMsgKey mThreadParentKey;
|
||
|
nsMsgKey mFirstMsgKey;
|
||
|
PRInt32 mChildIndex;
|
||
|
PRBool mDone;
|
||
|
PRBool mNeedToPrefetch;
|
||
|
nsMsgGroupThreadEnumeratorFilter mFilter;
|
||
|
void* mClosure;
|
||
|
PRBool mFoundChildren;
|
||
|
};
|
||
|
|
||
|
nsMsgGroupThreadEnumerator::nsMsgGroupThreadEnumerator(nsMsgGroupThread *thread, nsMsgKey startKey,
|
||
|
nsMsgGroupThreadEnumeratorFilter filter, void* closure)
|
||
|
: mDone(PR_FALSE),
|
||
|
mFilter(filter), mClosure(closure), mFoundChildren(PR_FALSE)
|
||
|
{
|
||
|
mThreadParentKey = startKey;
|
||
|
mChildIndex = 0;
|
||
|
mThread = thread;
|
||
|
mNeedToPrefetch = PR_TRUE;
|
||
|
mFirstMsgKey = nsMsgKey_None;
|
||
|
|
||
|
nsresult rv = mThread->GetRootHdr(nsnull, getter_AddRefs(mResultHdr));
|
||
|
|
||
|
if (NS_SUCCEEDED(rv) && mResultHdr)
|
||
|
mResultHdr->GetMessageKey(&mFirstMsgKey);
|
||
|
|
||
|
PRUint32 numChildren;
|
||
|
mThread->GetNumChildren(&numChildren);
|
||
|
|
||
|
if (mThreadParentKey != nsMsgKey_None)
|
||
|
{
|
||
|
nsMsgKey msgKey = nsMsgKey_None;
|
||
|
PRUint32 childIndex = 0;
|
||
|
|
||
|
|
||
|
for (childIndex = 0; childIndex < numChildren; childIndex++)
|
||
|
{
|
||
|
rv = mThread->GetChildHdrAt(childIndex, getter_AddRefs(mResultHdr));
|
||
|
if (NS_SUCCEEDED(rv) && mResultHdr)
|
||
|
{
|
||
|
mResultHdr->GetMessageKey(&msgKey);
|
||
|
|
||
|
if (msgKey == startKey)
|
||
|
{
|
||
|
mChildIndex = MsgKeyFirstChildIndex(msgKey);
|
||
|
mDone = (mChildIndex < 0);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (mDone)
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
else
|
||
|
NS_ASSERTION(PR_FALSE, "couldn't get child from thread");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUG_bienvenu1
|
||
|
nsCOMPtr <nsIMsgDBHdr> child;
|
||
|
for (PRUint32 childIndex = 0; childIndex < numChildren; childIndex++)
|
||
|
{
|
||
|
rv = mThread->GetChildHdrAt(childIndex, getter_AddRefs(child));
|
||
|
if (NS_SUCCEEDED(rv) && child)
|
||
|
{
|
||
|
nsMsgKey threadParent;
|
||
|
nsMsgKey msgKey;
|
||
|
// we're only doing one level of threading, so check if caller is
|
||
|
// asking for children of the first message in the thread or not.
|
||
|
// if not, we will tell him there are no children.
|
||
|
child->GetMessageKey(&msgKey);
|
||
|
child->GetThreadParent(&threadParent);
|
||
|
|
||
|
printf("index = %ld key = %ld parent = %lx\n", childIndex, msgKey, threadParent);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
NS_ADDREF(thread);
|
||
|
}
|
||
|
|
||
|
nsMsgGroupThreadEnumerator::~nsMsgGroupThreadEnumerator()
|
||
|
{
|
||
|
NS_RELEASE(mThread);
|
||
|
}
|
||
|
|
||
|
NS_IMPL_ISUPPORTS1(nsMsgGroupThreadEnumerator, nsISimpleEnumerator)
|
||
|
|
||
|
|
||
|
PRInt32 nsMsgGroupThreadEnumerator::MsgKeyFirstChildIndex(nsMsgKey inMsgKey)
|
||
|
{
|
||
|
// if (msgKey != mThreadParentKey)
|
||
|
// mDone = PR_TRUE;
|
||
|
// look through rest of thread looking for a child of this message.
|
||
|
// If the inMsgKey is the first message in the thread, then all children
|
||
|
// without parents are considered to be children of inMsgKey.
|
||
|
// Otherwise, only true children qualify.
|
||
|
PRUint32 numChildren;
|
||
|
nsCOMPtr <nsIMsgDBHdr> curHdr;
|
||
|
PRInt32 firstChildIndex = -1;
|
||
|
|
||
|
mThread->GetNumChildren(&numChildren);
|
||
|
|
||
|
// if this is the first message in the thread, just check if there's more than
|
||
|
// one message in the thread.
|
||
|
// if (inMsgKey == mThread->m_threadRootKey)
|
||
|
// return (numChildren > 1) ? 1 : -1;
|
||
|
|
||
|
for (PRUint32 curChildIndex = 0; curChildIndex < numChildren; curChildIndex++)
|
||
|
{
|
||
|
nsresult rv = mThread->GetChildHdrAt(curChildIndex, getter_AddRefs(curHdr));
|
||
|
if (NS_SUCCEEDED(rv) && curHdr)
|
||
|
{
|
||
|
nsMsgKey parentKey;
|
||
|
|
||
|
curHdr->GetThreadParent(&parentKey);
|
||
|
if (parentKey == inMsgKey)
|
||
|
{
|
||
|
firstChildIndex = curChildIndex;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#ifdef DEBUG_bienvenu1
|
||
|
printf("first child index of %ld = %ld\n", inMsgKey, firstChildIndex);
|
||
|
#endif
|
||
|
return firstChildIndex;
|
||
|
}
|
||
|
|
||
|
NS_IMETHODIMP nsMsgGroupThreadEnumerator::GetNext(nsISupports **aItem)
|
||
|
{
|
||
|
if (!aItem)
|
||
|
return NS_ERROR_NULL_POINTER;
|
||
|
nsresult rv = NS_OK;
|
||
|
|
||
|
if (mNeedToPrefetch)
|
||
|
rv = Prefetch();
|
||
|
|
||
|
if (NS_SUCCEEDED(rv) && mResultHdr)
|
||
|
{
|
||
|
*aItem = mResultHdr;
|
||
|
NS_ADDREF(*aItem);
|
||
|
mNeedToPrefetch = PR_TRUE;
|
||
|
}
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
nsresult nsMsgGroupThreadEnumerator::Prefetch()
|
||
|
{
|
||
|
nsresult rv=NS_OK; // XXX or should this default to an error?
|
||
|
mResultHdr = nsnull;
|
||
|
if (mThreadParentKey == nsMsgKey_None)
|
||
|
{
|
||
|
rv = mThread->GetRootHdr(&mChildIndex, getter_AddRefs(mResultHdr));
|
||
|
NS_ASSERTION(NS_SUCCEEDED(rv) && mResultHdr, "better be able to get root hdr");
|
||
|
mChildIndex = 0; // since root can be anywhere, set mChildIndex to 0.
|
||
|
}
|
||
|
else if (!mDone)
|
||
|
{
|
||
|
PRUint32 numChildren;
|
||
|
mThread->GetNumChildren(&numChildren);
|
||
|
|
||
|
while (mChildIndex < (PRInt32) numChildren)
|
||
|
{
|
||
|
rv = mThread->GetChildHdrAt(mChildIndex++, getter_AddRefs(mResultHdr));
|
||
|
if (NS_SUCCEEDED(rv) && mResultHdr)
|
||
|
{
|
||
|
nsMsgKey parentKey;
|
||
|
nsMsgKey curKey;
|
||
|
|
||
|
if (mFilter && NS_FAILED(mFilter(mResultHdr, mClosure))) {
|
||
|
mResultHdr = nsnull;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
mResultHdr->GetThreadParent(&parentKey);
|
||
|
mResultHdr->GetMessageKey(&curKey);
|
||
|
// if the parent is the same as the msg we're enumerating over,
|
||
|
// or the parentKey isn't set, and we're iterating over the top
|
||
|
// level message in the thread, then leave mResultHdr set to cur msg.
|
||
|
if (parentKey == mThreadParentKey ||
|
||
|
(parentKey == nsMsgKey_None
|
||
|
&& mThreadParentKey == mFirstMsgKey && curKey != mThreadParentKey))
|
||
|
break;
|
||
|
mResultHdr = nsnull;
|
||
|
}
|
||
|
else
|
||
|
NS_ASSERTION(PR_FALSE, "better be able to get child");
|
||
|
}
|
||
|
if (!mResultHdr && mThreadParentKey == mFirstMsgKey && !mFoundChildren && numChildren > 1)
|
||
|
{
|
||
|
// mThread->ReparentMsgsWithInvalidParent(numChildren, mThreadParentKey);
|
||
|
}
|
||
|
}
|
||
|
if (!mResultHdr)
|
||
|
{
|
||
|
mDone = PR_TRUE;
|
||
|
return NS_ERROR_FAILURE;
|
||
|
}
|
||
|
if (NS_FAILED(rv))
|
||
|
{
|
||
|
mDone = PR_TRUE;
|
||
|
return rv;
|
||
|
}
|
||
|
else
|
||
|
mNeedToPrefetch = PR_FALSE;
|
||
|
mFoundChildren = PR_TRUE;
|
||
|
|
||
|
#ifdef DEBUG_bienvenu1
|
||
|
nsMsgKey debugMsgKey;
|
||
|
mResultHdr->GetMessageKey(&debugMsgKey);
|
||
|
printf("next for %ld = %ld\n", mThreadParentKey, debugMsgKey);
|
||
|
#endif
|
||
|
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
NS_IMETHODIMP nsMsgGroupThreadEnumerator::HasMoreElements(PRBool *aResult)
|
||
|
{
|
||
|
if (!aResult)
|
||
|
return NS_ERROR_NULL_POINTER;
|
||
|
if (mNeedToPrefetch)
|
||
|
Prefetch();
|
||
|
*aResult = !mDone;
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
NS_IMETHODIMP nsMsgGroupThread::EnumerateMessages(nsMsgKey parentKey, nsISimpleEnumerator* *result)
|
||
|
{
|
||
|
nsMsgGroupThreadEnumerator* e = new nsMsgGroupThreadEnumerator(this, parentKey, nsnull, nsnull);
|
||
|
if (e == nsnull)
|
||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||
|
NS_ADDREF(e);
|
||
|
*result = e;
|
||
|
|
||
|
return NS_OK;
|
||
|
}
|
||
|
#if 0
|
||
|
nsresult nsMsgGroupThread::ReparentMsgsWithInvalidParent(PRUint32 numChildren, nsMsgKey threadParentKey)
|
||
|
{
|
||
|
nsresult ret = NS_OK;
|
||
|
// run through looking for messages that don't have a correct parent,
|
||
|
// i.e., a parent that's in the thread!
|
||
|
for (PRInt32 childIndex = 0; childIndex < (PRInt32) numChildren; childIndex++)
|
||
|
{
|
||
|
nsCOMPtr <nsIMsgDBHdr> curChild;
|
||
|
ret = GetChildHdrAt(childIndex, getter_AddRefs(curChild));
|
||
|
if (NS_SUCCEEDED(ret) && curChild)
|
||
|
{
|
||
|
nsMsgKey parentKey;
|
||
|
nsCOMPtr <nsIMsgDBHdr> parent;
|
||
|
|
||
|
curChild->GetThreadParent(&parentKey);
|
||
|
|
||
|
if (parentKey != nsMsgKey_None)
|
||
|
{
|
||
|
GetChild(parentKey, getter_AddRefs(parent));
|
||
|
if (!parent)
|
||
|
curChild->SetThreadParent(threadParentKey);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
#endif
|
||
|
NS_IMETHODIMP nsMsgGroupThread::GetRootHdr(PRInt32 *resultIndex, nsIMsgDBHdr **result)
|
||
|
{
|
||
|
if (!result)
|
||
|
return NS_ERROR_NULL_POINTER;
|
||
|
|
||
|
*result = nsnull;
|
||
|
|
||
|
if (m_threadRootKey != nsMsgKey_None)
|
||
|
{
|
||
|
nsresult ret = GetChildHdrForKey(m_threadRootKey, result, resultIndex);
|
||
|
if (NS_SUCCEEDED(ret) && *result)
|
||
|
return ret;
|
||
|
else
|
||
|
{
|
||
|
printf("need to reset thread root key\n");
|
||
|
PRUint32 numChildren;
|
||
|
nsMsgKey threadParentKey = nsMsgKey_None;
|
||
|
GetNumChildren(&numChildren);
|
||
|
|
||
|
for (PRInt32 childIndex = 0; childIndex < (PRInt32) numChildren; childIndex++)
|
||
|
{
|
||
|
nsCOMPtr <nsIMsgDBHdr> curChild;
|
||
|
ret = GetChildHdrAt(childIndex, getter_AddRefs(curChild));
|
||
|
if (NS_SUCCEEDED(ret) && curChild)
|
||
|
{
|
||
|
nsMsgKey parentKey;
|
||
|
|
||
|
curChild->GetThreadParent(&parentKey);
|
||
|
if (parentKey == nsMsgKey_None)
|
||
|
{
|
||
|
NS_ASSERTION(!(*result), "two top level msgs, not good");
|
||
|
curChild->GetMessageKey(&threadParentKey);
|
||
|
m_threadRootKey = threadParentKey;
|
||
|
if (resultIndex)
|
||
|
*resultIndex = childIndex;
|
||
|
*result = curChild;
|
||
|
NS_ADDREF(*result);
|
||
|
// ReparentMsgsWithInvalidParent(numChildren, threadParentKey);
|
||
|
// return NS_OK;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (*result)
|
||
|
{
|
||
|
return NS_OK;
|
||
|
}
|
||
|
}
|
||
|
// if we can't get the thread root key, we'll just get the first hdr.
|
||
|
// there's a bug where sometimes we weren't resetting the thread root key
|
||
|
// when removing the thread root key.
|
||
|
}
|
||
|
if (resultIndex)
|
||
|
*resultIndex = 0;
|
||
|
return GetChildHdrAt(0, result);
|
||
|
}
|
||
|
|
||
|
nsresult nsMsgGroupThread::ChangeUnreadChildCount(PRInt32 delta)
|
||
|
{
|
||
|
m_numUnreadChildren += delta;
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
nsresult nsMsgGroupThread::GetChildHdrForKey(nsMsgKey desiredKey, nsIMsgDBHdr **result, PRInt32 *resultIndex)
|
||
|
{
|
||
|
PRUint32 numChildren;
|
||
|
PRUint32 childIndex = 0;
|
||
|
nsresult rv = NS_OK; // XXX or should this default to an error?
|
||
|
|
||
|
if (!result)
|
||
|
return NS_ERROR_NULL_POINTER;
|
||
|
|
||
|
GetNumChildren(&numChildren);
|
||
|
|
||
|
if ((PRInt32) numChildren < 0)
|
||
|
numChildren = 0;
|
||
|
|
||
|
for (childIndex = 0; childIndex < numChildren; childIndex++)
|
||
|
{
|
||
|
rv = GetChildHdrAt(childIndex, result);
|
||
|
if (NS_SUCCEEDED(rv) && *result)
|
||
|
{
|
||
|
nsMsgKey msgKey;
|
||
|
// we're only doing one level of threading, so check if caller is
|
||
|
// asking for children of the first message in the thread or not.
|
||
|
// if not, we will tell him there are no children.
|
||
|
(*result)->GetMessageKey(&msgKey);
|
||
|
|
||
|
if (msgKey == desiredKey)
|
||
|
break;
|
||
|
NS_RELEASE(*result);
|
||
|
}
|
||
|
}
|
||
|
if (resultIndex)
|
||
|
*resultIndex = childIndex;
|
||
|
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
NS_IMETHODIMP nsMsgGroupThread::GetFirstUnreadChild(nsIMsgDBHdr **result)
|
||
|
{
|
||
|
NS_ENSURE_ARG(result);
|
||
|
PRUint32 numChildren;
|
||
|
nsresult rv = NS_OK;
|
||
|
|
||
|
GetNumChildren(&numChildren);
|
||
|
|
||
|
if ((PRInt32) numChildren < 0)
|
||
|
numChildren = 0;
|
||
|
|
||
|
for (PRUint32 childIndex = 0; childIndex < numChildren; childIndex++)
|
||
|
{
|
||
|
nsCOMPtr <nsIMsgDBHdr> child;
|
||
|
rv = GetChildHdrAt(childIndex, getter_AddRefs(child));
|
||
|
if (NS_SUCCEEDED(rv) && child)
|
||
|
{
|
||
|
nsMsgKey msgKey;
|
||
|
child->GetMessageKey(&msgKey);
|
||
|
|
||
|
PRBool isRead;
|
||
|
rv = m_db->IsRead(msgKey, &isRead);
|
||
|
if (NS_SUCCEEDED(rv) && !isRead)
|
||
|
{
|
||
|
*result = child;
|
||
|
NS_ADDREF(*result);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return rv;
|
||
|
}
|
||
|
|
||
|
NS_IMETHODIMP nsMsgGroupThread::GetNewestMsgDate(PRUint32 *aResult)
|
||
|
{
|
||
|
// if this hasn't been set, figure it out by enumerating the msgs in the thread.
|
||
|
if (!m_newestMsgDate)
|
||
|
{
|
||
|
PRUint32 numChildren;
|
||
|
nsresult rv = NS_OK;
|
||
|
|
||
|
GetNumChildren(&numChildren);
|
||
|
|
||
|
if ((PRInt32) numChildren < 0)
|
||
|
numChildren = 0;
|
||
|
|
||
|
for (PRUint32 childIndex = 0; childIndex < numChildren; childIndex++)
|
||
|
{
|
||
|
nsCOMPtr <nsIMsgDBHdr> child;
|
||
|
rv = GetChildHdrAt(childIndex, getter_AddRefs(child));
|
||
|
if (NS_SUCCEEDED(rv) && child)
|
||
|
{
|
||
|
PRUint32 msgDate;
|
||
|
child->GetDateInSeconds(&msgDate);
|
||
|
if (msgDate > m_newestMsgDate)
|
||
|
m_newestMsgDate = msgDate;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
*aResult = m_newestMsgDate;
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
NS_IMETHODIMP nsMsgGroupThread::SetNewestMsgDate(PRUint32 aNewestMsgDate)
|
||
|
{
|
||
|
m_newestMsgDate = aNewestMsgDate;
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
nsMsgXFGroupThread::nsMsgXFGroupThread()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
nsMsgXFGroupThread::~nsMsgXFGroupThread()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
NS_IMETHODIMP nsMsgXFGroupThread::GetNumChildren(PRUint32 *aNumChildren)
|
||
|
{
|
||
|
NS_ENSURE_ARG_POINTER(aNumChildren);
|
||
|
*aNumChildren = m_hdrs.Count();
|
||
|
return NS_OK;
|
||
|
}
|
||
|
|
||
|
NS_IMETHODIMP nsMsgXFGroupThread::GetChildKeyAt(PRInt32 aIndex, nsMsgKey *aResult)
|
||
|
{
|
||
|
NS_ASSERTION(PR_FALSE, "shouldn't call this");
|
||
|
return NS_ERROR_NOT_IMPLEMENTED;
|
||
|
}
|
||
|
|