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; }
NS_IMETHODIMP nsMsgXFViewThread::AddChild(nsIMsgDBHdr *aNewHdr, nsIMsgDBHdr *aInReplyTo, bool aThreadInThread, nsIDBChangeAnnouncer *aAnnouncer) { PRUint32 whereInserted; return AddHdr(aNewHdr, false, whereInserted, nsnull); }
NS_IMETHODIMP nsMsgQuickSearchDBView::OpenWithHdrs(nsISimpleEnumerator *aHeaders, nsMsgViewSortTypeValue aSortType, nsMsgViewSortOrderValue aSortOrder, nsMsgViewFlagsTypeValue aViewFlags, int32_t *aCount) { if (aViewFlags & nsMsgViewFlagsType::kGroupBySort) return nsMsgGroupView::OpenWithHdrs(aHeaders, aSortType, aSortOrder, aViewFlags, aCount); m_sortType = aSortType; m_sortOrder = aSortOrder; m_viewFlags = aViewFlags; bool hasMore; nsCOMPtr<nsISupports> supports; nsCOMPtr<nsIMsgDBHdr> msgHdr; nsresult rv = NS_OK; while (NS_SUCCEEDED(rv = aHeaders->HasMoreElements(&hasMore)) && hasMore) { rv = aHeaders->GetNext(getter_AddRefs(supports)); if (NS_SUCCEEDED(rv) && supports) { msgHdr = do_QueryInterface(supports); AddHdr(msgHdr); } else break; } *aCount = m_keys.Length(); return rv; }
NS_IMETHODIMP nsMsgQuickSearchDBView::OnNewSearch() { int32_t oldSize = GetSize(); m_keys.Clear(); m_levels.Clear(); m_flags.Clear(); m_hdrHits.Clear(); // this needs to happen after we remove all the keys, since RowCountChanged() will call our GetRowCount() if (mTree) mTree->RowCountChanged(0, -oldSize); uint32_t folderFlags = 0; if (m_viewFolder) m_viewFolder->GetFlags(&folderFlags); // check if it's a virtual folder - if so, we should get the cached hits // from the db, and set a flag saying that we're using cached values. if (folderFlags & nsMsgFolderFlags::Virtual) { nsCOMPtr<nsISimpleEnumerator> cachedHits; nsCString searchUri; m_viewFolder->GetURI(searchUri); m_db->GetCachedHits(searchUri.get(), getter_AddRefs(cachedHits)); if (cachedHits) { bool hasMore; m_usingCachedHits = true; cachedHits->HasMoreElements(&hasMore); m_cacheEmpty = !hasMore; if (mTree) mTree->BeginUpdateBatch(); while (hasMore) { nsCOMPtr <nsIMsgDBHdr> pHeader; nsresult rv = cachedHits->GetNext(getter_AddRefs(pHeader)); NS_ASSERTION(NS_SUCCEEDED(rv), "nsMsgDBEnumerator broken"); if (pHeader && NS_SUCCEEDED(rv)) AddHdr(pHeader); else break; cachedHits->HasMoreElements(&hasMore); } if (mTree) mTree->EndUpdateBatch(); } } return NS_OK; }
NS_IMETHODIMP nsMsgQuickSearchDBView::OnSearchHit(nsIMsgDBHdr* aMsgHdr, nsIMsgFolder *folder) { NS_ENSURE_ARG(aMsgHdr); if (!m_db) return NS_ERROR_NULL_POINTER; // remember search hit and when search is done, reconcile cache // with new hits; m_hdrHits.AppendObject(aMsgHdr); nsMsgKey key; aMsgHdr->GetMessageKey(&key); // Is FindKey going to be expensive here? A lot of hits could make // it a little bit slow to search through the view for every hit. if (m_cacheEmpty || FindKey(key, false) == nsMsgViewIndex_None) return AddHdr(aMsgHdr); else return NS_OK; }
// This method removes the thread at threadIndex from the view // and puts it back in its new position, determined by the sort order. // And, if the selection is affected, save and restore the selection. void nsMsgThreadedDBView::MoveThreadAt(nsMsgViewIndex threadIndex) { // we need to check if the thread is collapsed or not... // We want to turn off tree notifications so that we don't // reload the current message. // We also need to invalidate the range between where the thread was // and where it ended up. bool changesDisabled = mSuppressChangeNotification; if (!changesDisabled) SetSuppressChangeNotifications(true); nsCOMPtr <nsIMsgDBHdr> threadHdr; GetMsgHdrForViewIndex(threadIndex, getter_AddRefs(threadHdr)); PRInt32 childCount = 0; nsMsgKey preservedKey; nsAutoTArray<nsMsgKey, 1> preservedSelection; PRInt32 selectionCount; PRInt32 currentIndex; bool hasSelection = mTree && mTreeSelection && ((NS_SUCCEEDED(mTreeSelection->GetCurrentIndex(¤tIndex)) && currentIndex >= 0 && (PRUint32)currentIndex < GetSize()) || (NS_SUCCEEDED(mTreeSelection->GetRangeCount(&selectionCount)) && selectionCount > 0)); if (hasSelection) SaveAndClearSelection(&preservedKey, preservedSelection); PRUint32 saveFlags = m_flags[threadIndex]; bool threadIsExpanded = !(saveFlags & nsMsgMessageFlags::Elided); if (threadIsExpanded) { ExpansionDelta(threadIndex, &childCount); childCount = -childCount; } nsTArray<nsMsgKey> threadKeys; nsTArray<PRUint32> threadFlags; nsTArray<PRUint8> threadLevels; if (threadIsExpanded) { threadKeys.SetCapacity(childCount); threadFlags.SetCapacity(childCount); threadLevels.SetCapacity(childCount); for (nsMsgViewIndex index = threadIndex + 1; index < GetSize() && m_levels[index]; index++) { threadKeys.AppendElement(m_keys[index]); threadFlags.AppendElement(m_flags[index]); threadLevels.AppendElement(m_levels[index]); } PRUint32 collapseCount; CollapseByIndex(threadIndex, &collapseCount); } nsMsgDBView::RemoveByIndex(threadIndex); nsMsgViewIndex newIndex = nsMsgViewIndex_None; AddHdr(threadHdr, &newIndex); // AddHdr doesn't always set newIndex, and getting it to do so // is going to require some refactoring. if (newIndex == nsMsgViewIndex_None) newIndex = FindHdr(threadHdr); if (threadIsExpanded) { m_keys.InsertElementsAt(newIndex + 1, threadKeys); m_flags.InsertElementsAt(newIndex + 1, threadFlags); m_levels.InsertElementsAt(newIndex + 1, threadLevels); } if (newIndex == nsMsgViewIndex_None) { NS_WARNING("newIndex=-1 in MoveThreadAt"); newIndex = 0; } m_flags[newIndex] = saveFlags; // unfreeze selection. if (hasSelection) RestoreSelection(preservedKey, preservedSelection); if (!changesDisabled) SetSuppressChangeNotifications(false); nsMsgViewIndex lowIndex = threadIndex < newIndex ? threadIndex : newIndex; nsMsgViewIndex highIndex = lowIndex == threadIndex ? newIndex : threadIndex; NoteChange(lowIndex, highIndex - lowIndex + childCount + 1, nsMsgViewNotificationCode::changed); }
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; }