add support for sorting threads, r=sspitzer, sr=mscott 72493 also make expand all state remembered 64426

git-svn-id: svn://10.0.0.236/trunk@144825 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
bienvenu%netscape.com
2003-07-14 14:17:48 +00:00
parent ebc0973e4b
commit 64a573c7c8
11 changed files with 568 additions and 417 deletions

View File

@@ -77,11 +77,13 @@ interface nsMsgViewFlagsType
const nsMsgViewFlagsTypeValue kThreadedDisplay = 0x1;
const nsMsgViewFlagsTypeValue kShowIgnored = 0x8;
const nsMsgViewFlagsTypeValue kUnreadOnly = 0x10;
const nsMsgViewFlagsTypeValue kExpandAll = 0x20;
};
[scriptable, uuid(b2f31bca-fd18-11d4-a5be-0060b0fc04b7)]
interface nsMsgViewSortType
{
const nsMsgViewSortTypeValue byNone = 0x11; /* not sorted */
const nsMsgViewSortTypeValue byDate = 0x12;
const nsMsgViewSortTypeValue bySubject = 0x13;
const nsMsgViewSortTypeValue byAuthor = 0x14;
@@ -93,7 +95,7 @@ interface nsMsgViewSortType
const nsMsgViewSortTypeValue byFlagged = 0x1a;
const nsMsgViewSortTypeValue byUnread = 0x1b;
const nsMsgViewSortTypeValue byRecipient = 0x1c;
const nsMsgViewSortTypeValue byLocation = 0x1d;
const nsMsgViewSortTypeValue byLocation = 0x1d;
const nsMsgViewSortTypeValue byLabel = 0x1e;
const nsMsgViewSortTypeValue byJunkStatus = 0x1f;
};

View File

@@ -274,8 +274,6 @@ function RerootFolder(uri, newFolder, viewType, viewFlags, sortType, sortOrder)
//Set the window's new open folder.
msgWindow.openFolder = newFolder;
SetViewFlags(viewFlags);
//the new folder being selected should have its biff state get cleared.
if(newFolder)
{
@@ -334,7 +332,7 @@ function SwitchView(command)
// now switch views
var oldSortType = gDBView ? gDBView.sortType : nsMsgViewSortType.byThread;
var oldSortOrder = gDBView ? gDBView.sortOrder : nsMsgViewSortOrder.ascending;
var viewFlags = gCurViewFlags;
var viewFlags = gDBView ? gDBView.viewFlags : gCurViewFlags;
// close existing view.
if (gDBView) {
@@ -626,12 +624,6 @@ function CreateDBView(msgFolder, viewType, viewFlags, sortType, sortOrder)
UpdateSortIndicators(gCurSortType, sortOrder);
}
function SetViewFlags(viewFlags)
{
if (!gDBView) return;
gDBView.viewFlags = viewFlags;
}
//------------------------------------------------------------
// Sets the column header sort icon based on the requested
// column and direction.

View File

@@ -365,7 +365,7 @@ var DefaultController =
case "cmd_collapseAllThreads":
if (!gDBView || !gDBView.supportsThreading)
return false;
return (gDBView.sortType == nsMsgViewSortType.byThread);
return (gDBView.viewFlags & nsMsgViewFlagsType.kThreadedDisplay);
break;
case "cmd_nextFlaggedMsg":
case "cmd_previousFlaggedMsg":
@@ -463,7 +463,7 @@ var DefaultController =
break;
case "cmd_createFilterFromMenu":
MsgCreateFilter();
break;
break;
case "cmd_createFilterFromPopup":
break;// This does nothing because the createfilter is invoked from the popupnode oncommand.
case "button_delete":

View File

@@ -256,6 +256,13 @@ function InitViewMessagesMenu()
function InitViewMessageViewMenu()
{
var viewFlags = gDBView.viewFlags;
var viewType = gDBView.viewType;
var threadedMenuItem = document.getElementById("viewThreaded");
if (threadedMenuItem)
threadedMenuItem.setAttribute("checked", (viewFlags & nsMsgViewFlagsType.kThreadedDisplay) != 0);
var currentViewValue = document.getElementById("viewPicker").value;
var allMenuItem = document.getElementById("viewAll");

View File

@@ -1175,6 +1175,8 @@ Rights Reserved.
</menu>
<menu label="&msgsMenu.label;" id="viewMessageViewMenu" accesskey="&msgsMenu.accesskey;">
<menupopup id="viewMessageViewPopup" onpopupshowing="InitViewMessageViewMenu()">
<menuitem id="viewThreaded" type="checkbox" label="&viewThreaded.label;" accesskey="&viewThreaded.accesskey;" oncommand="MsgToggleThreaded();"/>
<menuseparator/>
<menuitem id="viewAll" type="radio" name="viewmessages" label="&viewAll.label;" accesskey="&viewAll.accesskey;" oncommand="ViewMessagesBy('viewPickerAll');"/>
<menuitem id="viewUnread" type="radio" name="viewmessages" label="&viewUnread.label;" accesskey="&viewUnread.accesskey;" oncommand="ViewMessagesBy('viewPickerUnread');"/>
<menuseparator/>

View File

@@ -122,6 +122,14 @@ function HandleColumnClick(columnID)
MsgReverseSortThreadPane();
}
else {
try {
var flatSort = pref.getBoolPref("mailnews.thread_pane_column_unthreads");
if (flatSort)
dbview.viewFlags &= ~nsMsgViewFlagsType.kThreadedDisplay;
}
catch (ex) {
}
MsgSortThreadPane(sortType);
}
}
@@ -253,6 +261,15 @@ function MsgReverseSortThreadPane()
}
}
function MsgToggleThreaded()
{
var dbview = GetDBView();
curFlags = dbview.viewFlags;
dbview.viewFlags = dbview.viewFlags ^ nsMsgViewFlagsType.kThreadedDisplay;
dbview.sort(dbview.sortType, dbview.sortOrder); // resort
UpdateSortIndicators(dbview.sortType, nsMsgViewSortOrder.ascending);
}
function MsgSortAscending()
{
var dbview = GetDBView();
@@ -271,7 +288,7 @@ function UpdateSortIndicators(sortType, sortOrder)
{
// show the twisties if the view is threaded
var currCol = document.getElementById("subjectCol");
var primary = (sortType == nsMsgViewSortType.byThread) && gDBView.supportsThreading;
var primary = (gDBView.viewFlags & nsMsgViewFlagsType.kThreadedDisplay) && gDBView.supportsThreading;
currCol.setAttribute("primary", primary);
// remove the sort indicator from all the columns
@@ -292,6 +309,17 @@ function UpdateSortIndicators(sortType, sortOrder)
else {
sortedColumn.setAttribute("sortDirection","descending");
}
if (sortedColumn != "threadCol")
{
currCol = document.getElementById("threadCol");
if (currCol)
{
if (gDBView.viewFlags & nsMsgViewFlagsType.kThreadedDisplay)
currCol.setAttribute("sortDirection", "ascending");
else
currCol.removeAttribute("sortDirection");
}
}
}
}
}

View File

@@ -178,6 +178,8 @@ Rights Reserved.
<!ENTITY sortDescending.accesskey "D">
<!ENTITY msgsMenu.label "Messages">
<!ENTITY msgsMenu.accesskey "M">
<!ENTITY viewThreaded.label "Threaded">
<!ENTITY viewThreaded.accesskey "T">
<!ENTITY viewAll.label "All">
<!ENTITY viewUnread.label "Unread">
<!ENTITY viewAll.accesskey "A">

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,7 @@
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 2001-2002
* Portions created by the Initial Developer are Copyright (C) 2001-2003
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
@@ -153,6 +153,7 @@ protected:
PRPackedBool mRemovingRow; // set when we're telling the outline a row is being removed. used to suppress msg loading.
// during delete/move operations.
PRPackedBool mCommandsNeedDisablingBecauseOffline;
PRPackedBool mSuppressChangeNotification;
virtual const char * GetViewName(void) {return "MsgDBView"; }
nsresult FetchAuthor(nsIMsgHdr * aHdr, PRUnichar ** aAuthorString);
nsresult FetchSubject(nsIMsgHdr * aMsgHdr, PRUint32 aFlags, PRUnichar ** aValue);
@@ -183,7 +184,6 @@ protected:
nsresult GetSelectedIndices(nsUInt32Array *selection);
nsresult GenerateURIForMsgKey(nsMsgKey aMsgKey, nsIMsgFolder *folder, char ** aURI);
// routines used in building up view
virtual nsresult AddKeys(nsMsgKey *pKeys, PRInt32 *pFlags, const char *pLevels, nsMsgViewSortTypeValue sortType, PRInt32 numKeysToAdd);
virtual PRBool WantsThisThread(nsIMsgThread * thread);
virtual nsresult AddHdr(nsIMsgDBHdr *msgHdr);
PRBool GetShowingIgnored() {return (m_viewFlags & nsMsgViewFlagsType::kShowIgnored) != 0;}

View File

@@ -135,10 +135,67 @@ nsresult nsMsgThreadedDBView::InitThreadedView(PRInt32 *pCount)
if (NS_SUCCEEDED(getSortrv))
{
rv = InitSort(m_sortType, m_sortOrder);
SaveSortInfo(m_sortType, m_sortOrder);
}
return rv;
}
nsresult nsMsgThreadedDBView::SortThreads(nsMsgViewSortTypeValue sortType, nsMsgViewSortOrderValue sortOrder)
{
nsresult rv = NS_OK;
if (!(m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay))
{
InitThreadedView(nsnull); // build up thread list.
m_sortType = nsMsgViewSortType::byNone; // need to pretend we're not sorted by thread so ::Sort won't expandAll
nsMsgDBView::Sort(sortType, sortOrder);
m_viewFlags |= nsMsgViewFlagsType::kThreadedDisplay;
SetViewFlags(m_viewFlags); // persist view flags
}
else
{
// 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).
nsMsgKeyArray saveKeys;
nsUInt32Array saveFlags;
saveKeys.CopyArray(m_keys);
saveFlags.CopyArray(m_flags);
m_keys.RemoveAll();
m_flags.RemoveAll();
m_levels.RemoveAll();
for (PRInt32 i = 0; i < saveKeys.GetSize(); i++)
{
if (saveFlags.GetAt(i) & MSG_VIEW_FLAG_ISTHREAD)
{
m_keys.Add(saveKeys.GetAt(i));
m_flags.Add(saveFlags.GetAt(i) | MSG_FLAG_ELIDED);
m_levels.Add(0);
}
}
m_viewFlags &= ~nsMsgViewFlagsType::kThreadedDisplay;
m_sortType = nsMsgViewSortType::byNone; // sort from scratch
nsMsgDBView::Sort(sortType, sortOrder);
m_viewFlags |= nsMsgViewFlagsType::kThreadedDisplay;
DisableChangeUpdates();
// Loop through the original array, for each thread that's expanded, find it in the new array
// and expand the thread.
for (PRInt32 j = 0; j < saveKeys.GetSize(); j++)
{
PRUint32 flags = saveFlags.GetAt(j);
if (flags & (MSG_VIEW_FLAG_ISTHREAD | MSG_FLAG_ELIDED) == MSG_VIEW_FLAG_ISTHREAD | MSG_FLAG_ELIDED)
FindKey(saveKeys.GetAt(j), PR_TRUE /* expand */);
}
EnableChangeUpdates();
}
return rv;
}
nsresult nsMsgThreadedDBView::AddKeys(nsMsgKey *pKeys, PRInt32 *pFlags, const char *pLevels, nsMsgViewSortTypeValue sortType, PRInt32 numKeysToAdd)
{
@@ -165,10 +222,6 @@ nsresult nsMsgThreadedDBView::AddKeys(nsMsgKey *pKeys, PRInt32 *pFlags, const ch
m_flags.Add(flag);
m_levels.Add(pLevels[i]);
numAdded++;
if ((/*m_viewFlags & nsMsgViewFlagsType::kUnreadOnly || */(sortType != nsMsgViewSortType::byThread)) && flag & MSG_FLAG_ELIDED)
{
ExpandByIndex(m_keys.GetSize() - 1, NULL);
}
}
return numAdded;
}
@@ -179,18 +232,22 @@ NS_IMETHODIMP nsMsgThreadedDBView::Sort(nsMsgViewSortTypeValue sortType, nsMsgVi
PRInt32 rowCountBeforeSort = GetSize();
if (!rowCountBeforeSort) {
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;
else
m_viewFlags &= ~nsMsgViewFlagsType::kThreadedDisplay;
if (sortType == nsMsgViewSortType::byThread && ! (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay))
SetViewFlags(m_viewFlags | nsMsgViewFlagsType::kThreadedDisplay);
SaveSortInfo(sortType, sortOrder);
return NS_OK;
}
PRBool sortThreads = m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay; // sort threads by sort order
// if sort type is by thread, but we're not threaded, change sort type to byId
if (sortType == nsMsgViewSortType::byThread && (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay) != 0)
sortType = nsMsgViewSortType::byId;
nsMsgKeyArray preservedSelection;
SaveAndClearSelection(&preservedSelection);
// if the client wants us to forget our cached id arrays, they
@@ -198,9 +255,9 @@ NS_IMETHODIMP nsMsgThreadedDBView::Sort(nsMsgViewSortTypeValue sortType, nsMsgVi
// need a method to do that.
if (sortType != m_sortType || !m_sortValid )
{
SaveSortInfo(sortType, sortOrder);
if (sortType == nsMsgViewSortType::byThread)
{
SaveSortInfo(sortType, sortOrder);
m_sortType = sortType;
m_viewFlags |= nsMsgViewFlagsType::kThreadedDisplay;
if ( m_havePrevView)
@@ -242,27 +299,42 @@ NS_IMETHODIMP nsMsgThreadedDBView::Sort(nsMsgViewSortTypeValue sortType, nsMsgVi
return NS_OK;
}
}
else if (sortType != nsMsgViewSortType::byThread && m_sortType == nsMsgViewSortType::byThread /* && !m_havePrevView*/)
else if (sortType != nsMsgViewSortType::byThread && (m_sortType == nsMsgViewSortType::byThread || sortThreads)/* && !m_havePrevView*/)
{
// going from SortByThread to non-thread sort - must build new key, level,and flags arrays
m_prevKeys.RemoveAll();
m_prevKeys.InsertAt(0, &m_keys);
m_prevFlags.RemoveAll();
m_prevFlags.InsertAt(0, &m_flags);
m_prevLevels.RemoveAll();
m_prevLevels.InsertAt(0, &m_levels);
// do this before we sort, so that we'll use the cheap method
// of expanding.
m_viewFlags &= ~nsMsgViewFlagsType::kThreadedDisplay;
ExpandAll();
// m_idArray.RemoveAll();
// m_flags.RemoveAll();
m_havePrevView = PR_TRUE;
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.RemoveAll();
m_prevKeys.InsertAt(0, &m_keys);
m_prevFlags.RemoveAll();
m_prevFlags.InsertAt(0, &m_flags);
m_prevLevels.RemoveAll();
m_prevLevels.InsertAt(0, &m_levels);
// do this before we sort, so that we'll use the cheap method
// of expanding.
m_viewFlags &= ~nsMsgViewFlagsType::kThreadedDisplay;
ExpandAll();
// m_idArray.RemoveAll();
// m_flags.RemoveAll();
m_havePrevView = PR_TRUE;
}
}
}
// call the base class in case we're not sorting by thread
rv = nsMsgDBView::Sort(sortType, sortOrder);
SaveSortInfo(sortType, sortOrder);
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
@@ -381,24 +453,6 @@ nsresult nsMsgThreadedDBView::ListThreadIds(nsMsgKey *startMsg, PRBool unreadOnl
return rv;
}
nsresult nsMsgThreadedDBView::ExpandAll()
{
nsresult rv = NS_OK;
// go through expanding in place
for (PRUint32 i = 0; i < m_keys.GetSize(); i++)
{
PRUint32 numExpanded;
PRUint32 flags = m_flags[i];
if (flags & MSG_VIEW_FLAG_HASCHILDREN && (flags & MSG_FLAG_ELIDED))
{
rv = ExpandByIndex(i, &numExpanded);
i += numExpanded;
NS_ENSURE_SUCCESS(rv, rv);
}
}
return rv;
}
void nsMsgThreadedDBView::OnExtraFlagChanged(nsMsgViewIndex index, PRUint32 extraFlag)
{
if (IsValidIndex(index))
@@ -454,15 +508,17 @@ nsresult nsMsgThreadedDBView::InitSort(nsMsgViewSortTypeValue sortType, nsMsgVie
nsMsgDBView::Sort(nsMsgViewSortType::byId, sortOrder); // sort top level threads by id.
m_sortType = nsMsgViewSortType::byThread;
m_viewFlags |= nsMsgViewFlagsType::kThreadedDisplay;
SetViewFlags(m_viewFlags); // persist the view flags.
// m_db->SetSortInfo(m_sortType, sortOrder);
}
else
m_viewFlags &= ~nsMsgViewFlagsType::kThreadedDisplay;
// else
// m_viewFlags &= ~nsMsgViewFlagsType::kThreadedDisplay;
// by default, the unread only view should have all threads expanded.
if ((m_viewFlags & nsMsgViewFlagsType::kUnreadOnly) && m_sortType == nsMsgViewSortType::byThread)
if ((m_viewFlags & (nsMsgViewFlagsType::kUnreadOnly|nsMsgViewFlagsType::kExpandAll))
&& (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay))
ExpandAll();
if (sortType != nsMsgViewSortType::byThread)
if (! (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay))
ExpandAll(); // for now, expand all and do a flat sort.
Sort(sortType, sortOrder);
@@ -606,6 +662,8 @@ NS_IMETHODIMP nsMsgThreadedDBView::OnParentChanged (nsMsgKey aKeyChanged, nsMsgK
nsMsgViewIndex nsMsgThreadedDBView::GetInsertInfoForNewHdr(nsIMsgDBHdr *newHdr, nsMsgViewIndex parentIndex, PRInt32 targetLevel)
{
if (targetLevel == 0) // newHdr is the top level header, so the thread index is our index.
return parentIndex;
PRInt32 viewSize = GetSize();
nsMsgViewIndex insertIndex = parentIndex + 1;
if (parentIndex != nsMsgViewIndex_None)

View File

@@ -65,12 +65,12 @@ protected:
virtual nsresult OnNewHeader(nsMsgKey newKey, nsMsgKey aParentKey, PRBool ensureListed);
virtual nsresult AddMsgToThreadNotInView(nsIMsgThread *threadHdr, nsIMsgDBHdr *msgHdr, PRBool ensureListed);
nsresult ListThreadIds(nsMsgKey *startMsg, PRBool unreadOnly, nsMsgKey *pOutput, PRInt32 *pFlags, char *pLevels,
PRInt32 numToList, PRInt32 *pNumListed, PRInt32 *pTotalHeaders);
PRInt32 numToList, PRInt32 *pNumListed, PRInt32 *pTotalHeaders);
nsresult InitSort(nsMsgViewSortTypeValue sortType, nsMsgViewSortOrderValue sortOrder);
nsresult ExpandAll();
virtual void OnExtraFlagChanged(nsMsgViewIndex index, PRUint32 extraFlag);
nsresult SortThreads(nsMsgViewSortTypeValue sortType, nsMsgViewSortOrderValue sortOrder);
virtual void OnExtraFlagChanged(nsMsgViewIndex index, PRUint32 extraFlag);
virtual void OnHeaderAddedOrDeleted();
void ClearPrevIdArray();
void ClearPrevIdArray();
virtual nsresult RemoveByIndex(nsMsgViewIndex index);
nsMsgViewIndex GetInsertInfoForNewHdr(nsIMsgDBHdr *newHdr, nsMsgViewIndex threadIndex, PRInt32 targetLevel);