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; }
nsresult nsMsgThreadedDBView::OnNewHeader(nsIMsgDBHdr *newHdr, nsMsgKey aParentKey, bool ensureListed) { if (m_viewFlags & nsMsgViewFlagsType::kGroupBySort) return nsMsgGroupView::OnNewHeader(newHdr, aParentKey, ensureListed); NS_ENSURE_TRUE(newHdr, NS_MSG_MESSAGE_NOT_FOUND); nsMsgKey newKey; newHdr->GetMessageKey(&newKey); // views can override this behaviour, which is to append to view. // This is the mail behaviour, but threaded views want // to insert in order... PRUint32 msgFlags; newHdr->GetFlags(&msgFlags); if ((m_viewFlags & nsMsgViewFlagsType::kUnreadOnly) && !ensureListed && (msgFlags & nsMsgMessageFlags::Read)) return NS_OK; // Currently, we only add the header in a threaded view if it's a thread. // We used to check if this was the first header in the thread, but that's // a bit harder in the unreadOnly view. But we'll catch it below. // if not threaded display just add it to the view. if (!(m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay)) return AddHdr(newHdr); // need to find the thread we added this to so we can change the hasnew flag // added message to existing thread, but not to view // Fix flags on thread header. PRInt32 threadCount; PRUint32 threadFlags; bool moveThread = false; nsMsgViewIndex threadIndex = ThreadIndexOfMsg(newKey, nsMsgViewIndex_None, &threadCount, &threadFlags); bool threadRootIsDisplayed = false; nsCOMPtr <nsIMsgThread> threadHdr; m_db->GetThreadContainingMsgHdr(newHdr, getter_AddRefs(threadHdr)); if (threadHdr && m_sortType == nsMsgViewSortType::byDate) { PRUint32 newestMsgInThread = 0, msgDate = 0; threadHdr->GetNewestMsgDate(&newestMsgInThread); newHdr->GetDateInSeconds(&msgDate); moveThread = (msgDate == newestMsgInThread); } if (threadIndex != nsMsgViewIndex_None) { threadRootIsDisplayed = (m_currentlyDisplayedViewIndex == threadIndex); PRUint32 flags = m_flags[threadIndex]; if (!(flags & MSG_VIEW_FLAG_HASCHILDREN)) { flags |= MSG_VIEW_FLAG_HASCHILDREN | MSG_VIEW_FLAG_ISTHREAD; if (!(m_viewFlags & nsMsgViewFlagsType::kUnreadOnly)) flags |= nsMsgMessageFlags::Elided; m_flags[threadIndex] = flags; } if (!(flags & nsMsgMessageFlags::Elided)) { // thread is expanded // insert child into thread // levels of other hdrs may have changed! PRUint32 newFlags = msgFlags; PRInt32 level = 0; nsMsgViewIndex insertIndex = threadIndex; if (aParentKey == nsMsgKey_None) { newFlags |= MSG_VIEW_FLAG_ISTHREAD | MSG_VIEW_FLAG_HASCHILDREN; } else { nsMsgViewIndex parentIndex = FindParentInThread(aParentKey, threadIndex); level = m_levels[parentIndex] + 1; insertIndex = GetInsertInfoForNewHdr(newHdr, parentIndex, level); } InsertMsgHdrAt(insertIndex, newHdr, newKey, newFlags, level); // the call to NoteChange() has to happen after we add the key // as NoteChange() will call RowCountChanged() which will call our GetRowCount() NoteChange(insertIndex, 1, nsMsgViewNotificationCode::insertOrDelete); if (aParentKey == nsMsgKey_None) { // this header is the new king! try collapsing the existing thread, // removing it, installing this header as king, and expanding it. CollapseByIndex(threadIndex, nsnull); // call base class, so child won't get promoted. // nsMsgDBView::RemoveByIndex(threadIndex); ExpandByIndex(threadIndex, nsnull); } } else if (aParentKey == nsMsgKey_None) { // if we have a collapsed thread which just got a new // top of thread, change the keys array. m_keys[threadIndex] = newKey; } // If this message is new, the thread is collapsed, it is the // root and it was displayed, expand it so that the user does // not find that their message has magically turned into a summary. if (msgFlags & nsMsgMessageFlags::New && m_flags[threadIndex] & nsMsgMessageFlags::Elided && threadRootIsDisplayed) ExpandByIndex(threadIndex, nsnull); if (moveThread) MoveThreadAt(threadIndex); else // note change, to update the parent thread's unread and total counts NoteChange(threadIndex, 1, nsMsgViewNotificationCode::changed); } else if (threadHdr) // adding msg to thread that's not in view. AddMsgToThreadNotInView(threadHdr, newHdr, ensureListed); return NS_OK; }