nsresult nsMsgSearchDBView::DeleteMessages(nsIMsgWindow *window, nsMsgViewIndex *indices, int32_t numIndices, bool deleteStorage) { nsresult rv = GetFoldersAndHdrsForSelection(indices, numIndices); NS_ENSURE_SUCCESS(rv, rv); if (mDeleteModel != nsMsgImapDeleteModels::MoveToTrash) deleteStorage = true; if (mDeleteModel != nsMsgImapDeleteModels::IMAPDelete) m_deletingRows = true; // remember the deleted messages in case the user undoes the delete, // and we want to restore the hdr to the view, even if it no // longer matches the search criteria. for (nsMsgViewIndex i = 0; i < (nsMsgViewIndex) numIndices; i++) { nsCOMPtr<nsIMsgDBHdr> msgHdr; (void) GetMsgHdrForViewIndex(indices[i], getter_AddRefs(msgHdr)); if (msgHdr) RememberDeletedMsgHdr(msgHdr); // if we are deleting rows, save off the view indices if (m_deletingRows) mIndicesToNoteChange.AppendElement(indices[i]); } rv = deleteStorage ? ProcessRequestsInAllFolders(window) : ProcessRequestsInOneFolder(window); if (NS_FAILED(rv)) m_deletingRows = false; return rv; }
nsresult nsMsgQuickSearchDBView::ListIdsInThread(nsIMsgThread *threadHdr, nsMsgViewIndex startOfThreadViewIndex, uint32_t *pNumListed) { if (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay && ! (m_viewFlags & nsMsgViewFlagsType::kGroupBySort)) { nsMsgKey parentKey = m_keys[startOfThreadViewIndex++]; return ListIdsInThreadOrder(threadHdr, parentKey, 1, &startOfThreadViewIndex, pNumListed); } uint32_t numChildren; threadHdr->GetNumChildren(&numChildren); uint32_t i; uint32_t viewIndex = startOfThreadViewIndex + 1; nsCOMPtr<nsIMsgDBHdr> rootHdr; nsMsgKey rootKey; uint32_t rootFlags = m_flags[startOfThreadViewIndex]; *pNumListed = 0; GetMsgHdrForViewIndex(startOfThreadViewIndex, getter_AddRefs(rootHdr)); rootHdr->GetMessageKey(&rootKey); // group threads can have the root key twice, one for the dummy row. bool rootKeySkipped = false; for (i = 0; i < numChildren; i++) { nsCOMPtr<nsIMsgDBHdr> msgHdr; threadHdr->GetChildHdrAt(i, getter_AddRefs(msgHdr)); if (msgHdr != nullptr) { nsMsgKey msgKey; msgHdr->GetMessageKey(&msgKey); if (msgKey != rootKey || (GroupViewUsesDummyRow() && rootKeySkipped)) { nsMsgViewIndex threadRootIndex = m_origKeys.BinaryIndexOf(msgKey); // if this hdr is in the original view, add it to new view. if (threadRootIndex != nsMsgViewIndex_None) { uint32_t childFlags; msgHdr->GetFlags(&childFlags); InsertMsgHdrAt(viewIndex, msgHdr, msgKey, childFlags, FindLevelInThread(msgHdr, startOfThreadViewIndex, viewIndex)); if (! (rootFlags & MSG_VIEW_FLAG_HASCHILDREN)) m_flags[startOfThreadViewIndex] = rootFlags | MSG_VIEW_FLAG_HASCHILDREN; viewIndex++; (*pNumListed)++; } } else { rootKeySkipped = true; } } } return NS_OK; }
nsresult nsMsgQuickSearchDBView::DeleteMessages(nsIMsgWindow *window, nsMsgViewIndex *indices, int32_t numIndices, bool deleteStorage) { for (nsMsgViewIndex i = 0; i < (nsMsgViewIndex) numIndices; i++) { nsCOMPtr<nsIMsgDBHdr> msgHdr; (void) GetMsgHdrForViewIndex(indices[i],getter_AddRefs(msgHdr)); if (msgHdr) RememberDeletedMsgHdr(msgHdr); } return nsMsgDBView::DeleteMessages(window, indices, numIndices, deleteStorage); }
// This method removes the specified line from the view, and adjusts the // various flags and levels of affected messages. nsresult nsMsgSearchDBView::RemoveByIndex(nsMsgViewIndex index) { if (!IsValidIndex(index)) return NS_MSG_INVALID_DBVIEW_INDEX; if (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay) { nsCOMPtr<nsIMsgDBHdr> msgHdr; nsCOMPtr<nsIMsgThread> thread; nsresult rv = GetMsgHdrForViewIndex(index, getter_AddRefs(msgHdr)); NS_ENSURE_SUCCESS(rv, rv); GetXFThreadFromMsgHdr(msgHdr, getter_AddRefs(thread)); if (thread) { nsMsgXFViewThread *viewThread = static_cast<nsMsgXFViewThread*>(thread.get()); if (viewThread->MsgCount() == 2) { // if we removed the next to last message in the thread, // we need to adjust the flags on the first message in the thread. nsMsgViewIndex threadIndex = m_levels[index] ? index -1 : index; if (threadIndex != nsMsgViewIndex_None) { AndExtraFlag(threadIndex, ~(MSG_VIEW_FLAG_ISTHREAD | nsMsgMessageFlags::Elided | MSG_VIEW_FLAG_HASCHILDREN)); m_levels[threadIndex] = 0; NoteChange(threadIndex, 1, nsMsgViewNotificationCode::changed); } } // Bump up the level of all the descendents of the message // that was removed, if the thread was expanded. uint8_t removedLevel = m_levels[index]; nsMsgViewIndex i = index + 1; if (i < m_levels.Length() && m_levels[i] > removedLevel) { // promote the child of the removed message. uint8_t promotedLevel = m_levels[i]; m_levels[i] = promotedLevel - 1; i++; // now promote all the children of the promoted message. for (; i < m_levels.Length() && m_levels[i] > promotedLevel; i++) m_levels[i] = m_levels[i] - 1; } } } m_folders.RemoveObjectAt(index); return nsMsgDBView::RemoveByIndex(index); }
nsMsgViewIndex nsMsgSearchDBView::FindHdr(nsIMsgDBHdr *msgHdr, nsMsgViewIndex startIndex, bool allowDummy) { nsCOMPtr<nsIMsgDBHdr> curHdr; uint32_t index; // it would be nice to take advantage of sorted views when possible. for (index = startIndex; index < GetSize(); index++) { GetMsgHdrForViewIndex(index, getter_AddRefs(curHdr)); if (curHdr == msgHdr && (allowDummy || !(m_flags[index] & MSG_VIEW_FLAG_DUMMY) || (m_flags[index] & nsMsgMessageFlags::Elided))) break; } return index < GetSize() ? index : nsMsgViewIndex_None; }
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; }
// if nothing selected, return an NS_ERROR NS_IMETHODIMP nsMsgSearchDBView::GetHdrForFirstSelectedMessage(nsIMsgDBHdr **hdr) { NS_ENSURE_ARG_POINTER(hdr); int32_t index; if (!mTreeSelection) { // We're in standalone mode, so use the message view index to get the header // We can't use the key here because we don't have an m_db index = m_currentlyDisplayedViewIndex; } else { nsresult rv = mTreeSelection->GetCurrentIndex(&index); NS_ENSURE_SUCCESS(rv, rv); } return GetMsgHdrForViewIndex(index, hdr); }
void nsMsgGroupView::InternalClose() { m_groupsTable.Clear(); // nothing to do if we're not grouped. if (!(m_viewFlags & nsMsgViewFlagsType::kGroupBySort)) return; bool rcvDate = false; if (m_sortType == nsMsgViewSortType::byReceived) rcvDate = true; if (m_db && ((m_sortType == nsMsgViewSortType::byDate) || (m_sortType == nsMsgViewSortType::byReceived))) { nsCOMPtr <nsIDBFolderInfo> dbFolderInfo; m_db->GetDBFolderInfo(getter_AddRefs(dbFolderInfo)); if (dbFolderInfo) { uint32_t expandFlags = 0; uint32_t num = GetSize(); for (uint32_t i = 0; i < num; i++) { if (m_flags[i] & MSG_VIEW_FLAG_ISTHREAD && ! (m_flags[i] & nsMsgMessageFlags::Elided)) { nsCOMPtr <nsIMsgDBHdr> msgHdr; GetMsgHdrForViewIndex(i, getter_AddRefs(msgHdr)); if (msgHdr) { uint32_t ageBucket; nsresult rv = GetAgeBucketValue(msgHdr, &ageBucket, rcvDate); if (NS_SUCCEEDED(rv)) expandFlags |= 1 << ageBucket; } } } dbFolderInfo->SetUint32Property("dateGroupFlags", expandFlags); } } }
NS_IMETHODIMP nsMsgGroupView::GetCellProperties(int32_t aRow, nsITreeColumn *aCol, nsAString& aProperties) { if (!IsValidIndex(aRow)) return NS_MSG_INVALID_DBVIEW_INDEX; if (m_flags[aRow] & MSG_VIEW_FLAG_DUMMY) { aProperties.AssignLiteral("dummy read"); if (!(m_flags[aRow] & nsMsgMessageFlags::Elided)) return NS_OK; // Set unread property if a collapsed group thread has unread. nsCOMPtr <nsIMsgDBHdr> msgHdr; nsresult rv = GetMsgHdrForViewIndex(aRow, getter_AddRefs(msgHdr)); NS_ENSURE_SUCCESS(rv, rv); nsString hashKey; rv = HashHdr(msgHdr, hashKey); if (NS_FAILED(rv)) return NS_OK; nsCOMPtr<nsIMsgThread> msgThread; m_groupsTable.Get(hashKey, getter_AddRefs(msgThread)); nsMsgGroupThread *groupThread = static_cast<nsMsgGroupThread *>(msgThread.get()); if (!groupThread) return NS_OK; uint32_t numUnrMsg = 0; groupThread->GetNumUnreadChildren(&numUnrMsg); if (numUnrMsg > 0) aProperties.AppendLiteral(" hasUnread"); return NS_OK; } return nsMsgDBView::GetCellProperties(aRow, aCol, aProperties); }
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; }
// 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 nsMsgSearchDBView::MoveThreadAt(nsMsgViewIndex threadIndex) { bool updatesSuppressed = mSuppressChangeNotification; // Turn off tree notifications so that we don't reload the current message. if (!updatesSuppressed) SetSuppressChangeNotifications(true); nsCOMPtr<nsIMsgDBHdr> threadHdr; GetMsgHdrForViewIndex(threadIndex, getter_AddRefs(threadHdr)); uint32_t saveFlags = m_flags[threadIndex]; bool threadIsExpanded = !(saveFlags & nsMsgMessageFlags::Elided); int32_t childCount = 0; nsMsgKey preservedKey; nsAutoTArray<nsMsgKey, 1> preservedSelection; int32_t selectionCount; int32_t currentIndex; bool hasSelection = mTree && mTreeSelection && ((NS_SUCCEEDED(mTreeSelection->GetCurrentIndex(¤tIndex)) && currentIndex >= 0 && (uint32_t)currentIndex < GetSize()) || (NS_SUCCEEDED(mTreeSelection->GetRangeCount(&selectionCount)) && selectionCount > 0)); if (hasSelection) SaveAndClearSelection(&preservedKey, preservedSelection); if (threadIsExpanded) { ExpansionDelta(threadIndex, &childCount); childCount = -childCount; } nsTArray<nsMsgKey> threadKeys; nsTArray<uint32_t> threadFlags; nsTArray<uint8_t> threadLevels; nsCOMArray<nsIMsgFolder> threadFolders; if (threadIsExpanded) { threadKeys.SetCapacity(childCount); threadFlags.SetCapacity(childCount); threadLevels.SetCapacity(childCount); threadFolders.SetCapacity(childCount); for (nsMsgViewIndex index = threadIndex + 1; index < (nsMsgViewIndex) GetSize() && m_levels[index]; index++) { threadKeys.AppendElement(m_keys[index]); threadFlags.AppendElement(m_flags[index]); threadLevels.AppendElement(m_levels[index]); threadFolders.AppendObject(m_folders[index]); } uint32_t collapseCount; CollapseByIndex(threadIndex, &collapseCount); } nsMsgDBView::RemoveByIndex(threadIndex); m_folders.RemoveObjectAt(threadIndex); nsMsgViewIndex newIndex = GetIndexForThread(threadHdr); NS_ASSERTION(newIndex == m_levels.Length() || !m_levels[newIndex], "inserting into middle of thread"); if (newIndex == nsMsgViewIndex_None) newIndex = 0; nsMsgKey msgKey; uint32_t msgFlags; threadHdr->GetMessageKey(&msgKey); threadHdr->GetFlags(&msgFlags); InsertMsgHdrAt(newIndex, threadHdr, msgKey, msgFlags, 0); if (threadIsExpanded) { m_keys.InsertElementsAt(newIndex + 1, threadKeys); m_flags.InsertElementsAt(newIndex + 1, threadFlags); m_levels.InsertElementsAt(newIndex + 1, threadLevels); m_folders.InsertObjectsAt(threadFolders, newIndex + 1); } m_flags[newIndex] = saveFlags; // unfreeze selection. if (hasSelection) RestoreSelection(preservedKey, preservedSelection); if (!updatesSuppressed) SetSuppressChangeNotifications(false); nsMsgViewIndex lowIndex = threadIndex < newIndex ? threadIndex : newIndex; nsMsgViewIndex highIndex = lowIndex == threadIndex ? newIndex : threadIndex; NoteChange(lowIndex, highIndex - lowIndex + childCount + 1, nsMsgViewNotificationCode::changed); }
nsresult nsMsgQuickSearchDBView::SortThreads(nsMsgViewSortTypeValue sortType, nsMsgViewSortOrderValue sortOrder) { // don't need to sort by threads for group view. if (m_viewFlags & nsMsgViewFlagsType::kGroupBySort) return NS_OK; // iterate over the messages in the view, getting the thread id's // sort m_keys so we can quickly find if a key is in the view. m_keys.Sort(); // array of the threads' root hdr keys. nsTArray<nsMsgKey> threadRootIds; nsCOMPtr <nsIMsgDBHdr> rootHdr; nsCOMPtr <nsIMsgDBHdr> msgHdr; nsCOMPtr <nsIMsgThread> threadHdr; for (uint32_t i = 0; i < m_keys.Length(); i++) { GetMsgHdrForViewIndex(i, getter_AddRefs(msgHdr)); m_db->GetThreadContainingMsgHdr(msgHdr, getter_AddRefs(threadHdr)); if (threadHdr) { nsMsgKey rootKey; threadHdr->GetChildKeyAt(0, &rootKey); nsMsgViewIndex threadRootIndex = threadRootIds.BinaryIndexOf(rootKey); // if we already have that id in top level threads, ignore this msg. if (threadRootIndex != nsMsgViewIndex_None) continue; // it would be nice if GetInsertIndexHelper always found the hdr, but it doesn't. threadHdr->GetChildHdrAt(0, getter_AddRefs(rootHdr)); if (!rootHdr) continue; threadRootIndex = GetInsertIndexHelper(rootHdr, threadRootIds, nullptr, nsMsgViewSortOrder::ascending, nsMsgViewSortType::byId); threadRootIds.InsertElementAt(threadRootIndex, rootKey); } } // need to sort the top level threads now by sort order, if it's not by id. if (sortType != nsMsgViewSortType::byId) { m_keys.SwapElements(threadRootIds); nsMsgDBView::Sort(sortType, sortOrder); threadRootIds.SwapElements(m_keys); } m_keys.Clear(); m_levels.Clear(); m_flags.Clear(); // now we've build up the list of thread ids - need to build the view // from that. So for each thread id, we need to list the messages in the thread. uint32_t numThreads = threadRootIds.Length(); for (uint32_t threadIndex = 0; threadIndex < numThreads; threadIndex++) { m_db->GetMsgHdrForKey(threadRootIds[threadIndex], getter_AddRefs(rootHdr)); if (rootHdr) { nsCOMPtr <nsIMsgDBHdr> displayRootHdr; m_db->GetThreadContainingMsgHdr(rootHdr, getter_AddRefs(threadHdr)); if (threadHdr) { nsMsgKey rootKey; uint32_t rootFlags; GetFirstMessageHdrToDisplayInThread(threadHdr, getter_AddRefs(displayRootHdr)); if (!displayRootHdr) continue; displayRootHdr->GetMessageKey(&rootKey); displayRootHdr->GetFlags(&rootFlags); rootFlags |= MSG_VIEW_FLAG_ISTHREAD; m_keys.AppendElement(rootKey); m_flags.AppendElement(rootFlags); m_levels.AppendElement(0); nsMsgViewIndex startOfThreadViewIndex = m_keys.Length(); nsMsgViewIndex rootIndex = startOfThreadViewIndex - 1; uint32_t numListed = 0; ListIdsInThreadOrder(threadHdr, rootKey, 1, &startOfThreadViewIndex, &numListed); if (numListed > 0) m_flags[rootIndex] = rootFlags | MSG_VIEW_FLAG_HASCHILDREN; } } } return NS_OK; }
NS_IMETHODIMP nsMsgGroupView::CellTextForColumn(int32_t aRow, const char16_t *aColumnName, nsAString &aValue) { if (!IsValidIndex(aRow)) return NS_MSG_INVALID_DBVIEW_INDEX; if (m_flags[aRow] & MSG_VIEW_FLAG_DUMMY && aColumnName[0] != 'u') { nsCOMPtr <nsIMsgDBHdr> msgHdr; nsresult rv = GetMsgHdrForViewIndex(aRow, getter_AddRefs(msgHdr)); NS_ENSURE_SUCCESS(rv, rv); nsString hashKey; rv = HashHdr(msgHdr, hashKey); if (NS_FAILED(rv)) return NS_OK; nsCOMPtr<nsIMsgThread> msgThread; m_groupsTable.Get(hashKey, getter_AddRefs(msgThread)); nsMsgGroupThread * groupThread = static_cast<nsMsgGroupThread *>(msgThread.get()); if (aColumnName[0] == 's' && aColumnName[1] == 'u' ) { uint32_t flags; bool rcvDate = false; msgHdr->GetFlags(&flags); aValue.Truncate(); nsString tmp_str; switch (m_sortType) { case nsMsgViewSortType::byReceived: rcvDate = true; case nsMsgViewSortType::byDate: { uint32_t ageBucket = 0; GetAgeBucketValue(msgHdr, &ageBucket, rcvDate); switch (ageBucket) { case 1: if (m_kTodayString.IsEmpty()) m_kTodayString.Adopt(GetString(MOZ_UTF16("today"))); aValue.Assign(m_kTodayString); break; case 2: if (m_kYesterdayString.IsEmpty()) m_kYesterdayString.Adopt(GetString(MOZ_UTF16("yesterday"))); aValue.Assign(m_kYesterdayString); break; case 3: if (m_kLastWeekString.IsEmpty()) m_kLastWeekString.Adopt(GetString(MOZ_UTF16("lastWeek"))); aValue.Assign(m_kLastWeekString); break; case 4: if (m_kTwoWeeksAgoString.IsEmpty()) m_kTwoWeeksAgoString.Adopt(GetString(MOZ_UTF16("twoWeeksAgo"))); aValue.Assign(m_kTwoWeeksAgoString); break; case 5: if (m_kOldMailString.IsEmpty()) m_kOldMailString.Adopt(GetString(MOZ_UTF16("older"))); aValue.Assign(m_kOldMailString); break; default: NS_ASSERTION(false, "bad age thread"); break; } break; } case nsMsgViewSortType::bySubject: FetchSubject(msgHdr, m_flags[aRow], aValue); break; case nsMsgViewSortType::byAuthor: FetchAuthor(msgHdr, aValue); break; case nsMsgViewSortType::byStatus: rv = FetchStatus(m_flags[aRow], aValue); if (aValue.IsEmpty()) { tmp_str.Adopt(GetString(MOZ_UTF16("messagesWithNoStatus"))); aValue.Assign(tmp_str); } break; case nsMsgViewSortType::byTags: rv = FetchTags(msgHdr, aValue); if (aValue.IsEmpty()) { tmp_str.Adopt(GetString(MOZ_UTF16("untaggedMessages"))); aValue.Assign(tmp_str); } break; case nsMsgViewSortType::byPriority: FetchPriority(msgHdr, aValue); if (aValue.IsEmpty()) { tmp_str.Adopt(GetString(MOZ_UTF16("noPriority"))); aValue.Assign(tmp_str); } break; case nsMsgViewSortType::byAccount: FetchAccount(msgHdr, aValue); break; case nsMsgViewSortType::byRecipient: FetchRecipients(msgHdr, aValue); break; case nsMsgViewSortType::byAttachments: tmp_str.Adopt(GetString(flags & nsMsgMessageFlags::Attachment ? MOZ_UTF16("attachments") : MOZ_UTF16("noAttachments"))); aValue.Assign(tmp_str); break; case nsMsgViewSortType::byFlagged: tmp_str.Adopt(GetString(flags & nsMsgMessageFlags::Marked ? MOZ_UTF16("groupFlagged") : MOZ_UTF16("notFlagged"))); aValue.Assign(tmp_str); break; // byLocation is a special case; we don't want to have duplicate // all this logic in nsMsgSearchDBView, and its hash key is what we // want anyways, so just copy it across. case nsMsgViewSortType::byLocation: case nsMsgViewSortType::byCorrespondent: aValue = hashKey; break; case nsMsgViewSortType::byCustom: { nsIMsgCustomColumnHandler* colHandler = GetCurColumnHandlerFromDBInfo(); if (colHandler) { rv = colHandler->GetSortStringForRow(msgHdr.get(), aValue); break; } } default: NS_ASSERTION(false, "we don't sort by group for this type"); break; } if (groupThread) { // Get number of messages in group nsAutoString formattedCountMsg; uint32_t numMsg = groupThread->NumRealChildren(); formattedCountMsg.AppendInt(numMsg); // Get number of unread messages nsAutoString formattedCountUnrMsg; uint32_t numUnrMsg = 0; groupThread->GetNumUnreadChildren(&numUnrMsg); formattedCountUnrMsg.AppendInt(numUnrMsg); // Add text to header aValue.Append(NS_LITERAL_STRING(" (")); if (numUnrMsg) { aValue.Append(formattedCountUnrMsg); aValue.Append(NS_LITERAL_STRING("/")); } aValue.Append(formattedCountMsg); aValue.Append(NS_LITERAL_STRING(")")); } } else if (aColumnName[0] == 't' && aColumnName[1] == 'o') { nsAutoString formattedCountString; uint32_t numChildren = (groupThread) ? groupThread->NumRealChildren() : 0; formattedCountString.AppendInt(numChildren); aValue.Assign(formattedCountString); } return NS_OK; } return nsMsgDBView::CellTextForColumn(aRow, aColumnName, aValue); }
// 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); }