// 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); }
nsresult nsMsgThreadedDBView::SortThreads(nsMsgViewSortTypeValue sortType, nsMsgViewSortOrderValue sortOrder) { NS_PRECONDITION(m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay, "trying to sort unthreaded threads"); PRUint32 numThreads = 0; // the idea here is that copy the current view, then build up an m_keys and m_flags array of just the top level // messages in the view, and then call nsMsgDBView::Sort(sortType, sortOrder). // Then, we expand the threads in the result array that were expanded in the original view (perhaps by copying // from the original view, but more likely just be calling expand). for (PRUint32 i = 0; i < m_keys.Length(); i++) { if (m_flags[i] & MSG_VIEW_FLAG_ISTHREAD) { if (numThreads < i) { m_keys[numThreads] = m_keys[i]; m_flags[numThreads] = m_flags[i]; } m_levels[numThreads] = 0; numThreads++; } } m_keys.SetLength(numThreads); m_flags.SetLength(numThreads); m_levels.SetLength(numThreads); //m_viewFlags &= ~nsMsgViewFlagsType::kThreadedDisplay; m_sortType = nsMsgViewSortType::byNone; // sort from scratch nsMsgDBView::Sort(sortType, sortOrder); m_viewFlags |= nsMsgViewFlagsType::kThreadedDisplay; SetSuppressChangeNotifications(true); // Loop through the original array, for each thread that's expanded, find it in the new array // and expand the thread. We have to update MSG_VIEW_FLAG_HAS_CHILDREN because // we may be going from a flat sort, which doesn't maintain that flag, // to a threaded sort, which requires that flag. for (PRUint32 j = 0; j < m_keys.Length(); j++) { PRUint32 flags = m_flags[j]; if ((flags & (MSG_VIEW_FLAG_HASCHILDREN | nsMsgMessageFlags::Elided)) == MSG_VIEW_FLAG_HASCHILDREN) { PRUint32 numExpanded; m_flags[j] = flags | nsMsgMessageFlags::Elided; ExpandByIndex(j, &numExpanded); j += numExpanded; if (numExpanded > 0) m_flags[j - numExpanded] = flags | MSG_VIEW_FLAG_HASCHILDREN; } else if (flags & MSG_VIEW_FLAG_ISTHREAD && ! (flags & MSG_VIEW_FLAG_HASCHILDREN)) { nsCOMPtr <nsIMsgDBHdr> msgHdr; nsCOMPtr <nsIMsgThread> pThread; m_db->GetMsgHdrForKey(m_keys[j], getter_AddRefs(msgHdr)); if (msgHdr) { m_db->GetThreadContainingMsgHdr(msgHdr, getter_AddRefs(pThread)); if (pThread) { PRUint32 numChildren; pThread->GetNumChildren(&numChildren); if (numChildren > 1) m_flags[j] = flags | MSG_VIEW_FLAG_HASCHILDREN | nsMsgMessageFlags::Elided; } } } } SetSuppressChangeNotifications(false); return NS_OK; }