NS_IMETHODIMP nsMsgXFVirtualFolderDBView::OnSearchDone(nsresult status) { NS_ENSURE_TRUE(m_viewFolder, NS_ERROR_NOT_INITIALIZED); // handle any non verified hits we haven't handled yet. if (NS_SUCCEEDED(status) && !m_doingQuickSearch && status != NS_MSG_SEARCH_INTERRUPTED) UpdateCacheAndViewForPrevSearchedFolders(nullptr); m_doingSearch = false; //we want to set imap delete model once the search is over because setting next //message after deletion will happen before deleting the message and search scope //can change with every search. mDeleteModel = nsMsgImapDeleteModels::MoveToTrash; //set to default in case it is non-imap folder nsIMsgFolder *curFolder = m_folders.SafeObjectAt(0); if (curFolder) GetImapDeleteModel(curFolder); nsCOMPtr<nsIMsgDatabase> virtDatabase; nsCOMPtr<nsIDBFolderInfo> dbFolderInfo; nsresult rv = m_viewFolder->GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo), getter_AddRefs(virtDatabase)); NS_ENSURE_SUCCESS(rv, rv); // count up the number of unread and total messages from the view, and set those in the // folder - easier than trying to keep the count up to date in the face of // search hits coming in while the user is reading/deleting messages. uint32_t numUnread = 0; for (uint32_t i = 0; i < m_flags.Length(); i++) if (m_flags[i] & nsMsgMessageFlags::Elided) { nsCOMPtr<nsIMsgThread> thread; GetThreadContainingIndex(i, getter_AddRefs(thread)); if (thread) { uint32_t unreadInThread; thread->GetNumUnreadChildren(&unreadInThread); numUnread += unreadInThread; } } else { if (!(m_flags[i] & nsMsgMessageFlags::Read)) numUnread++; } dbFolderInfo->SetNumUnreadMessages(numUnread); dbFolderInfo->SetNumMessages(m_totalMessagesInView); m_viewFolder->UpdateSummaryTotals(true); // force update from db. virtDatabase->Commit(nsMsgDBCommitType::kLargeCommit); if (!m_sortValid && m_sortType != nsMsgViewSortType::byThread && !(m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay)) { m_sortValid = false; //sort the results Sort(m_sortType, m_sortOrder); } m_foldersSearchingOver.Clear(); m_curFolderGettingHits = nullptr; return rv; }
nsresult nsMsgQuickSearchDBView::ExpansionDelta(nsMsgViewIndex index, int32_t *expansionDelta) { *expansionDelta = 0; if (index >= ((nsMsgViewIndex) m_keys.Length())) return NS_MSG_MESSAGE_NOT_FOUND; char flags = m_flags[index]; if (!(m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay)) return NS_OK; nsCOMPtr<nsIMsgThread> threadHdr; nsresult rv = GetThreadContainingIndex(index, getter_AddRefs(threadHdr)); NS_ENSURE_SUCCESS(rv, rv); uint32_t numChildren; threadHdr->GetNumChildren(&numChildren); nsCOMPtr<nsIMsgDBHdr> rootHdr; nsMsgKey rootKey; GetMsgHdrForViewIndex(index, getter_AddRefs(rootHdr)); rootHdr->GetMessageKey(&rootKey); // group threads can have the root key twice, one for the dummy row. bool rootKeySkipped = false; for (uint32_t i = 0; i < numChildren; i++) { nsCOMPtr<nsIMsgDBHdr> msgHdr; threadHdr->GetChildHdrAt(i, getter_AddRefs(msgHdr)); if (msgHdr) { nsMsgKey msgKey; msgHdr->GetMessageKey(&msgKey); if (msgKey != rootKey || (GroupViewUsesDummyRow() && rootKeySkipped)) { // if this hdr is in the original view, add it to new view. if (m_origKeys.BinaryIndexOf(msgKey) != m_origKeys.NoIndex) (*expansionDelta)++; } else { rootKeySkipped = true; } } } if (! (flags & nsMsgMessageFlags::Elided)) *expansionDelta = - (*expansionDelta); return NS_OK; }
nsresult nsMsgQuickSearchDBView::ListCollapsedChildren(nsMsgViewIndex viewIndex, nsIMutableArray *messageArray) { nsCOMPtr<nsIMsgThread> threadHdr; nsresult rv = GetThreadContainingIndex(viewIndex, getter_AddRefs(threadHdr)); NS_ENSURE_SUCCESS(rv, rv); uint32_t numChildren; threadHdr->GetNumChildren(&numChildren); nsCOMPtr<nsIMsgDBHdr> rootHdr; nsMsgKey rootKey; GetMsgHdrForViewIndex(viewIndex, getter_AddRefs(rootHdr)); rootHdr->GetMessageKey(&rootKey); // group threads can have the root key twice, one for the dummy row. bool rootKeySkipped = false; for (uint32_t i = 0; i < numChildren; i++) { nsCOMPtr<nsIMsgDBHdr> msgHdr; threadHdr->GetChildHdrAt(i, getter_AddRefs(msgHdr)); if (msgHdr) { nsMsgKey msgKey; msgHdr->GetMessageKey(&msgKey); if (msgKey != rootKey || (GroupViewUsesDummyRow() && rootKeySkipped)) { // if this hdr is in the original view, add it to new view. if (m_origKeys.BinaryIndexOf(msgKey) != m_origKeys.NoIndex) messageArray->AppendElement(msgHdr, false); } else { rootKeySkipped = true; } } } 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; }
// This method just removes the specified line from the view. It does // NOT delete it from the database. nsresult nsMsgThreadedDBView::RemoveByIndex(nsMsgViewIndex index) { nsresult rv = NS_OK; PRInt32 flags; if (!IsValidIndex(index)) return NS_MSG_INVALID_DBVIEW_INDEX; OnHeaderAddedOrDeleted(); flags = m_flags[index]; if (! (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay)) return nsMsgDBView::RemoveByIndex(index); nsCOMPtr<nsIMsgThread> threadHdr; GetThreadContainingIndex(index, getter_AddRefs(threadHdr)); NS_ENSURE_SUCCESS(rv, rv); PRUint32 numThreadChildren = 0; // if we can't get thread, it's already deleted and thus has 0 children if (threadHdr) threadHdr->GetNumChildren(&numThreadChildren); // check if we're the top level msg in the thread, and we're not collapsed. if ((flags & MSG_VIEW_FLAG_ISTHREAD) && !(flags & nsMsgMessageFlags::Elided) && (flags & MSG_VIEW_FLAG_HASCHILDREN)) { // fix flags on thread header...Newly promoted message // should have flags set correctly if (threadHdr) { nsMsgDBView::RemoveByIndex(index); nsCOMPtr<nsIMsgThread> nextThreadHdr; if (numThreadChildren > 0) { // unreadOnly nsCOMPtr<nsIMsgDBHdr> msgHdr; rv = threadHdr->GetChildHdrAt(0, getter_AddRefs(msgHdr)); if (msgHdr != nsnull) { PRUint32 flag = 0; msgHdr->GetFlags(&flag); if (numThreadChildren > 1) flag |= MSG_VIEW_FLAG_ISTHREAD | MSG_VIEW_FLAG_HASCHILDREN; m_flags[index] = flag; m_levels[index] = 0; } } } return rv; } else if (!(flags & MSG_VIEW_FLAG_ISTHREAD)) { // we're not deleting the top level msg, but top level msg might be only msg in thread now if (threadHdr && numThreadChildren == 1) { nsMsgKey msgKey; rv = threadHdr->GetChildKeyAt(0, &msgKey); if (NS_SUCCEEDED(rv)) { nsMsgViewIndex threadIndex = FindViewIndex(msgKey); if (threadIndex != nsMsgViewIndex_None) { PRUint32 flags = m_flags[threadIndex]; flags &= ~(MSG_VIEW_FLAG_ISTHREAD | nsMsgMessageFlags::Elided | MSG_VIEW_FLAG_HASCHILDREN); m_flags[threadIndex] = flags; NoteChange(threadIndex, 1, nsMsgViewNotificationCode::changed); } } } return nsMsgDBView::RemoveByIndex(index); } // deleting collapsed thread header is special case. Child will be promoted, // so just tell FE that line changed, not that it was deleted if (threadHdr && numThreadChildren > 0) // header has aleady been deleted from thread { // change the id array and flags array to reflect the child header. // If we're not deleting the header, we want the second header, // Otherwise, the first one (which just got promoted). nsCOMPtr<nsIMsgDBHdr> msgHdr; rv = threadHdr->GetChildHdrAt(0, getter_AddRefs(msgHdr)); if (msgHdr != nsnull) { msgHdr->GetMessageKey(&m_keys[index]); PRUint32 flag = 0; msgHdr->GetFlags(&flag); flag |= MSG_VIEW_FLAG_ISTHREAD; // if only hdr in thread (with one about to be deleted) if (numThreadChildren == 1) { // adjust flags. flag &= ~MSG_VIEW_FLAG_HASCHILDREN; flag &= ~nsMsgMessageFlags::Elided; // tell FE that thread header needs to be repainted. NoteChange(index, 1, nsMsgViewNotificationCode::changed); } else { flag |= MSG_VIEW_FLAG_HASCHILDREN; flag |= nsMsgMessageFlags::Elided; } m_flags[index] = flag; mIndicesToNoteChange.RemoveElement(index); } else NS_ASSERTION(false, "couldn't find thread child"); NoteChange(index, 1, nsMsgViewNotificationCode::changed); } else { // we may have deleted a whole, collapsed thread - if so, // ensure that the current index will be noted as changed. if (!mIndicesToNoteChange.Contains(index)) mIndicesToNoteChange.AppendElement(index); rv = nsMsgDBView::RemoveByIndex(index); } return rv; }