summaryrefslogtreecommitdiffstats
path: root/mailnews/base/src/nsMsgXFViewThread.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mailnews/base/src/nsMsgXFViewThread.cpp')
-rw-r--r--mailnews/base/src/nsMsgXFViewThread.cpp481
1 files changed, 481 insertions, 0 deletions
diff --git a/mailnews/base/src/nsMsgXFViewThread.cpp b/mailnews/base/src/nsMsgXFViewThread.cpp
new file mode 100644
index 000000000..113aeef6f
--- /dev/null
+++ b/mailnews/base/src/nsMsgXFViewThread.cpp
@@ -0,0 +1,481 @@
+/* -*- 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 "nsMsgXFViewThread.h"
+#include "nsMsgSearchDBView.h"
+#include "nsMsgMessageFlags.h"
+
+NS_IMPL_ISUPPORTS(nsMsgXFViewThread, nsIMsgThread)
+
+nsMsgXFViewThread::nsMsgXFViewThread(nsMsgSearchDBView *view, nsMsgKey threadId)
+{
+ m_numUnreadChildren = 0;
+ m_numChildren = 0;
+ m_flags = 0;
+ m_newestMsgDate = 0;
+ m_view = view;
+ m_threadId = threadId;
+}
+
+nsMsgXFViewThread::~nsMsgXFViewThread()
+{
+}
+
+NS_IMETHODIMP nsMsgXFViewThread::SetThreadKey(nsMsgKey threadKey)
+{
+ m_threadId = threadKey;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgXFViewThread::GetThreadKey(nsMsgKey *aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = m_threadId;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgXFViewThread::GetFlags(uint32_t *aFlags)
+{
+ NS_ENSURE_ARG_POINTER(aFlags);
+ *aFlags = m_flags;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgXFViewThread::SetFlags(uint32_t aFlags)
+{
+ m_flags = aFlags;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgXFViewThread::SetSubject(const nsACString& aSubject)
+{
+ NS_ASSERTION(false, "shouldn't call this");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsMsgXFViewThread::GetSubject(nsACString& result)
+{
+ NS_ASSERTION(false, "shouldn't call this");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsMsgXFViewThread::GetNumChildren(uint32_t *aNumChildren)
+{
+ NS_ENSURE_ARG_POINTER(aNumChildren);
+ *aNumChildren = m_keys.Length();
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgXFViewThread::GetNumUnreadChildren (uint32_t *aNumUnreadChildren)
+{
+ NS_ENSURE_ARG_POINTER(aNumUnreadChildren);
+ *aNumUnreadChildren = m_numUnreadChildren;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMsgXFViewThread::AddChild(nsIMsgDBHdr *aNewHdr, nsIMsgDBHdr *aInReplyTo,
+ bool aThreadInThread, nsIDBChangeAnnouncer *aAnnouncer)
+{
+ uint32_t whereInserted;
+ return AddHdr(aNewHdr, false, whereInserted, nullptr);
+}
+
+// Returns the parent of the newly added header. If reparentChildren
+// is true, we believe that the new header is a parent of an existing
+// header, and we should find it, and reparent it.
+nsresult nsMsgXFViewThread::AddHdr(nsIMsgDBHdr *newHdr,
+ bool reparentChildren,
+ uint32_t &whereInserted,
+ nsIMsgDBHdr **outParent)
+{
+ nsCOMPtr<nsIMsgFolder> newHdrFolder;
+ newHdr->GetFolder(getter_AddRefs(newHdrFolder));
+
+ uint32_t newHdrFlags = 0;
+ uint32_t msgDate;
+ nsMsgKey newHdrKey = 0;
+
+ newHdr->GetMessageKey(&newHdrKey);
+ newHdr->GetDateInSeconds(&msgDate);
+ newHdr->GetFlags(&newHdrFlags);
+ if (msgDate > m_newestMsgDate)
+ SetNewestMsgDate(msgDate);
+
+ if (newHdrFlags & nsMsgMessageFlags::Watched)
+ SetFlags(m_flags | nsMsgMessageFlags::Watched);
+
+ ChangeChildCount(1);
+ if (! (newHdrFlags & nsMsgMessageFlags::Read))
+ ChangeUnreadChildCount(1);
+
+ if (m_numChildren == 1)
+ {
+ m_keys.InsertElementAt(0, newHdrKey);
+ m_levels.InsertElementAt(0, 0);
+ m_folders.InsertObjectAt(newHdrFolder, 0);
+ if (outParent)
+ *outParent = nullptr;
+ whereInserted = 0;
+ return NS_OK;
+ }
+
+ // Find our parent, if any, in the thread. Starting at the newest
+ // reference, and working our way back, see if we've mapped that reference
+ // to this thread.
+ uint16_t numReferences;
+ newHdr->GetNumReferences(&numReferences);
+ nsCOMPtr<nsIMsgDBHdr> parent;
+ int32_t parentIndex = -1;
+
+ for (int32_t i = numReferences - 1; i >= 0; i--)
+ {
+ nsAutoCString reference;
+ newHdr->GetStringReference(i, reference);
+ if (reference.IsEmpty())
+ break;
+
+ // I could look for the thread from the reference, but getting
+ // the header directly should be fine. If it's not, that means
+ // that the parent isn't in this thread, though it should be.
+ m_view->GetMsgHdrFromHash(reference, getter_AddRefs(parent));
+ if (parent)
+ {
+ parentIndex = HdrIndex(parent);
+ if (parentIndex == -1)
+ {
+ NS_ERROR("how did we get in the wrong thread?");
+ parent = nullptr;
+ }
+ break;
+ }
+ }
+ if (parent)
+ {
+ if (outParent)
+ NS_ADDREF(*outParent = parent);
+ uint32_t parentLevel = m_levels[parentIndex];
+ nsMsgKey parentKey;
+ parent->GetMessageKey(&parentKey);
+ nsCOMPtr<nsIMsgFolder> parentFolder;
+ parent->GetFolder(getter_AddRefs(parentFolder));
+ // iterate over our parents' children until we find one we're older than,
+ // and insert ourselves before it, or as the last child. In other words,
+ // insert, sorted by date.
+ uint32_t msgDate, childDate;
+ newHdr->GetDateInSeconds(&msgDate);
+ nsCOMPtr<nsIMsgDBHdr> child;
+ nsMsgViewIndex i;
+ nsMsgViewIndex insertIndex = m_keys.Length();
+ uint32_t insertLevel = parentLevel + 1;
+ for (i = parentIndex;
+ i < m_keys.Length() && (i == (nsMsgViewIndex)parentIndex || m_levels[i] >= parentLevel);
+ i++)
+ {
+ GetChildHdrAt(i, getter_AddRefs(child));
+ if (child)
+ {
+ if (reparentChildren && IsHdrParentOf(newHdr, child))
+ {
+ insertIndex = i;
+ // bump all the children of the current child, and the child
+ nsMsgViewIndex j = insertIndex;
+ uint8_t childLevel = m_levels[insertIndex];
+ do
+ {
+ m_levels[j] = m_levels[j] + 1;
+ j++;
+ }
+ while (j < m_keys.Length() && m_levels[j] > childLevel);
+ break;
+ }
+ else if (m_levels[i] == parentLevel + 1) // possible sibling
+ {
+ child->GetDateInSeconds(&childDate);
+ if (msgDate < childDate)
+ {
+ // if we think we need to reparent, remember this
+ // insert index, but keep looking for children.
+ insertIndex = i;
+ insertLevel = m_levels[i];
+ // if the sibling we're inserting after has children, we need
+ // to go after the children.
+ while (insertIndex + 1 < m_keys.Length() && m_levels[insertIndex + 1] > insertLevel)
+ insertIndex++;
+ if (!reparentChildren)
+ break;
+ }
+ }
+ }
+ }
+ m_keys.InsertElementAt(insertIndex, newHdrKey);
+ m_levels.InsertElementAt(insertIndex, insertLevel);
+ m_folders.InsertObjectAt(newHdrFolder, insertIndex);
+ whereInserted = insertIndex;
+ }
+ else
+ {
+ if (outParent)
+ *outParent = nullptr;
+ nsCOMPtr<nsIMsgDBHdr> rootHdr;
+ GetChildHdrAt(0, getter_AddRefs(rootHdr));
+ // If the new header is a parent of the root then it should be promoted.
+ if (rootHdr && IsHdrParentOf(newHdr, rootHdr))
+ {
+ m_keys.InsertElementAt(0, newHdrKey);
+ m_levels.InsertElementAt(0, 0);
+ m_folders.InsertObjectAt(newHdrFolder, 0);
+ whereInserted = 0;
+ // Adjust level of old root hdr and its children
+ for (nsMsgViewIndex i = 1; i < m_keys.Length(); i++)
+ m_levels[i] = m_levels[1] + 1;
+ }
+ else
+ {
+ m_keys.AppendElement(newHdrKey);
+ m_levels.AppendElement(1);
+ m_folders.AppendObject(newHdrFolder);
+ if (outParent)
+ NS_IF_ADDREF(*outParent = rootHdr);
+ whereInserted = m_keys.Length() -1;
+ }
+ }
+
+ // ### TODO handle the case where the root header starts
+ // with Re, and the new one doesn't, and is earlier. In that
+ // case, we want to promote the new header to root.
+
+// PRTime newHdrDate;
+// newHdr->GetDate(&newHdrDate);
+
+// if (numChildren > 0 && !(newHdrFlags & nsMsgMessageFlags::HasRe))
+// {
+// PRTime topLevelHdrDate;
+
+// nsCOMPtr<nsIMsgDBHdr> topLevelHdr;
+// rv = GetRootHdr(nullptr, getter_AddRefs(topLevelHdr));
+// if (NS_SUCCEEDED(rv) && topLevelHdr)
+// {
+// topLevelHdr->GetDate(&topLevelHdrDate);
+// if (newHdrDate < topLevelHdrDate)
+
+// }
+// }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgXFViewThread::GetChildHdrAt(uint32_t aIndex, nsIMsgDBHdr **aResult)
+{
+ if (aIndex >= m_keys.Length())
+ return NS_MSG_MESSAGE_NOT_FOUND;
+ nsCOMPtr<nsIMsgDatabase> db;
+ nsresult rv = m_folders[aIndex]->GetMsgDatabase(getter_AddRefs(db));
+ NS_ENSURE_SUCCESS(rv, rv);
+ return db->GetMsgHdrForKey(m_keys[aIndex], aResult);
+}
+
+NS_IMETHODIMP nsMsgXFViewThread::RemoveChildAt(uint32_t aIndex)
+{
+ m_keys.RemoveElementAt(aIndex);
+ m_levels.RemoveElementAt(aIndex);
+ m_folders.RemoveObjectAt(aIndex);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgXFViewThread::RemoveChildHdr(nsIMsgDBHdr *child, nsIDBChangeAnnouncer *announcer)
+{
+ NS_ENSURE_ARG_POINTER(child);
+ nsMsgKey msgKey;
+ uint32_t msgFlags;
+ child->GetMessageKey(&msgKey);
+ child->GetFlags(&msgFlags);
+ nsCOMPtr<nsIMsgFolder> msgFolder;
+ child->GetFolder(getter_AddRefs(msgFolder));
+ // 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);
+
+ for (uint32_t childIndex = 0; childIndex < m_keys.Length(); childIndex++)
+ {
+ if (m_keys[childIndex] == msgKey && m_folders[childIndex] == msgFolder)
+ {
+ uint8_t levelRemoved = m_keys[childIndex];
+ // Adjust the levels of all the children of this header
+ nsMsgViewIndex i;
+ for (i = childIndex + 1;
+ i < m_keys.Length() && m_levels[i] > levelRemoved; i++)
+ m_levels[i] = m_levels[i] - 1;
+
+ m_view->NoteChange(childIndex + 1, i - childIndex + 1,
+ nsMsgViewNotificationCode::changed);
+ m_keys.RemoveElementAt(childIndex);
+ m_levels.RemoveElementAt(childIndex);
+ m_folders.RemoveObjectAt(childIndex);
+ if (!(msgFlags & nsMsgMessageFlags::Read))
+ ChangeUnreadChildCount(-1);
+ ChangeChildCount(-1);
+ return NS_OK;
+ }
+ }
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsMsgXFViewThread::GetRootHdr(int32_t *aResultIndex, nsIMsgDBHdr **aResult)
+{
+ NS_ENSURE_ARG_POINTER(aResult);
+ if (aResultIndex)
+ *aResultIndex = 0;
+ return GetChildHdrAt(0, aResult);
+}
+
+NS_IMETHODIMP nsMsgXFViewThread::GetChildKeyAt(uint32_t aIndex, nsMsgKey *aResult)
+{
+ NS_ASSERTION(false, "shouldn't call this");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsMsgXFViewThread::GetChild(nsMsgKey msgKey, nsIMsgDBHdr **aResult)
+{
+ NS_ASSERTION(false, "shouldn't call this");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
+int32_t nsMsgXFViewThread::HdrIndex(nsIMsgDBHdr *hdr)
+{
+ nsMsgKey msgKey;
+ nsCOMPtr<nsIMsgFolder> folder;
+ hdr->GetMessageKey(&msgKey);
+ hdr->GetFolder(getter_AddRefs(folder));
+ for (uint32_t i = 0; i < m_keys.Length(); i++)
+ {
+ if (m_keys[i] == msgKey && m_folders[i] == folder)
+ return i;
+ }
+ return -1;
+}
+
+void nsMsgXFViewThread::ChangeUnreadChildCount(int32_t delta)
+{
+ m_numUnreadChildren += delta;
+}
+
+void nsMsgXFViewThread::ChangeChildCount(int32_t delta)
+{
+ m_numChildren += delta;
+}
+
+bool nsMsgXFViewThread::IsHdrParentOf(nsIMsgDBHdr *possibleParent,
+ nsIMsgDBHdr *possibleChild)
+{
+ uint16_t referenceToCheck = 0;
+ possibleChild->GetNumReferences(&referenceToCheck);
+ nsAutoCString reference;
+
+ nsCString messageId;
+ possibleParent->GetMessageId(getter_Copies(messageId));
+
+ while (referenceToCheck > 0)
+ {
+ possibleChild->GetStringReference(referenceToCheck - 1, reference);
+
+ if (reference.Equals(messageId))
+ return true;
+ // if reference didn't match, check if this ref is for a non-existent
+ // header. If it is, continue looking at ancestors.
+ nsCOMPtr<nsIMsgDBHdr> refHdr;
+ m_view->GetMsgHdrFromHash(reference, getter_AddRefs(refHdr));
+ if (refHdr)
+ break;
+ referenceToCheck--;
+ }
+ return false;
+}
+
+NS_IMETHODIMP nsMsgXFViewThread::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);
+
+ 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)
+ {
+ uint32_t msgDate;
+ child->GetDateInSeconds(&msgDate);
+ if (msgDate > m_newestMsgDate)
+ m_newestMsgDate = msgDate;
+ }
+ }
+ }
+ *aResult = m_newestMsgDate;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgXFViewThread::SetNewestMsgDate(uint32_t aNewestMsgDate)
+{
+ m_newestMsgDate = aNewestMsgDate;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgXFViewThread::MarkChildRead(bool aRead)
+{
+ ChangeUnreadChildCount(aRead ? -1 : 1);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsMsgXFViewThread::GetFirstUnreadChild(nsIMsgDBHdr **aResult)
+{
+ NS_ENSURE_ARG(aResult);
+ 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;
+ nsCOMPtr<nsIMsgDatabase> db;
+ nsresult rv = m_folders[childIndex]->GetMsgDatabase(getter_AddRefs(db));
+ if (NS_SUCCEEDED(rv))
+ rv = db->IsRead(msgKey, &isRead);
+ if (NS_SUCCEEDED(rv) && !isRead)
+ {
+ NS_ADDREF(*aResult = child);
+ break;
+ }
+ }
+ }
+ return rv;
+}
+NS_IMETHODIMP nsMsgXFViewThread::EnumerateMessages(nsMsgKey aParentKey,
+ nsISimpleEnumerator **aResult)
+{
+ NS_ERROR("shouldn't call this");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}