/* -*- 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 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 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 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 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 curChild; ret = GetChildHdrAt(childIndex, getter_AddRefs(curChild)); if (NS_SUCCEEDED(ret) && curChild) { nsMsgKey parentKey; nsCOMPtr 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 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 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 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 folder; hdr->GetFolder(getter_AddRefs(folder)); m_folders.InsertObjectAt(folder, index); nsMsgGroupThread::InsertMsgHdrAt(index, hdr); } void nsMsgXFGroupThread::SetMsgHdrAt(nsMsgViewIndex index, nsIMsgDBHdr *hdr) { nsCOMPtr 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 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 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); }