NS_IMETHODIMP nsMsgSearchDBView::Sort(nsMsgViewSortTypeValue sortType, nsMsgViewSortOrderValue sortOrder) { int32_t rowCountBeforeSort = GetSize(); if (!rowCountBeforeSort) return NS_OK; if (m_viewFlags & (nsMsgViewFlagsType::kThreadedDisplay | nsMsgViewFlagsType::kGroupBySort)) { // ### This forgets which threads were expanded, and is sub-optimal // since it rebuilds the thread objects. m_sortType = sortType; m_sortOrder = sortOrder; return RebuildView(m_viewFlags); } nsMsgKey preservedKey; nsAutoTArray<nsMsgKey, 1> preservedSelection; SaveAndClearSelection(&preservedKey, preservedSelection); nsresult rv = nsMsgDBView::Sort(sortType,sortOrder); // the sort may have changed the number of rows // before we restore the selection, tell the tree // do this before we call restore selection // this is safe when there is no selection. rv = AdjustRowCount(rowCountBeforeSort, GetSize()); RestoreSelection(preservedKey, preservedSelection); if (mTree) mTree->Invalidate(); NS_ENSURE_SUCCESS(rv,rv); return rv; }
// E.g., if the day has changed, we need to close and re-open the view. // Or, if we're switching between grouping and threading in a cross-folder // saved search. In that case, we needed to build an enumerator based on the // old view type, and internally close the view based on its old type, but // rebuild the new view based on the new view type. So we pass the new // view flags to OpenWithHdrs. nsresult nsMsgGroupView::RebuildView(nsMsgViewFlagsTypeValue newFlags) { nsCOMPtr <nsISimpleEnumerator> headers; if (NS_SUCCEEDED(GetMessageEnumerator(getter_AddRefs(headers)))) { int32_t count; m_dayChanged = false; nsAutoTArray<nsMsgKey, 1> preservedSelection; nsMsgKey curSelectedKey; SaveAndClearSelection(&curSelectedKey, preservedSelection); InternalClose(); int32_t oldSize = GetSize(); // this is important, because the tree will ask us for our // row count, which get determine from the number of keys. m_keys.Clear(); // be consistent m_flags.Clear(); m_levels.Clear(); // this needs to happen after we remove all the keys, since RowCountChanged() will call our GetRowCount() if (mTree) mTree->RowCountChanged(0, -oldSize); SetSuppressChangeNotifications(true); nsresult rv = OpenWithHdrs(headers, m_sortType, m_sortOrder, newFlags, &count); SetSuppressChangeNotifications(false); if (mTree) mTree->RowCountChanged(0, GetSize()); NS_ENSURE_SUCCESS(rv,rv); // now, restore our desired selection nsAutoTArray<nsMsgKey, 1> keyArray; keyArray.AppendElement(curSelectedKey); return RestoreSelection(curSelectedKey, keyArray); } 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); }
// 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); }
NS_IMETHODIMP nsMsgThreadedDBView::Sort(nsMsgViewSortTypeValue sortType, nsMsgViewSortOrderValue sortOrder) { nsresult rv; PRInt32 rowCountBeforeSort = GetSize(); if (!rowCountBeforeSort) { // still need to setup our flags even when no articles - bug 98183. m_sortType = sortType; if (sortType == nsMsgViewSortType::byThread && ! (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay)) SetViewFlags(m_viewFlags | nsMsgViewFlagsType::kThreadedDisplay); SaveSortInfo(sortType, sortOrder); return NS_OK; } // sort threads by sort order bool sortThreads = m_viewFlags & (nsMsgViewFlagsType::kThreadedDisplay | nsMsgViewFlagsType::kGroupBySort); // if sort type is by thread, and we're already threaded, change sort type to byId if (sortType == nsMsgViewSortType::byThread && (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay) != 0) sortType = nsMsgViewSortType::byId; nsMsgKey preservedKey; nsAutoTArray<nsMsgKey, 1> preservedSelection; SaveAndClearSelection(&preservedKey, preservedSelection); // if the client wants us to forget our cached id arrays, they // should build a new view. If this isn't good enough, we // need a method to do that. if (sortType != m_sortType || !m_sortValid || sortThreads) { SaveSortInfo(sortType, sortOrder); if (sortType == nsMsgViewSortType::byThread) { m_sortType = sortType; m_viewFlags |= nsMsgViewFlagsType::kThreadedDisplay; m_viewFlags &= nsMsgViewFlagsType::kGroupBySort; if ( m_havePrevView) { // restore saved id array and flags array m_keys = m_prevKeys; m_flags = m_prevFlags; m_levels = m_prevLevels; m_sortValid = true; // the sort may have changed the number of rows // before we restore the selection, tell the tree // do this before we call restore selection // this is safe when there is no selection. rv = AdjustRowCount(rowCountBeforeSort, GetSize()); RestoreSelection(preservedKey, preservedSelection); if (mTree) mTree->Invalidate(); return NS_OK; } else { // set sort info in anticipation of what Init will do. InitThreadedView(nsnull); // build up thread list. if (sortOrder != nsMsgViewSortOrder::ascending) Sort(sortType, sortOrder); // the sort may have changed the number of rows // before we update the selection, tell the tree // do this before we call restore selection // this is safe when there is no selection. rv = AdjustRowCount(rowCountBeforeSort, GetSize()); RestoreSelection(preservedKey, preservedSelection); if (mTree) mTree->Invalidate(); return NS_OK; } } else if (sortType != nsMsgViewSortType::byThread && (m_sortType == nsMsgViewSortType::byThread || sortThreads)/* && !m_havePrevView*/) { if (sortThreads) { SortThreads(sortType, sortOrder); sortType = nsMsgViewSortType::byThread; // hack so base class won't do anything } else { // going from SortByThread to non-thread sort - must build new key, level,and flags arrays m_prevKeys = m_keys; m_prevFlags = m_flags; m_prevLevels = m_levels; // do this before we sort, so that we'll use the cheap method // of expanding. m_viewFlags &= ~(nsMsgViewFlagsType::kThreadedDisplay | nsMsgViewFlagsType::kGroupBySort); ExpandAll(); // m_idArray.RemoveAll(); // m_flags.Clear(); m_havePrevView = true; } } } else if (m_sortOrder != sortOrder)// check for toggling the sort { nsMsgDBView::Sort(sortType, sortOrder); } if (!sortThreads) { // call the base class in case we're not sorting by thread rv = nsMsgDBView::Sort(sortType, sortOrder); SaveSortInfo(sortType, sortOrder); } // the sort may have changed the number of rows // before we restore the selection, tell the tree // do this before we call restore selection // this is safe when there is no selection. rv = AdjustRowCount(rowCountBeforeSort, GetSize()); RestoreSelection(preservedKey, preservedSelection); if (mTree) mTree->Invalidate(); NS_ENSURE_SUCCESS(rv,rv); return NS_OK; }