nsresult nsMsgThreadsWithUnreadDBView::AddMsgToThreadNotInView(nsIMsgThread *threadHdr, nsIMsgDBHdr *msgHdr, bool ensureListed) { nsresult rv = NS_OK; nsCOMPtr <nsIMsgDBHdr> parentHdr; PRUint32 msgFlags; msgHdr->GetFlags(&msgFlags); GetFirstMessageHdrToDisplayInThread(threadHdr, getter_AddRefs(parentHdr)); if (parentHdr && (ensureListed || !(msgFlags & nsMsgMessageFlags::Read))) { nsMsgKey key; PRUint32 numMsgsInThread; rv = AddHdr(parentHdr); threadHdr->GetNumChildren(&numMsgsInThread); if (numMsgsInThread > 1) { parentHdr->GetMessageKey(&key); nsMsgViewIndex viewIndex = FindViewIndex(key); if (viewIndex != nsMsgViewIndex_None) OrExtraFlag(viewIndex, nsMsgMessageFlags::Elided | MSG_VIEW_FLAG_HASCHILDREN); } m_totalUnwantedMessagesInView -= numMsgsInThread; } else m_totalUnwantedMessagesInView++; return rv; }
nsresult nsMsgSearchDBView::AddHdrFromFolder(nsIMsgDBHdr *msgHdr, nsIMsgFolder *folder) { if (m_viewFlags & nsMsgViewFlagsType::kGroupBySort) return nsMsgGroupView::OnNewHeader(msgHdr, nsMsgKey_None, true); nsMsgKey msgKey; uint32_t msgFlags; msgHdr->GetMessageKey(&msgKey); msgHdr->GetFlags(&msgFlags); if (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay) { nsCOMPtr<nsIMsgThread> thread; nsCOMPtr<nsIMsgDBHdr> threadRoot; // if we find an xf thread in the hash table corresponding to the new msg's // message id, a previous header must be a reference child of the new // message, which means we need to reparent later. bool msgIsReferredTo; GetXFThreadFromMsgHdr(msgHdr, getter_AddRefs(thread), &msgIsReferredTo); bool newThread = !thread; nsMsgXFViewThread *viewThread; if (!thread) { viewThread = new nsMsgXFViewThread(this, m_nextThreadId++); if (!viewThread) return NS_ERROR_OUT_OF_MEMORY; thread = do_QueryInterface(viewThread); } else { viewThread = static_cast<nsMsgXFViewThread*>(thread.get()); thread->GetChildHdrAt(0, getter_AddRefs(threadRoot)); } AddMsgToHashTables(msgHdr, thread); nsCOMPtr<nsIMsgDBHdr> parent; uint32_t posInThread; // We need to move threads in order to keep ourselves sorted // correctly. We want the index of the original thread...we can do this by // getting the root header before we add the new header, and finding that. if (newThread || !viewThread->MsgCount()) { viewThread->AddHdr(msgHdr, false, posInThread, getter_AddRefs(parent)); nsMsgViewIndex insertIndex = GetIndexForThread(msgHdr); NS_ASSERTION(insertIndex == m_levels.Length() || !m_levels[insertIndex], "inserting into middle of thread"); if (insertIndex == nsMsgViewIndex_None) return NS_ERROR_FAILURE; if (!(m_viewFlags & nsMsgViewFlagsType::kExpandAll)) msgFlags |= nsMsgMessageFlags::Elided; InsertMsgHdrAt(insertIndex, msgHdr, msgKey, msgFlags, 0); NoteChange(insertIndex, 1, nsMsgViewNotificationCode::insertOrDelete); } else { // get the thread root index before we add the header, because adding // the header can change the sort position. nsMsgViewIndex threadIndex = GetThreadRootIndex(threadRoot); viewThread->AddHdr(msgHdr, msgIsReferredTo, posInThread, getter_AddRefs(parent)); if (threadIndex == nsMsgViewIndex_None) { NS_ERROR("couldn't find thread index for newly inserted header"); return NS_OK; // not really OK, but not failure exactly. } NS_ASSERTION(!m_levels[threadIndex], "threadRoot incorrect, or level incorrect"); bool moveThread = false; if (m_sortType == nsMsgViewSortType::byDate) { uint32_t newestMsgInThread = 0, msgDate = 0; viewThread->GetNewestMsgDate(&newestMsgInThread); msgHdr->GetDateInSeconds(&msgDate); moveThread = (msgDate == newestMsgInThread); } OrExtraFlag(threadIndex, MSG_VIEW_FLAG_HASCHILDREN | MSG_VIEW_FLAG_ISTHREAD); if (!(m_flags[threadIndex] & nsMsgMessageFlags::Elided)) { if (parent) { // since we know posInThread, we just want to insert the new hdr // at threadIndex + posInThread, and then rebuild the view until we // get to a sibling of the new hdr. uint8_t newMsgLevel = viewThread->ChildLevelAt(posInThread); InsertMsgHdrAt(threadIndex + posInThread, msgHdr, msgKey, msgFlags, newMsgLevel); NoteChange(threadIndex + posInThread, 1, nsMsgViewNotificationCode::insertOrDelete); for (nsMsgViewIndex viewIndex = threadIndex + ++posInThread; posInThread < viewThread->MsgCount() && viewThread->ChildLevelAt(posInThread) > newMsgLevel; viewIndex++) { m_levels[viewIndex] = viewThread->ChildLevelAt(posInThread++); } } else // The new header is the root, so we need to adjust // all the children. { InsertMsgHdrAt(threadIndex, msgHdr, msgKey, msgFlags, 0); NoteChange(threadIndex, 1, nsMsgViewNotificationCode::insertOrDelete); nsMsgViewIndex i; for (i = threadIndex + 1; i < m_keys.Length() && (i == threadIndex + 1 || m_levels[i]); i++) m_levels[i] = m_levels[i] + 1; // turn off thread flags on old root. AndExtraFlag(threadIndex + 1, ~(MSG_VIEW_FLAG_ISTHREAD | nsMsgMessageFlags::Elided | MSG_VIEW_FLAG_HASCHILDREN)); NoteChange(threadIndex + 1, i - threadIndex + 1, nsMsgViewNotificationCode::changed); } } else if (!parent) { // new parent came into collapsed thread nsCOMPtr<nsIMsgFolder> msgFolder; msgHdr->GetFolder(getter_AddRefs(msgFolder)); m_keys[threadIndex] = msgKey; m_folders.ReplaceObjectAt(msgFolder, threadIndex); m_flags[threadIndex] = msgFlags | MSG_VIEW_FLAG_ISTHREAD | nsMsgMessageFlags::Elided | MSG_VIEW_FLAG_HASCHILDREN; NoteChange(threadIndex, 1, nsMsgViewNotificationCode::changed); } if (moveThread) MoveThreadAt(threadIndex); } } else { m_folders.AppendObject(folder); // nsMsgKey_None means it's not a valid hdr. if (msgKey != nsMsgKey_None) { msgHdr->GetFlags(&msgFlags); m_keys.AppendElement(msgKey); m_levels.AppendElement(0); m_flags.AppendElement(msgFlags); NoteChange(GetSize() - 1, 1, nsMsgViewNotificationCode::insertOrDelete); } } return NS_OK; }
NS_IMETHODIMP nsMsgSearchDBView::OnHdrDeleted(nsIMsgDBHdr *aHdrDeleted, nsMsgKey aParentKey, int32_t aFlags, nsIDBChangeListener *aInstigator) { if (m_viewFlags & nsMsgViewFlagsType::kGroupBySort) return nsMsgGroupView::OnHdrDeleted(aHdrDeleted, aParentKey, aFlags, aInstigator); if (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay) { nsMsgViewIndex deletedIndex = FindHdr(aHdrDeleted); uint32_t savedFlags = 0; if (deletedIndex != nsMsgViewIndex_None) { savedFlags = m_flags[deletedIndex]; RemoveByIndex(deletedIndex); } nsCOMPtr<nsIMsgThread> thread; GetXFThreadFromMsgHdr(aHdrDeleted, getter_AddRefs(thread)); if (thread) { nsMsgXFViewThread *viewThread = static_cast<nsMsgXFViewThread*>(thread.get()); viewThread->RemoveChildHdr(aHdrDeleted, nullptr); if (deletedIndex == nsMsgViewIndex_None && viewThread->MsgCount() == 1) { // remove the last child of a collapsed thread. Need to find the root, // and remove the thread flags on it. nsCOMPtr<nsIMsgDBHdr> rootHdr; thread->GetRootHdr(nullptr, getter_AddRefs(rootHdr)); if (rootHdr) { nsMsgViewIndex threadIndex = GetThreadRootIndex(rootHdr); if (threadIndex != nsMsgViewIndex_None) AndExtraFlag(threadIndex, ~(MSG_VIEW_FLAG_ISTHREAD | nsMsgMessageFlags::Elided | MSG_VIEW_FLAG_HASCHILDREN)); } } else if (savedFlags & MSG_VIEW_FLAG_HASCHILDREN) { if (savedFlags & nsMsgMessageFlags::Elided) { nsCOMPtr<nsIMsgDBHdr> rootHdr; nsresult rv = thread->GetRootHdr(nullptr, getter_AddRefs(rootHdr)); NS_ENSURE_SUCCESS(rv, rv); nsMsgKey msgKey; uint32_t msgFlags; rootHdr->GetMessageKey(&msgKey); rootHdr->GetFlags(&msgFlags); // promote the new thread root if (viewThread->MsgCount() > 1) msgFlags |= MSG_VIEW_FLAG_ISTHREAD | nsMsgMessageFlags::Elided | MSG_VIEW_FLAG_HASCHILDREN; InsertMsgHdrAt(deletedIndex, rootHdr, msgKey, msgFlags, 0); if (!m_deletingRows) NoteChange(deletedIndex, 1, nsMsgViewNotificationCode::insertOrDelete); } else if (viewThread->MsgCount() > 1) { OrExtraFlag(deletedIndex, MSG_VIEW_FLAG_ISTHREAD | MSG_VIEW_FLAG_HASCHILDREN); } } } } else { return nsMsgDBView::OnHdrDeleted(aHdrDeleted, aParentKey, aFlags, aInstigator); } return NS_OK; }
NS_IMETHODIMP nsMsgGroupView::OpenWithHdrs(nsISimpleEnumerator *aHeaders, nsMsgViewSortTypeValue aSortType, nsMsgViewSortOrderValue aSortOrder, nsMsgViewFlagsTypeValue aViewFlags, int32_t *aCount) { nsresult rv = NS_OK; m_groupsTable.Clear(); if (aSortType == nsMsgViewSortType::byThread || aSortType == nsMsgViewSortType::byId || aSortType == nsMsgViewSortType::byNone || aSortType == nsMsgViewSortType::bySize) return NS_ERROR_INVALID_ARG; m_sortType = aSortType; m_sortOrder = aSortOrder; m_viewFlags = aViewFlags | nsMsgViewFlagsType::kThreadedDisplay | nsMsgViewFlagsType::kGroupBySort; SaveSortInfo(m_sortType, m_sortOrder); bool hasMore; nsCOMPtr <nsISupports> supports; nsCOMPtr <nsIMsgDBHdr> msgHdr; while (NS_SUCCEEDED(rv) && NS_SUCCEEDED(rv = aHeaders->HasMoreElements(&hasMore)) && hasMore) { rv = aHeaders->GetNext(getter_AddRefs(supports)); if (NS_SUCCEEDED(rv) && supports) { bool notUsed; msgHdr = do_QueryInterface(supports); AddHdrToThread(msgHdr, ¬Used); } } uint32_t expandFlags = 0; bool expandAll = m_viewFlags & nsMsgViewFlagsType::kExpandAll; uint32_t viewFlag = (m_sortType == nsMsgViewSortType::byDate) ? MSG_VIEW_FLAG_DUMMY : 0; if (viewFlag && m_db) { nsCOMPtr <nsIDBFolderInfo> dbFolderInfo; nsresult rv = m_db->GetDBFolderInfo(getter_AddRefs(dbFolderInfo)); NS_ENSURE_SUCCESS(rv, rv); if (dbFolderInfo) dbFolderInfo->GetUint32Property("dateGroupFlags", 0, &expandFlags); } // go through the view updating the flags for threads with more than one message... // and if grouped by date, expanding threads that were expanded before. for (uint32_t viewIndex = 0; viewIndex < m_keys.Length(); viewIndex++) { nsCOMPtr <nsIMsgThread> thread; GetThreadContainingIndex(viewIndex, getter_AddRefs(thread)); if (thread) { uint32_t numChildren; thread->GetNumChildren(&numChildren); if (numChildren > 1 || viewFlag) OrExtraFlag(viewIndex, viewFlag | MSG_VIEW_FLAG_HASCHILDREN); if (expandAll || expandFlags) { nsMsgGroupThread *groupThread = static_cast<nsMsgGroupThread *>((nsIMsgThread *) thread); if (expandAll || expandFlags & (1 << groupThread->m_threadKey)) { uint32_t numExpanded; ExpandByIndex(viewIndex, &numExpanded); viewIndex += numExpanded; } } } } *aCount = m_keys.Length(); return rv; }