diff options
Diffstat (limited to 'mailnews/base/src/nsMsgGroupThread.cpp')
-rw-r--r-- | mailnews/base/src/nsMsgGroupThread.cpp | 856 |
1 files changed, 856 insertions, 0 deletions
diff --git a/mailnews/base/src/nsMsgGroupThread.cpp b/mailnews/base/src/nsMsgGroupThread.cpp new file mode 100644 index 000000000..c68a6329d --- /dev/null +++ b/mailnews/base/src/nsMsgGroupThread.cpp @@ -0,0 +1,856 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "msgCore.h" +#include "nsMsgGroupThread.h" +#include "nsMsgDBView.h" +#include "nsMsgMessageFlags.h" +#include "nsMsgUtils.h" + +NS_IMPL_ISUPPORTS(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 = 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(uint32_t *aFlags) +{ + NS_ENSURE_ARG_POINTER(aFlags); + *aFlags = m_flags; + return NS_OK; +} + +NS_IMETHODIMP nsMsgGroupThread::SetFlags(uint32_t aFlags) +{ + m_flags = aFlags; + return NS_OK; +} + +NS_IMETHODIMP nsMsgGroupThread::SetSubject(const nsACString& aSubject) +{ + NS_ASSERTION(false, "shouldn't call this"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsMsgGroupThread::GetSubject(nsACString& result) +{ + NS_ASSERTION(false, "shouldn't call this"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsMsgGroupThread::GetNumChildren(uint32_t *aNumChildren) +{ + NS_ENSURE_ARG_POINTER(aNumChildren); + *aNumChildren = m_keys.Length(); // - ((m_dummy) ? 1 : 0); + return NS_OK; +} + +uint32_t nsMsgGroupThread::NumRealChildren() +{ + return m_keys.Length() - ((m_dummy) ? 1 : 0); +} + +NS_IMETHODIMP nsMsgGroupThread::GetNumUnreadChildren (uint32_t *aNumUnreadChildren) +{ + NS_ENSURE_ARG_POINTER(aNumUnreadChildren); + *aNumUnreadChildren = m_numUnreadChildren; + return NS_OK; +} + +void nsMsgGroupThread::InsertMsgHdrAt(nsMsgViewIndex index, nsIMsgDBHdr *hdr) +{ + nsMsgKey msgKey; + hdr->GetMessageKey(&msgKey); + m_keys.InsertElementAt(index, msgKey); +} + +void nsMsgGroupThread::SetMsgHdrAt(nsMsgViewIndex index, nsIMsgDBHdr *hdr) +{ + nsMsgKey msgKey; + hdr->GetMessageKey(&msgKey); + m_keys[index] = msgKey; +} + +nsMsgViewIndex nsMsgGroupThread::FindMsgHdr(nsIMsgDBHdr *hdr) +{ + nsMsgKey msgKey; + hdr->GetMessageKey(&msgKey); + return (nsMsgViewIndex)m_keys.IndexOf(msgKey); +} + +NS_IMETHODIMP nsMsgGroupThread::AddChild(nsIMsgDBHdr *child, nsIMsgDBHdr *inReplyTo, bool threadInThread, + nsIDBChangeAnnouncer *announcer) +{ + NS_ASSERTION(false, "shouldn't call this"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +nsMsgViewIndex nsMsgGroupThread::AddMsgHdrInDateOrder(nsIMsgDBHdr *child, nsMsgDBView *view) +{ + nsMsgKey newHdrKey; + child->GetMessageKey(&newHdrKey); + uint32_t 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.Length() > 0) + { + nsMsgViewSortTypeValue sortType; + nsMsgViewSortOrderValue sortOrder; + (void) view->GetSortType(&sortType); + (void) view->GetSortOrder(&sortOrder); + // historical behavior is ascending date order unless our primary sort is + // on date + nsMsgViewSortOrderValue threadSortOrder = + (sortType == nsMsgViewSortType::byDate + && sortOrder == nsMsgViewSortOrder::descending) ? + nsMsgViewSortOrder::descending : nsMsgViewSortOrder::ascending; + // new behavior is tricky and uses the secondary sort order if the secondary + // sort is on the date + nsMsgViewSortTypeValue secondarySortType; + nsMsgViewSortOrderValue secondarySortOrder; + (void) view->GetSecondarySortType(&secondarySortType); + (void) view->GetSecondarySortOrder(&secondarySortOrder); + if (secondarySortType == nsMsgViewSortType::byDate) + threadSortOrder = secondarySortOrder; + // sort by date within group. + insertIndex = GetInsertIndexFromView(view, child, threadSortOrder); + } + m_keys.InsertElementAt(insertIndex, newHdrKey); + if (!insertIndex) + m_threadRootKey = newHdrKey; + return insertIndex; +} + +nsMsgViewIndex +nsMsgGroupThread::GetInsertIndexFromView(nsMsgDBView *view, + nsIMsgDBHdr *child, + nsMsgViewSortOrderValue threadSortOrder) +{ + return view->GetInsertIndexHelper(child, m_keys, nullptr, threadSortOrder, nsMsgViewSortType::byDate); +} + +nsMsgViewIndex nsMsgGroupThread::AddChildFromGroupView(nsIMsgDBHdr *child, nsMsgDBView *view) +{ + uint32_t newHdrFlags = 0; + uint32_t msgDate; + nsMsgKey newHdrKey = 0; + + child->GetFlags(&newHdrFlags); + child->GetMessageKey(&newHdrKey); + child->GetDateInSeconds(&msgDate); + if (msgDate > m_newestMsgDate) + SetNewestMsgDate(msgDate); + + child->AndFlags(~(nsMsgMessageFlags::Watched), &newHdrFlags); + uint32_t 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 & nsMsgMessageFlags::Read)) + ChangeUnreadChildCount(1); + + return AddMsgHdrInDateOrder(child, view); +} + +nsresult nsMsgGroupThread::ReparentNonReferenceChildrenOf(nsIMsgDBHdr *topLevelHdr, nsMsgKey newParentKey, + nsIDBChangeAnnouncer *announcer) +{ + return NS_OK; +} + +NS_IMETHODIMP nsMsgGroupThread::GetChildKeyAt(uint32_t aIndex, nsMsgKey *aResult) +{ + NS_ENSURE_ARG_POINTER(aResult); + if (aIndex >= m_keys.Length()) + return NS_ERROR_INVALID_ARG; + *aResult = m_keys[aIndex]; + return NS_OK; +} + +NS_IMETHODIMP nsMsgGroupThread::GetChildHdrAt(uint32_t aIndex, nsIMsgDBHdr **aResult) +{ + if (aIndex >= m_keys.Length()) + return NS_MSG_MESSAGE_NOT_FOUND; + return m_db->GetMsgHdrForKey(m_keys[aIndex], aResult); +} + + +NS_IMETHODIMP nsMsgGroupThread::GetChild(nsMsgKey msgKey, nsIMsgDBHdr **aResult) +{ + return GetChildHdrAt(m_keys.IndexOf(msgKey), aResult); +} + +NS_IMETHODIMP nsMsgGroupThread::RemoveChildAt(uint32_t aIndex) +{ + NS_ENSURE_TRUE(aIndex < m_keys.Length(), NS_MSG_MESSAGE_NOT_FOUND); + + m_keys.RemoveElementAt(aIndex); + return NS_OK; +} + +nsresult nsMsgGroupThread::RemoveChild(nsMsgKey msgKey) +{ + m_keys.RemoveElement(msgKey); + return NS_OK; +} + +NS_IMETHODIMP nsMsgGroupThread::RemoveChildHdr(nsIMsgDBHdr *child, nsIDBChangeAnnouncer *announcer) +{ + NS_ENSURE_ARG_POINTER(child); + + uint32_t flags; + nsMsgKey key; + + child->GetFlags(&flags); + child->GetMessageKey(&key); + + // if this was the newest msg, clear the newest msg date so we'll recalc. + uint32_t date; + child->GetDateInSeconds(&date); + if (date == m_newestMsgDate) + SetNewestMsgDate(0); + + if (!(flags & nsMsgMessageFlags::Read)) + ChangeUnreadChildCount(-1); + nsMsgViewIndex threadIndex = FindMsgHdr(child); + bool wasFirstChild = threadIndex == 0; + nsresult rv = RemoveChildAt(threadIndex); + // 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 && wasFirstChild && m_keys.Length() > 1) + { + nsIMsgDBHdr *newRootChild; + rv = GetChildHdrAt(1, &newRootChild); + NS_ENSURE_SUCCESS(rv, rv); + SetMsgHdrAt(0, newRootChild); + } + + return rv; +} + +nsresult nsMsgGroupThread::ReparentChildrenOf(nsMsgKey oldParent, nsMsgKey newParent, nsIDBChangeAnnouncer *announcer) +{ + nsresult rv = NS_OK; + + uint32_t numChildren; + uint32_t 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, nullptr); + // 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(bool 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); + int32_t MsgKeyFirstChildIndex(nsMsgKey inMsgKey); + +protected: + virtual ~nsMsgGroupThreadEnumerator(); + + nsresult Prefetch(); + + nsCOMPtr <nsIMsgDBHdr> mResultHdr; + nsMsgGroupThread* mThread; + nsMsgKey mThreadParentKey; + nsMsgKey mFirstMsgKey; + int32_t mChildIndex; + bool mDone; + bool mNeedToPrefetch; + nsMsgGroupThreadEnumeratorFilter mFilter; + void* mClosure; + bool mFoundChildren; +}; + +nsMsgGroupThreadEnumerator::nsMsgGroupThreadEnumerator(nsMsgGroupThread *thread, nsMsgKey startKey, + nsMsgGroupThreadEnumeratorFilter filter, void* closure) + : mDone(false), + mFilter(filter), mClosure(closure), mFoundChildren(false) +{ + mThreadParentKey = startKey; + mChildIndex = 0; + mThread = thread; + mNeedToPrefetch = true; + mFirstMsgKey = nsMsgKey_None; + + nsresult rv = mThread->GetRootHdr(nullptr, getter_AddRefs(mResultHdr)); + + if (NS_SUCCEEDED(rv) && mResultHdr) + mResultHdr->GetMessageKey(&mFirstMsgKey); + + uint32_t numChildren; + mThread->GetNumChildren(&numChildren); + + if (mThreadParentKey != nsMsgKey_None) + { + nsMsgKey msgKey = nsMsgKey_None; + uint32_t 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(false, "couldn't get child from thread"); + } + } + +#ifdef DEBUG_bienvenu1 + nsCOMPtr <nsIMsgDBHdr> child; + for (uint32_t 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_ISUPPORTS(nsMsgGroupThreadEnumerator, nsISimpleEnumerator) + + +int32_t nsMsgGroupThreadEnumerator::MsgKeyFirstChildIndex(nsMsgKey inMsgKey) +{ + // if (msgKey != mThreadParentKey) + // mDone = 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. + uint32_t numChildren; + nsCOMPtr <nsIMsgDBHdr> curHdr; + int32_t 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 (uint32_t 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 = true; + } + return rv; +} + +nsresult nsMsgGroupThreadEnumerator::Prefetch() +{ + nsresult rv=NS_OK; // XXX or should this default to an error? + mResultHdr = nullptr; + 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) + { + uint32_t numChildren; + mThread->GetNumChildren(&numChildren); + + while ((uint32_t)mChildIndex < 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 = nullptr; + 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 = nullptr; + } + else + NS_ASSERTION(false, "better be able to get child"); + } + if (!mResultHdr && mThreadParentKey == mFirstMsgKey && !mFoundChildren && numChildren > 1) + { +// mThread->ReparentMsgsWithInvalidParent(numChildren, mThreadParentKey); + } + } + if (!mResultHdr) + { + mDone = true; + return NS_ERROR_FAILURE; + } + if (NS_FAILED(rv)) + { + mDone = true; + return rv; + } + else + mNeedToPrefetch = false; + mFoundChildren = true; + +#ifdef DEBUG_bienvenu1 + nsMsgKey debugMsgKey; + mResultHdr->GetMessageKey(&debugMsgKey); + printf("next for %ld = %ld\n", mThreadParentKey, debugMsgKey); +#endif + + return rv; +} + +NS_IMETHODIMP nsMsgGroupThreadEnumerator::HasMoreElements(bool *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, nullptr, nullptr); + if (e == nullptr) + return NS_ERROR_OUT_OF_MEMORY; + NS_ADDREF(e); + *result = e; + + return NS_OK; +} +#if 0 +nsresult nsMsgGroupThread::ReparentMsgsWithInvalidParent(uint32_t 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 (int32_t childIndex = 0; childIndex < (int32_t) 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(int32_t *resultIndex, nsIMsgDBHdr **result) +{ + if (!result) + return NS_ERROR_NULL_POINTER; + + *result = nullptr; + + 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"); + uint32_t numChildren; + nsMsgKey threadParentKey = nsMsgKey_None; + GetNumChildren(&numChildren); + + for (uint32_t childIndex = 0; childIndex < 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(int32_t delta) +{ + m_numUnreadChildren += delta; + return NS_OK; +} + +nsresult nsMsgGroupThread::GetChildHdrForKey(nsMsgKey desiredKey, nsIMsgDBHdr **result, int32_t *resultIndex) +{ + uint32_t numChildren; + uint32_t childIndex = 0; + nsresult rv = NS_OK; // XXX or should this default to an error? + + if (!result) + return NS_ERROR_NULL_POINTER; + + GetNumChildren(&numChildren); + + 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 = (int32_t) childIndex; + + return rv; +} + +NS_IMETHODIMP nsMsgGroupThread::GetFirstUnreadChild(nsIMsgDBHdr **result) +{ + NS_ENSURE_ARG(result); + uint32_t numChildren; + nsresult rv = NS_OK; + + GetNumChildren(&numChildren); + + if ((int32_t) numChildren < 0) + numChildren = 0; + + for (uint32_t childIndex = 0; childIndex < numChildren; childIndex++) + { + nsCOMPtr <nsIMsgDBHdr> child; + rv = GetChildHdrAt(childIndex, getter_AddRefs(child)); + if (NS_SUCCEEDED(rv) && child) + { + nsMsgKey msgKey; + child->GetMessageKey(&msgKey); + + bool isRead; + rv = m_db->IsRead(msgKey, &isRead); + if (NS_SUCCEEDED(rv) && !isRead) + { + *result = child; + NS_ADDREF(*result); + break; + } + } + } + + return rv; +} + +NS_IMETHODIMP nsMsgGroupThread::GetNewestMsgDate(uint32_t *aResult) +{ + // if this hasn't been set, figure it out by enumerating the msgs in the thread. + if (!m_newestMsgDate) + { + uint32_t numChildren; + nsresult rv = NS_OK; + + GetNumChildren(&numChildren); + + for (uint32_t childIndex = 0; childIndex < numChildren; childIndex++) + { + nsCOMPtr <nsIMsgDBHdr> child; + rv = GetChildHdrAt(childIndex, getter_AddRefs(child)); + if (NS_SUCCEEDED(rv) && child) + { + uint32_t msgDate; + child->GetDateInSeconds(&msgDate); + if (msgDate > m_newestMsgDate) + m_newestMsgDate = msgDate; + } + } + + } + *aResult = m_newestMsgDate; + return NS_OK; +} + + +NS_IMETHODIMP nsMsgGroupThread::SetNewestMsgDate(uint32_t aNewestMsgDate) +{ + m_newestMsgDate = aNewestMsgDate; + return NS_OK; +} + +nsMsgXFGroupThread::nsMsgXFGroupThread() +{ +} + +nsMsgXFGroupThread::~nsMsgXFGroupThread() +{ +} + +NS_IMETHODIMP nsMsgXFGroupThread::GetNumChildren(uint32_t *aNumChildren) +{ + NS_ENSURE_ARG_POINTER(aNumChildren); + *aNumChildren = m_folders.Length(); + return NS_OK; +} + +NS_IMETHODIMP nsMsgXFGroupThread::GetChildHdrAt(uint32_t aIndex, nsIMsgDBHdr **aResult) +{ + if (aIndex >= m_folders.Length()) + return NS_MSG_MESSAGE_NOT_FOUND; + return m_folders.ObjectAt(aIndex)->GetMessageHeader(m_keys[aIndex], aResult); +} + +NS_IMETHODIMP nsMsgXFGroupThread::GetChildKeyAt(uint32_t aIndex, nsMsgKey *aResult) +{ + NS_ASSERTION(false, "shouldn't call this"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsMsgXFGroupThread::RemoveChildAt(uint32_t aIndex) +{ + NS_ENSURE_TRUE(aIndex < m_folders.Length(), NS_MSG_MESSAGE_NOT_FOUND); + + nsresult rv = nsMsgGroupThread::RemoveChildAt(aIndex); + NS_ENSURE_SUCCESS(rv, rv); + m_folders.RemoveElementAt(aIndex); + return NS_OK; +} + +void nsMsgXFGroupThread::InsertMsgHdrAt(nsMsgViewIndex index, nsIMsgDBHdr *hdr) +{ + nsCOMPtr<nsIMsgFolder> folder; + hdr->GetFolder(getter_AddRefs(folder)); + m_folders.InsertObjectAt(folder, index); + nsMsgGroupThread::InsertMsgHdrAt(index, hdr); +} + +void nsMsgXFGroupThread::SetMsgHdrAt(nsMsgViewIndex index, nsIMsgDBHdr *hdr) +{ + nsCOMPtr<nsIMsgFolder> folder; + hdr->GetFolder(getter_AddRefs(folder)); + m_folders.ReplaceObjectAt(folder, index); + nsMsgGroupThread::SetMsgHdrAt(index, hdr); +} + +nsMsgViewIndex nsMsgXFGroupThread::FindMsgHdr(nsIMsgDBHdr *hdr) +{ + nsMsgKey msgKey; + hdr->GetMessageKey(&msgKey); + nsCOMPtr<nsIMsgFolder> folder; + hdr->GetFolder(getter_AddRefs(folder)); + size_t index = 0; + while (true) { + index = m_keys.IndexOf(msgKey, index); + if (index == m_keys.NoIndex || m_folders[index] == folder) + break; + index++; + } + return (nsMsgViewIndex)index; +} + +nsMsgViewIndex nsMsgXFGroupThread::AddMsgHdrInDateOrder(nsIMsgDBHdr *child, nsMsgDBView *view) +{ + nsMsgViewIndex insertIndex = nsMsgGroupThread::AddMsgHdrInDateOrder(child, view); + nsCOMPtr<nsIMsgFolder> folder; + child->GetFolder(getter_AddRefs(folder)); + m_folders.InsertObjectAt(folder, insertIndex); + return insertIndex; +} +nsMsgViewIndex +nsMsgXFGroupThread::GetInsertIndexFromView(nsMsgDBView *view, + nsIMsgDBHdr *child, + nsMsgViewSortOrderValue threadSortOrder) +{ + return view->GetInsertIndexHelper(child, m_keys, &m_folders, threadSortOrder, nsMsgViewSortType::byDate); +} + |