From 64a573c7c81a44404ef628e382d645ef4ab33e76 Mon Sep 17 00:00:00 2001 From: "bienvenu%netscape.com" Date: Mon, 14 Jul 2003 14:17:48 +0000 Subject: [PATCH] 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 --- mozilla/mailnews/base/public/nsIMsgDBView.idl | 4 +- .../base/resources/content/commandglue.js | 10 +- .../content/mail3PaneWindowCommands.js | 4 +- .../resources/content/mailWindowOverlay.js | 7 + .../resources/content/mailWindowOverlay.xul | 2 + .../base/resources/content/threadPane.js | 30 +- .../base/resources/locale/en-US/messenger.dtd | 2 + mozilla/mailnews/base/src/nsMsgDBView.cpp | 756 ++++++++++-------- mozilla/mailnews/base/src/nsMsgDBView.h | 4 +- .../mailnews/base/src/nsMsgThreadedDBView.cpp | 158 ++-- .../mailnews/base/src/nsMsgThreadedDBView.h | 8 +- 11 files changed, 568 insertions(+), 417 deletions(-) diff --git a/mozilla/mailnews/base/public/nsIMsgDBView.idl b/mozilla/mailnews/base/public/nsIMsgDBView.idl index c64c2e1b769..81201715241 100644 --- a/mozilla/mailnews/base/public/nsIMsgDBView.idl +++ b/mozilla/mailnews/base/public/nsIMsgDBView.idl @@ -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; }; diff --git a/mozilla/mailnews/base/resources/content/commandglue.js b/mozilla/mailnews/base/resources/content/commandglue.js index 32788196c9d..6b55d187b94 100644 --- a/mozilla/mailnews/base/resources/content/commandglue.js +++ b/mozilla/mailnews/base/resources/content/commandglue.js @@ -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. diff --git a/mozilla/mailnews/base/resources/content/mail3PaneWindowCommands.js b/mozilla/mailnews/base/resources/content/mail3PaneWindowCommands.js index 922c6cec6de..27008dfb678 100644 --- a/mozilla/mailnews/base/resources/content/mail3PaneWindowCommands.js +++ b/mozilla/mailnews/base/resources/content/mail3PaneWindowCommands.js @@ -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": diff --git a/mozilla/mailnews/base/resources/content/mailWindowOverlay.js b/mozilla/mailnews/base/resources/content/mailWindowOverlay.js index cecadf04fbd..0b972a2b785 100644 --- a/mozilla/mailnews/base/resources/content/mailWindowOverlay.js +++ b/mozilla/mailnews/base/resources/content/mailWindowOverlay.js @@ -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"); diff --git a/mozilla/mailnews/base/resources/content/mailWindowOverlay.xul b/mozilla/mailnews/base/resources/content/mailWindowOverlay.xul index e6ba3096620..afe319618d0 100644 --- a/mozilla/mailnews/base/resources/content/mailWindowOverlay.xul +++ b/mozilla/mailnews/base/resources/content/mailWindowOverlay.xul @@ -1175,6 +1175,8 @@ Rights Reserved. + + diff --git a/mozilla/mailnews/base/resources/content/threadPane.js b/mozilla/mailnews/base/resources/content/threadPane.js index 8c5009e23a5..0ead6411a18 100644 --- a/mozilla/mailnews/base/resources/content/threadPane.js +++ b/mozilla/mailnews/base/resources/content/threadPane.js @@ -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"); + } + } } } } diff --git a/mozilla/mailnews/base/resources/locale/en-US/messenger.dtd b/mozilla/mailnews/base/resources/locale/en-US/messenger.dtd index 5b5d3f28463..d9ab243651b 100644 --- a/mozilla/mailnews/base/resources/locale/en-US/messenger.dtd +++ b/mozilla/mailnews/base/resources/locale/en-US/messenger.dtd @@ -178,6 +178,8 @@ Rights Reserved. + + diff --git a/mozilla/mailnews/base/src/nsMsgDBView.cpp b/mozilla/mailnews/base/src/nsMsgDBView.cpp index a593339870b..337218f0716 100644 --- a/mozilla/mailnews/base/src/nsMsgDBView.cpp +++ b/mozilla/mailnews/base/src/nsMsgDBView.cpp @@ -134,6 +134,7 @@ nsMsgDBView::nsMsgDBView() mNumSelectedRows = 0; mSuppressMsgDisplay = PR_FALSE; mSuppressCommandUpdating = PR_FALSE; + mSuppressChangeNotification = PR_FALSE; mTreatRecipientAsAuthor = PR_FALSE; mIsNews = PR_FALSE; mDeleteModel = nsMsgImapDeleteModels::MoveToTrash; @@ -1792,11 +1793,6 @@ NS_IMETHODIMP nsMsgDBView::GetSuppressMsgDisplay(PRBool * aSuppressDisplay) return NS_OK; } -nsresult nsMsgDBView::AddKeys(nsMsgKey *pKeys, PRInt32 *pFlags, const char *pLevels, nsMsgViewSortTypeValue sortType, PRInt32 numKeysToAdd) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - int PR_CALLBACK CompareViewIndices (const void *v1, const void *v2, void *) { nsMsgViewIndex i1 = *(nsMsgViewIndex*) v1; @@ -1954,11 +1950,11 @@ NS_IMETHODIMP nsMsgDBView::DoCommand(nsMsgViewCommandTypeValue command) NoteEndChange(nsMsgViewNotificationCode::none, 0, 0); break; case nsMsgViewCommandType::selectAll: - if (mTreeSelection && mTree) { + if (mTreeSelection && mTree) + { // if in threaded mode, we need to expand all before selecting - if (m_sortType == nsMsgViewSortType::byThread) { + if (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay) rv = ExpandAll(); - } mTreeSelection->SelectAll(); mTree->Invalidate(); } @@ -1980,12 +1976,16 @@ NS_IMETHODIMP nsMsgDBView::DoCommand(nsMsgViewCommandTypeValue command) break; case nsMsgViewCommandType::expandAll: rv = ExpandAll(); + m_viewFlags |= nsMsgViewFlagsType::kExpandAll; + SetViewFlags(m_viewFlags); NS_ASSERTION(mTree, "no tree, see bug #114956"); if(mTree) mTree->Invalidate(); break; case nsMsgViewCommandType::collapseAll: rv = CollapseAll(); + m_viewFlags &= ~nsMsgViewFlagsType::kExpandAll; + SetViewFlags(m_viewFlags); NS_ASSERTION(mTree, "no tree, see bug #114956"); if(mTree) mTree->Invalidate(); @@ -2491,7 +2491,7 @@ nsresult nsMsgDBView::SetReadByIndex(nsMsgViewIndex index, PRBool read) rv = dbToUse->MarkRead(m_keys[index], read, this); NoteChange(index, 1, nsMsgViewNotificationCode::changed); - if (m_sortType == nsMsgViewSortType::byThread) + if (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay) { nsMsgViewIndex threadIndex = ThreadIndexOfMsg(m_keys[index], index, nsnull, nsnull); if (threadIndex != index) @@ -2846,16 +2846,17 @@ nsMsgDBView::SaveJunkMsgForAction(nsIMsgIncomingServer *aServer, const char *aMs nsresult nsMsgDBView::ReverseThreads() { nsUInt32Array *newFlagArray = new nsUInt32Array; - if (!newFlagArray) { + if (!newFlagArray) return NS_ERROR_OUT_OF_MEMORY; - } nsMsgKeyArray *newKeyArray = new nsMsgKeyArray; - if (!newKeyArray) { + if (!newKeyArray) + { delete newFlagArray; return NS_ERROR_OUT_OF_MEMORY; } nsUint8Array *newLevelArray = new nsUint8Array; - if (!newLevelArray) { + if (!newLevelArray) + { delete newFlagArray; delete newKeyArray; return NS_ERROR_OUT_OF_MEMORY; @@ -2868,14 +2869,17 @@ nsresult nsMsgDBView::ReverseThreads() newFlagArray->SetSize(m_flags.GetSize()); newLevelArray->SetSize(m_levels.GetSize()); - for (sourceIndex = 0, destIndex = viewSize - 1; sourceIndex < viewSize;) { + for (sourceIndex = 0, destIndex = viewSize - 1; sourceIndex < viewSize;) + { PRInt32 endThread; // find end of current thread. PRBool inExpandedThread = PR_FALSE; - for (endThread = sourceIndex; endThread < viewSize; endThread++) { + for (endThread = sourceIndex; endThread < viewSize; endThread++) + { PRUint32 flags = m_flags.GetAt(endThread); if (!inExpandedThread && (flags & (MSG_VIEW_FLAG_ISTHREAD|MSG_VIEW_FLAG_HASCHILDREN)) && !(flags & MSG_FLAG_ELIDED)) inExpandedThread = PR_TRUE; - else if (flags & MSG_VIEW_FLAG_ISTHREAD) { + else if (flags & MSG_VIEW_FLAG_ISTHREAD) + { if (inExpandedThread) endThread--; break; @@ -2921,7 +2925,8 @@ nsresult nsMsgDBView::ReverseSort() GetFolders(getter_AddRefs(folders)); // go up half the array swapping values - for (PRUint32 i = 0; i < (num / 2); i++) { + for (PRUint32 i = 0; i < (num / 2); i++) + { // swap flags PRUint32 end = num - i - 1; PRUint32 tempFlags = m_flags.GetAt(i); @@ -2950,7 +2955,8 @@ nsresult nsMsgDBView::ReverseSort() return NS_OK; } -typedef struct entryInfo { +typedef struct entryInfo +{ nsMsgKey id; PRUint32 bits; PRUint32 len; @@ -2958,13 +2964,15 @@ typedef struct entryInfo { nsIMsgFolder* folder; } EntryInfo; -typedef struct tagIdKey { +typedef struct tagIdKey +{ EntryInfo info; PRUint8 key[1]; } IdKey; -typedef struct tagIdPtrKey { +typedef struct tagIdPtrKey +{ EntryInfo info; PRUint8 *key; } IdKeyPtr; @@ -3014,7 +3022,8 @@ FnSortIdKeyPtr(const void *pItem1, const void *pItem2, void *privateData) } -typedef struct tagIdDWord { +typedef struct tagIdDWord +{ EntryInfo info; PRUint32 dword; } IdDWord; @@ -3034,7 +3043,8 @@ FnSortIdDWord(const void *pItem1, const void *pItem2, void *privateData) return(-1); } -typedef struct tagIdPRTime { +typedef struct tagIdPRTime +{ EntryInfo info; PRTime prtime; } IdPRTime; @@ -3115,7 +3125,8 @@ nsresult nsMsgDBView::GetPRTimeField(nsIMsgHdr *msgHdr, nsMsgViewSortTypeValue s NS_ENSURE_ARG_POINTER(msgHdr); NS_ENSURE_ARG_POINTER(result); - switch (sortType) { + switch (sortType) + { case nsMsgViewSortType::byDate: rv = msgHdr->GetDate(result); break; @@ -3139,13 +3150,15 @@ nsresult nsMsgDBView::GetStatusSortValue(nsIMsgHdr *msgHdr, PRUint32 *result) nsresult rv = msgHdr->GetFlags(&messageFlags); NS_ENSURE_SUCCESS(rv,rv); - if (messageFlags & MSG_FLAG_NEW) { + if (messageFlags & MSG_FLAG_NEW) + { // happily, new by definition stands alone *result = 0; return NS_OK; } - switch (messageFlags & MSG_STATUS_MASK) { + switch (messageFlags & MSG_STATUS_MASK) + { case MSG_FLAG_REPLIED: *result = 2; break; @@ -3156,12 +3169,7 @@ nsresult nsMsgDBView::GetStatusSortValue(nsIMsgHdr *msgHdr, PRUint32 *result) *result = 3; break; default: - if (messageFlags & MSG_FLAG_READ) { - *result = 4; - } - else { - *result = 5; - } + *result = (messageFlags & MSG_FLAG_READ) ? 4 : 5; break; } @@ -3177,23 +3185,18 @@ nsresult nsMsgDBView::GetLongField(nsIMsgHdr *msgHdr, nsMsgViewSortTypeValue sor PRBool isRead; PRUint32 bits; - switch (sortType) { + switch (sortType) + { case nsMsgViewSortType::bySize: - if (mIsNews) { - rv = msgHdr->GetLineCount(result); - } - else { - rv = msgHdr->GetMessageSize(result); - } - break; + rv = (mIsNews) ? msgHdr->GetLineCount(result) : msgHdr->GetMessageSize(result); + break; case nsMsgViewSortType::byPriority: nsMsgPriorityValue priority; rv = msgHdr->GetPriority(&priority); // treat "none" as "normal" when sorting. - if (priority == nsMsgPriority::none) { + if (priority == nsMsgPriority::none) priority = nsMsgPriority::normal; - } // we want highest priority to have lowest value // so ascending sort will have highest priority first. @@ -3249,7 +3252,8 @@ nsMsgDBView::GetCollationKey(nsIMsgHdr *msgHdr, nsMsgViewSortTypeValue sortType, NS_ENSURE_ARG_POINTER(msgHdr); NS_ENSURE_ARG_POINTER(result); - switch (sortType) { + switch (sortType) + { case nsMsgViewSortType::bySubject: rv = msgHdr->GetSubjectCollationKey(result, len); break; @@ -3270,7 +3274,8 @@ nsMsgDBView::GetCollationKey(nsIMsgHdr *msgHdr, nsMsgViewSortTypeValue sortType, // bailing out with failure will stop the sort and leave us in // a bad state. try to continue on, instead NS_ASSERTION(NS_SUCCEEDED(rv),"failed to get the collation key"); - if (NS_FAILED(rv)) { + if (NS_FAILED(rv)) + { *result = nsnull; *len = 0; } @@ -3322,262 +3327,276 @@ nsresult nsMsgDBView::SaveSortInfo(nsMsgViewSortTypeValue sortType, nsMsgViewSor NS_IMETHODIMP nsMsgDBView::Sort(nsMsgViewSortTypeValue sortType, nsMsgViewSortOrderValue sortOrder) { - nsresult rv; - + nsresult rv; + if (m_sortType == sortType && m_sortValid) { if (m_sortOrder == sortOrder) { - // same as it ever was. do nothing - return NS_OK; - } + // same as it ever was. do nothing + return NS_OK; + } else { SaveSortInfo(sortType, sortOrder); - if (m_sortType != nsMsgViewSortType::byThread) { - rv = ReverseSort(); - NS_ENSURE_SUCCESS(rv,rv); - } - else { - rv = ReverseThreads(); - NS_ENSURE_SUCCESS(rv,rv); - } - - m_sortOrder = sortOrder; - // we just reversed the sort order...we still need to invalidate the view - return NS_OK; - } + if (! (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay)) + { + (void ) ReverseSort(); // doesn't fail. + } + else + { + rv = ReverseThreads(); + NS_ENSURE_SUCCESS(rv,rv); + } + + m_sortOrder = sortOrder; + // we just reversed the sort order...we still need to invalidate the view + return NS_OK; } - - if (sortType == nsMsgViewSortType::byThread) { - return NS_OK; + } + + if (sortType == nsMsgViewSortType::byThread) + return NS_OK; + + SaveSortInfo(sortType, sortOrder); + // figure out how much memory we'll need, and the malloc it + PRUint16 maxLen; + eFieldType fieldType; + + rv = GetFieldTypeAndLenForSort(sortType, &maxLen, &fieldType); + NS_ENSURE_SUCCESS(rv,rv); + + nsVoidArray ptrs; + PRUint32 arraySize = GetSize(); + + if (!arraySize) + return NS_OK; + + nsCOMPtr folders; + GetFolders(getter_AddRefs(folders)); + + // use IdPRTime, it is the biggest + IdPRTime** pPtrBase = (IdPRTime**)PR_Malloc(arraySize * sizeof(IdPRTime*)); + NS_ASSERTION(pPtrBase, "out of memory, can't sort"); + if (!pPtrBase) return NS_ERROR_OUT_OF_MEMORY; + ptrs.AppendElement((void *)pPtrBase); // remember this pointer so we can free it later + + // build up the beast, so we can sort it. + PRUint32 numSoFar = 0; + // calc max possible size needed for all the rest + PRUint32 maxSize = (PRUint32)(maxLen + sizeof(EntryInfo) + 1) * (PRUint32)(arraySize - numSoFar); + + PRUint32 maxBlockSize = (PRUint32) 0xf000L; + PRUint32 allocSize = PR_MIN(maxBlockSize, maxSize); + char *pTemp = (char *) PR_Malloc(allocSize); + NS_ASSERTION(pTemp, "out of memory, can't sort"); + if (!pTemp) + { + FreeAll(&ptrs); + return NS_ERROR_OUT_OF_MEMORY; + } + + ptrs.AppendElement(pTemp); // remember this pointer so we can free it later + + char *pBase = pTemp; + PRBool more = PR_TRUE; + + nsCOMPtr msgHdr; + PRUint8 *keyValue = nsnull; + PRUint32 longValue; + PRTime timeValue; + while (more && numSoFar < arraySize) + { + nsMsgKey thisKey = m_keys.GetAt(numSoFar); + if (sortType != nsMsgViewSortType::byId) + { + rv = GetMsgHdrForViewIndex(numSoFar, getter_AddRefs(msgHdr)); + NS_ASSERTION(NS_SUCCEEDED(rv) && msgHdr, "header not found"); + if (NS_FAILED(rv) || !msgHdr) + { + FreeAll(&ptrs); + return NS_ERROR_UNEXPECTED; + } + } + else + { + msgHdr = nsnull; } - - SaveSortInfo(sortType, sortOrder); - // figure out how much memory we'll need, and the malloc it - PRUint16 maxLen; - eFieldType fieldType; - - rv = GetFieldTypeAndLenForSort(sortType, &maxLen, &fieldType); - NS_ENSURE_SUCCESS(rv,rv); - - nsVoidArray ptrs; - PRUint32 arraySize = GetSize(); - - if (!arraySize) { - return NS_OK; - } /* endif */ - - nsCOMPtr folders; - GetFolders(getter_AddRefs(folders)); - - // use IdPRTime, it is the biggest - IdPRTime** pPtrBase = (IdPRTime**)PR_Malloc(arraySize * sizeof(IdPRTime*)); - NS_ASSERTION(pPtrBase, "out of memory, can't sort"); - if (!pPtrBase) return NS_ERROR_OUT_OF_MEMORY; - ptrs.AppendElement((void *)pPtrBase); // remember this pointer so we can free it later - // build up the beast, so we can sort it. - PRUint32 numSoFar = 0; - // calc max possible size needed for all the rest - PRUint32 maxSize = (PRUint32)(maxLen + sizeof(EntryInfo) + 1) * (PRUint32)(arraySize - numSoFar); - - PRUint32 maxBlockSize = (PRUint32) 0xf000L; - PRUint32 allocSize = PR_MIN(maxBlockSize, maxSize); - char *pTemp = (char *) PR_Malloc(allocSize); - NS_ASSERTION(pTemp, "out of memory, can't sort"); - if (!pTemp) { - FreeAll(&ptrs); - return NS_ERROR_OUT_OF_MEMORY; + // could be a problem here if the ones that appear here are different than the ones already in the array + void *pField = nsnull; + PRUint32 actualFieldLen = 0; + if (fieldType == kCollationKey) + { + rv = GetCollationKey(msgHdr, sortType, &keyValue, &actualFieldLen); + NS_ENSURE_SUCCESS(rv,rv); + + pField = (void *) keyValue; } - - ptrs.AppendElement(pTemp); // remember this pointer so we can free it later - - char *pBase = pTemp; - PRBool more = PR_TRUE; - - nsCOMPtr msgHdr; - PRUint8 *keyValue = nsnull; - PRUint32 longValue; - PRTime timeValue; - while (more && numSoFar < arraySize) { - nsMsgKey thisKey = m_keys.GetAt(numSoFar); - if (sortType != nsMsgViewSortType::byId) { - rv = GetMsgHdrForViewIndex(numSoFar, getter_AddRefs(msgHdr)); - NS_ASSERTION(NS_SUCCEEDED(rv) && msgHdr, "header not found"); - if (NS_FAILED(rv) || !msgHdr) { - FreeAll(&ptrs); - return NS_ERROR_UNEXPECTED; - } + else if (fieldType == kPRTime) + { + rv = GetPRTimeField(msgHdr, sortType, &timeValue); + NS_ENSURE_SUCCESS(rv,rv); + + pField = (void *) &timeValue; + actualFieldLen = maxLen; + } + else + { + if (sortType == nsMsgViewSortType::byId) + { + longValue = thisKey; } - else { - msgHdr = nsnull; - } - - // could be a problem here if the ones that appear here are different than the ones already in the array - void *pField = nsnull; - PRUint32 actualFieldLen = 0; - if (fieldType == kCollationKey) { - rv = GetCollationKey(msgHdr, sortType, &keyValue, &actualFieldLen); + else + { + rv = GetLongField(msgHdr, sortType, &longValue); NS_ENSURE_SUCCESS(rv,rv); - - pField = (void *) keyValue; } - else if (fieldType == kPRTime) { - rv = GetPRTimeField(msgHdr, sortType, &timeValue); - NS_ENSURE_SUCCESS(rv,rv); - - pField = (void *) &timeValue; - actualFieldLen = maxLen; - } - else { - if (sortType == nsMsgViewSortType::byId) { - longValue = thisKey; - } - else { - rv = GetLongField(msgHdr, sortType, &longValue); - NS_ENSURE_SUCCESS(rv,rv); - } - pField = (void *)&longValue; - actualFieldLen = maxLen; - } - - // check to see if this entry fits into the block we have allocated so far - // pTemp - pBase = the space we have used so far - // sizeof(EntryInfo) + fieldLen = space we need for this entry - // allocSize = size of the current block - if ((PRUint32)(pTemp - pBase) + (PRUint32)sizeof(EntryInfo) + (PRUint32)actualFieldLen >= allocSize) { - maxSize = (PRUint32)(maxLen + sizeof(EntryInfo) + 1) * (PRUint32)(arraySize - numSoFar); - maxBlockSize = (PRUint32) 0xf000L; - allocSize = PR_MIN(maxBlockSize, maxSize); - pTemp = (char *) PR_Malloc(allocSize); - NS_ASSERTION(pTemp, "out of memory, can't sort"); - if (!pTemp) { - FreeAll(&ptrs); - return NS_ERROR_OUT_OF_MEMORY; - } - pBase = pTemp; - ptrs.AppendElement(pTemp); // remember this pointer so we can free it later - } - - // make sure there aren't more IDs than we allocated space for - NS_ASSERTION(numSoFar < arraySize, "out of memory"); - if (numSoFar >= arraySize) { + pField = (void *)&longValue; + actualFieldLen = maxLen; + } + + // check to see if this entry fits into the block we have allocated so far + // pTemp - pBase = the space we have used so far + // sizeof(EntryInfo) + fieldLen = space we need for this entry + // allocSize = size of the current block + if ((PRUint32)(pTemp - pBase) + (PRUint32)sizeof(EntryInfo) + (PRUint32)actualFieldLen >= allocSize) { + maxSize = (PRUint32)(maxLen + sizeof(EntryInfo) + 1) * (PRUint32)(arraySize - numSoFar); + maxBlockSize = (PRUint32) 0xf000L; + allocSize = PR_MIN(maxBlockSize, maxSize); + pTemp = (char *) PR_Malloc(allocSize); + NS_ASSERTION(pTemp, "out of memory, can't sort"); + if (!pTemp) + { FreeAll(&ptrs); return NS_ERROR_OUT_OF_MEMORY; } - - // now store this entry away in the allocated memory - pPtrBase[numSoFar] = (IdPRTime*)pTemp; - EntryInfo *info = (EntryInfo*)pTemp; - info->id = thisKey; - info->bits = m_flags.GetAt(numSoFar); - info->len = actualFieldLen; - //info->pad = 0; - + pBase = pTemp; + ptrs.AppendElement(pTemp); // remember this pointer so we can free it later + } + + // make sure there aren't more IDs than we allocated space for + NS_ASSERTION(numSoFar < arraySize, "out of memory"); + if (numSoFar >= arraySize) + { + FreeAll(&ptrs); + return NS_ERROR_OUT_OF_MEMORY; + } + + // now store this entry away in the allocated memory + pPtrBase[numSoFar] = (IdPRTime*)pTemp; + EntryInfo *info = (EntryInfo*)pTemp; + info->id = thisKey; + info->bits = m_flags.GetAt(numSoFar); + info->len = actualFieldLen; + //info->pad = 0; + if (folders) { - nsCOMPtr curFolder = do_QueryElementAt(folders, numSoFar);; - if(curFolder) { - info->folder = curFolder; - } + nsCOMPtr curFolder = do_QueryElementAt(folders, numSoFar);; + if(curFolder) + info->folder = curFolder; } - - pTemp += sizeof(EntryInfo); - - PRInt32 bytesLeft = allocSize - (PRInt32)(pTemp - pBase); - PRInt32 bytesToCopy = PR_MIN(bytesLeft, (PRInt32)actualFieldLen); - if (pField && bytesToCopy > 0) { - memcpy((void *)pTemp, pField, bytesToCopy); - if (bytesToCopy < (PRInt32)actualFieldLen) { - NS_ASSERTION(0, "wow, big block"); - info->len = bytesToCopy; - } + + pTemp += sizeof(EntryInfo); + + PRInt32 bytesLeft = allocSize - (PRInt32)(pTemp - pBase); + PRInt32 bytesToCopy = PR_MIN(bytesLeft, (PRInt32)actualFieldLen); + if (pField && bytesToCopy > 0) + { + memcpy((void *)pTemp, pField, bytesToCopy); + if (bytesToCopy < (PRInt32)actualFieldLen) + { + NS_ASSERTION(0, "wow, big block"); + info->len = bytesToCopy; } - else { - *pTemp = 0; - } - //In order to align memory for systems that require it, such as HP-UX - //calculate the correct value to pad the bytesToCopy value - PRInt32 bytesToPad = sizeof(PRInt32) - (bytesToCopy & 0x00000003); - - //if bytesToPad is not 4 then alignment is needed so add the padding - //otherwise memory is already aligned - no need to add padding - if (bytesToPad != sizeof(PRInt32)) { - //Add the necessary padding to bytesToCopy - bytesToCopy += bytesToPad; - } - - pTemp += bytesToCopy; - ++numSoFar; - PR_FREEIF(keyValue); } - - // do the sort - switch (fieldType) { - case kCollationKey: - { - - nsCOMPtr dbToUse = m_db; - - if (!dbToUse) // probably search view - GetDBForViewIndex(0, getter_AddRefs(dbToUse)); - if (dbToUse) - NS_QuickSort(pPtrBase, numSoFar, sizeof(IdKey*), FnSortIdKey, dbToUse); - } - break; - case kU32: - NS_QuickSort(pPtrBase, numSoFar, sizeof(IdDWord*), FnSortIdDWord, nsnull); - break; - case kPRTime: - NS_QuickSort(pPtrBase, numSoFar, sizeof(IdPRTime*), FnSortIdPRTime, nsnull); - break; - default: - NS_ASSERTION(0, "not supposed to get here"); - break; + else + { + *pTemp = 0; } - - // now put the IDs into the array in proper order - for (PRUint32 i = 0; i < numSoFar; i++) { - m_keys.SetAt(i, pPtrBase[i]->info.id); - m_flags.SetAt(i, pPtrBase[i]->info.bits); - - if (folders) - { - nsCOMPtr tmpSupports - = do_QueryInterface(pPtrBase[i]->info.folder); - folders->SetElementAt(i, tmpSupports); - } + //In order to align memory for systems that require it, such as HP-UX + //calculate the correct value to pad the bytesToCopy value + PRInt32 bytesToPad = sizeof(PRInt32) - (bytesToCopy & 0x00000003); + + //if bytesToPad is not 4 then alignment is needed so add the padding + //otherwise memory is already aligned - no need to add padding + //Add the necessary padding to bytesToCopy + if (bytesToPad != sizeof(PRInt32)) + bytesToCopy += bytesToPad; + + pTemp += bytesToCopy; + ++numSoFar; + PR_Free(keyValue); + } + + // do the sort + switch (fieldType) + { + case kCollationKey: + { + + nsCOMPtr dbToUse = m_db; + + if (!dbToUse) // probably search view + GetDBForViewIndex(0, getter_AddRefs(dbToUse)); + if (dbToUse) + NS_QuickSort(pPtrBase, numSoFar, sizeof(IdKey*), FnSortIdKey, dbToUse); } - - m_sortType = sortType; - m_sortOrder = sortOrder; - - if (sortOrder == nsMsgViewSortOrder::descending) { - rv = ReverseSort(); - NS_ASSERTION(NS_SUCCEEDED(rv),"failed to reverse sort"); + break; + case kU32: + NS_QuickSort(pPtrBase, numSoFar, sizeof(IdDWord*), FnSortIdDWord, nsnull); + break; + case kPRTime: + NS_QuickSort(pPtrBase, numSoFar, sizeof(IdPRTime*), FnSortIdPRTime, nsnull); + break; + default: + NS_ASSERTION(0, "not supposed to get here"); + break; + } + + // now put the IDs into the array in proper order + for (PRUint32 i = 0; i < numSoFar; i++) + { + m_keys.SetAt(i, pPtrBase[i]->info.id); + m_flags.SetAt(i, pPtrBase[i]->info.bits); + + if (folders) + { + nsCOMPtr tmpSupports + = do_QueryInterface(pPtrBase[i]->info.folder); + folders->SetElementAt(i, tmpSupports); } - - // free all the memory we allocated - FreeAll(&ptrs); - - m_sortValid = PR_TRUE; - //m_db->SetSortInfo(sortType, sortOrder); - - return NS_OK; + } + + m_sortType = sortType; + m_sortOrder = sortOrder; + + if (sortOrder == nsMsgViewSortOrder::descending) + { + rv = ReverseSort(); + NS_ASSERTION(NS_SUCCEEDED(rv),"failed to reverse sort"); + } + + // free all the memory we allocated + FreeAll(&ptrs); + + m_sortValid = PR_TRUE; + //m_db->SetSortInfo(sortType, sortOrder); + + return NS_OK; } void nsMsgDBView::FreeAll(nsVoidArray *ptrs) { - PRInt32 i; - PRInt32 count = (PRInt32) ptrs->Count(); - if (count == 0) return; + PRInt32 i; + PRInt32 count = (PRInt32) ptrs->Count(); + if (count == 0) + return; - for (i=(count - 1);i>=0;i--) { - void *ptr = (void *) ptrs->ElementAt(i); - PR_FREEIF(ptr); - } - ptrs->Clear(); + for (i=(count - 1);i>=0;i--) + PR_Free((void *) ptrs->ElementAt(i)); + ptrs->Clear(); } nsMsgViewIndex nsMsgDBView::GetIndexOfFirstDisplayedKeyInThread(nsIMsgThread *threadHdr) @@ -3620,7 +3639,7 @@ nsMsgViewIndex nsMsgDBView::ThreadIndexOfMsg(nsMsgKey msgKey, PRInt32 *pThreadCount /* = NULL */, PRUint32 *pFlags /* = NULL */) { - if (m_sortType != nsMsgViewSortType::byThread) + if (! (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay)) return nsMsgViewIndex_None; nsCOMPtr threadHdr; nsCOMPtr msgHdr; @@ -3667,17 +3686,16 @@ nsMsgKey nsMsgDBView::GetKeyOfFirstMsgInThread(nsMsgKey key) { nsCOMPtr pThread; nsCOMPtr msgHdr; - nsresult rv = m_db->GetMsgHdrForKey(key, getter_AddRefs(msgHdr)); - NS_ENSURE_SUCCESS(rv, rv); - rv = m_db->GetThreadContainingMsgHdr(msgHdr, getter_AddRefs(pThread)); - NS_ENSURE_SUCCESS(rv, rv); + nsresult rv = m_db->GetMsgHdrForKey(key, getter_AddRefs(msgHdr)); + NS_ENSURE_SUCCESS(rv, rv); + rv = m_db->GetThreadContainingMsgHdr(msgHdr, getter_AddRefs(pThread)); + NS_ENSURE_SUCCESS(rv, rv); nsMsgKey firstKeyInThread = nsMsgKey_None; NS_ASSERTION(pThread, "error getting msg from thread"); if (!pThread) - { return firstKeyInThread; - } + // ### dmb UnreadOnly - this is wrong. But didn't seem to matter in 4.x pThread->GetChildKeyAt(0, &firstKeyInThread); return firstKeyInThread; @@ -3763,7 +3781,7 @@ nsresult nsMsgDBView::ExpansionDelta(nsMsgViewIndex index, PRInt32 *expansionDel return NS_MSG_MESSAGE_NOT_FOUND; char flags = m_flags[index]; - if (m_sortType != nsMsgViewSortType::byThread) + if (!(m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay)) return NS_OK; // The client can pass in the key of any message @@ -3966,7 +3984,7 @@ nsresult nsMsgDBView::CollapseByIndex(nsMsgViewIndex index, PRUint32 *pNumCollap PRInt32 flags = m_flags[index]; PRInt32 threadCount = 0; - if (flags & MSG_FLAG_ELIDED || m_sortType != nsMsgViewSortType::byThread || !(flags & MSG_VIEW_FLAG_HASCHILDREN)) + if (flags & MSG_FLAG_ELIDED || !(m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay) || !(flags & MSG_VIEW_FLAG_HASCHILDREN)) return NS_OK; flags |= MSG_FLAG_ELIDED; @@ -4231,8 +4249,8 @@ nsMsgViewIndex nsMsgDBView::GetInsertIndex(nsIMsgDBHdr *msgHdr) else if (retStatus < 0) retIndex = tryIndex; - PR_FREEIF(keyInfo1.key); - PR_FREEIF(keyInfo2.key); + PR_Free(keyInfo1.key); + PR_Free(keyInfo2.key); return retIndex; } @@ -4611,14 +4629,18 @@ void nsMsgDBView::ClearHdrCache() void nsMsgDBView::EnableChangeUpdates() { + mSuppressChangeNotification = PR_FALSE; } + void nsMsgDBView::DisableChangeUpdates() { + mSuppressChangeNotification = PR_TRUE; } + void nsMsgDBView::NoteChange(nsMsgViewIndex firstLineChanged, PRInt32 numChanged, nsMsgViewNotificationCodeValue changeType) { - if (mTree) + if (mTree && !mSuppressChangeNotification) { switch (changeType) { @@ -4679,7 +4701,20 @@ NS_IMETHODIMP nsMsgDBView::GetViewFlags(nsMsgViewFlagsTypeValue *aViewFlags) NS_IMETHODIMP nsMsgDBView::SetViewFlags(nsMsgViewFlagsTypeValue aViewFlags) { - m_viewFlags = aViewFlags; + // if we're turning off threaded display, we need to expand all so that all + // messages will be displayed. + if (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay && ! (aViewFlags & nsMsgViewFlagsType::kThreadedDisplay)) + ExpandAll(); + m_viewFlags = aViewFlags; + + if (m_folder) + { + nsCOMPtr folderInfo; + nsresult rv = m_folder->GetDBFolderInfoAndDB(getter_AddRefs(folderInfo), getter_AddRefs(m_db)); + NS_ENSURE_SUCCESS(rv,rv); + return folderInfo->SetViewFlags(aViewFlags); + } + else return NS_OK; } @@ -4692,9 +4727,9 @@ nsresult nsMsgDBView::MarkThreadOfMsgRead(nsMsgKey msgId, nsMsgViewIndex msgInde nsMsgViewIndex threadIndex; NS_ASSERTION(threadHdr, "threadHdr is null"); - if (!threadHdr) { + if (!threadHdr) return NS_MSG_MESSAGE_NOT_FOUND; - } + nsCOMPtr firstHdr; threadHdr->GetChildAt(0, getter_AddRefs(firstHdr)); nsMsgKey firstHdrId; @@ -4703,8 +4738,7 @@ nsresult nsMsgDBView::MarkThreadOfMsgRead(nsMsgKey msgId, nsMsgViewIndex msgInde threadIndex = GetIndexOfFirstDisplayedKeyInThread(threadHdr); else threadIndex = msgIndex; - rv = MarkThreadRead(threadHdr, threadIndex, idsMarkedRead, bRead); - return rv; + return MarkThreadRead(threadHdr, threadIndex, idsMarkedRead, bRead); } nsresult nsMsgDBView::MarkThreadRead(nsIMsgThread *threadHdr, nsMsgViewIndex threadIndex, nsMsgKeyArray &idsMarkedRead, PRBool bRead) @@ -4715,13 +4749,13 @@ nsresult nsMsgDBView::MarkThreadRead(nsIMsgThread *threadHdr, nsMsgViewIndex thr PRUint32 numChildren; threadHdr->GetNumChildren(&numChildren); - for (PRInt32 childIndex = 0; childIndex < (PRInt32) numChildren ; childIndex++) { + for (PRInt32 childIndex = 0; childIndex < (PRInt32) numChildren ; childIndex++) + { nsCOMPtr msgHdr; threadHdr->GetChildHdrAt(childIndex, getter_AddRefs(msgHdr)); NS_ASSERTION(msgHdr, "msgHdr is null"); - if (!msgHdr) { + if (!msgHdr) continue; - } PRBool isRead; @@ -4729,7 +4763,8 @@ nsresult nsMsgDBView::MarkThreadRead(nsIMsgThread *threadHdr, nsMsgViewIndex thr msgHdr->GetMessageKey(&hdrMsgId); m_db->IsRead(hdrMsgId, &isRead); - if (isRead != bRead) { + if (isRead != bRead) + { // MarkHdrRead will change the unread count on the thread m_db->MarkHdrRead(msgHdr, bRead, nsnull); // insert at the front. should we insert at the end? @@ -4793,13 +4828,15 @@ nsresult nsMsgDBView::NavigateFromPos(nsMsgNavigationTypeValue motion, nsMsgView nsMsgViewIndex threadIndex = nsMsgViewIndex_None; // if there aren't any messages in the view, bail out. - if (GetSize() <= 0) { + if (GetSize() <= 0) + { *pResultIndex = nsMsgViewIndex_None; *pResultKey = nsMsgKey_None; return NS_OK; } - switch (motion) { + switch (motion) + { case nsMsgNavigationType::firstMessage: *pResultIndex = 0; *pResultKey = m_keys.GetAt(0); @@ -4844,24 +4881,25 @@ nsresult nsMsgDBView::NavigateFromPos(nsMsgNavigationTypeValue motion, nsMsgView PRUint32 flags = m_flags.GetAt(curIndex); // don't return start index since navigate should move - if (!(flags & MSG_FLAG_READ) && (curIndex != startIndex)) { + if (!(flags & MSG_FLAG_READ) && (curIndex != startIndex)) + { *pResultIndex = curIndex; *pResultKey = m_keys.GetAt(*pResultIndex); break; } // check for collapsed thread with new children - if (m_sortType == nsMsgViewSortType::byThread && flags & MSG_VIEW_FLAG_ISTHREAD && flags & MSG_FLAG_ELIDED) { + if ((m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay) && flags & MSG_VIEW_FLAG_ISTHREAD && flags & MSG_FLAG_ELIDED) { nsCOMPtr threadHdr; GetThreadContainingIndex(curIndex, getter_AddRefs(threadHdr)); NS_ENSURE_SUCCESS(rv, rv); NS_ASSERTION(threadHdr, "threadHdr is null"); - if (!threadHdr) { + if (!threadHdr) continue; - } PRUint32 numUnreadChildren; threadHdr->GetNumUnreadChildren(&numUnreadChildren); - if (numUnreadChildren > 0) { + if (numUnreadChildren > 0) + { PRUint32 numExpanded; ExpandByIndex(curIndex, &numExpanded); lastIndex += numExpanded; @@ -4870,36 +4908,42 @@ nsresult nsMsgDBView::NavigateFromPos(nsMsgNavigationTypeValue motion, nsMsgView } } } - if (curIndex > lastIndex) { + if (curIndex > lastIndex) + { // wrap around by starting at index 0. - if (wrap) { + if (wrap) + { nsMsgKey startKey = GetAt(startIndex); rv = NavigateFromPos(nsMsgNavigationType::nextUnreadMessage, nsMsgViewIndex_None, pResultKey, pResultIndex, pThreadIndex, PR_FALSE); - if (*pResultKey == startKey) { + if (*pResultKey == startKey) + { // wrapped around and found start message! *pResultIndex = nsMsgViewIndex_None; *pResultKey = nsMsgKey_None; } } - else { + else + { *pResultIndex = nsMsgViewIndex_None; *pResultKey = nsMsgKey_None; } } break; case nsMsgNavigationType::previousUnreadMessage: - if (startIndex == nsMsgViewIndex_None) { + if (startIndex == nsMsgViewIndex_None) break; - } rv = FindPrevUnread(m_keys.GetAt(startIndex), pResultKey, &resultThreadKey); - if (NS_SUCCEEDED(rv)) { + if (NS_SUCCEEDED(rv)) + { *pResultIndex = FindViewIndex(*pResultKey); - if (*pResultKey != resultThreadKey && m_sortType == nsMsgViewSortType::byThread) { + if (*pResultKey != resultThreadKey && (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay)) + { threadIndex = ThreadIndexOfMsg(*pResultKey, nsMsgViewIndex_None); - if (*pResultIndex == nsMsgViewIndex_None) { + if (*pResultIndex == nsMsgViewIndex_None) + { nsCOMPtr threadHdr; nsCOMPtr msgHdr; rv = m_db->GetMsgHdrForKey(*pResultKey, getter_AddRefs(msgHdr)); @@ -4908,12 +4952,12 @@ nsresult nsMsgDBView::NavigateFromPos(nsMsgNavigationTypeValue motion, nsMsgView NS_ENSURE_SUCCESS(rv, rv); NS_ASSERTION(threadHdr, "threadHdr is null"); - if (threadHdr) { + if (threadHdr) break; - } PRUint32 numUnreadChildren; threadHdr->GetNumUnreadChildren(&numUnreadChildren); - if (numUnreadChildren > 0) { + if (numUnreadChildren > 0) + { PRUint32 numExpanded; ExpandByIndex(threadIndex, &numExpanded); } @@ -4930,7 +4974,8 @@ nsresult nsMsgDBView::NavigateFromPos(nsMsgNavigationTypeValue motion, nsMsgView { nsMsgKeyArray idsMarkedRead; - if (startIndex == nsMsgViewIndex_None) { + if (startIndex == nsMsgViewIndex_None) + { NS_ASSERTION(0,"startIndex == nsMsgViewIndex_None"); break; } @@ -4943,25 +4988,29 @@ nsresult nsMsgDBView::NavigateFromPos(nsMsgNavigationTypeValue motion, nsMsgView { PRBool resultKilled; - if (startIndex == nsMsgViewIndex_None) { + if (startIndex == nsMsgViewIndex_None) + { NS_ASSERTION(0,"startIndex == nsMsgViewIndex_None"); break; } threadIndex = ThreadIndexOfMsg(GetAt(startIndex), startIndex); ToggleIgnored(&startIndex, 1, &resultKilled); - if (resultKilled) { + if (resultKilled) + { if (threadIndex != nsMsgViewIndex_None) CollapseByIndex(threadIndex, nsnull); return NavigateFromPos(nsMsgNavigationType::nextUnreadThread, threadIndex, pResultKey, pResultIndex, pThreadIndex, PR_TRUE); } - else { + else + { *pResultIndex = startIndex; *pResultKey = m_keys.GetAt(*pResultIndex); return NS_OK; } } case nsMsgNavigationType::laterMessage: - if (startIndex == nsMsgViewIndex_None) { + if (startIndex == nsMsgViewIndex_None) + { NS_ASSERTION(0, "unexpected"); break; } @@ -4988,7 +5037,8 @@ NS_IMETHODIMP nsMsgDBView::NavigateStatus(nsMsgNavigationTypeValue motion, PRBoo // warning - we no longer validate index up front because fe passes in -1 for no // selection, so if you use index, be sure to validate it before using it // as an array index. - switch (motion) { + switch (motion) + { case nsMsgNavigationType::firstMessage: case nsMsgNavigationType::lastMessage: if (GetSize() > 0) @@ -5032,7 +5082,8 @@ NS_IMETHODIMP nsMsgDBView::NavigateStatus(nsMsgNavigationTypeValue motion, PRBoo enable = PR_TRUE; // always enabled break; case nsMsgNavigationType::previousUnreadMessage: - if (IsValidIndex(index)) { + if (IsValidIndex(index)) + { nsMsgKey threadId; rv = FindPrevUnread(m_keys.GetAt(index), &resultKey, &threadId); enable = (resultKey != nsMsgKey_None); @@ -5060,10 +5111,13 @@ nsresult nsMsgDBView::FindNextFlagged(nsMsgViewIndex startIndex, nsMsgViewIndex *pResultIndex = nsMsgViewIndex_None; - if (GetSize() > 0) { - for (curIndex = startIndex; curIndex <= lastIndex; curIndex++) { + if (GetSize() > 0) + { + for (curIndex = startIndex; curIndex <= lastIndex; curIndex++) + { PRUint32 flags = m_flags.GetAt(curIndex); - if (flags & MSG_FLAG_MARKED) { + if (flags & MSG_FLAG_MARKED) + { *pResultIndex = curIndex; break; } @@ -5075,7 +5129,8 @@ nsresult nsMsgDBView::FindNextFlagged(nsMsgViewIndex startIndex, nsMsgViewIndex nsresult nsMsgDBView::FindFirstNew(nsMsgViewIndex *pResultIndex) { - if (m_db) { + if (m_db) + { nsMsgKey firstNewKey; m_db->GetFirstNew(&firstNewKey); if (pResultIndex) @@ -5101,19 +5156,23 @@ nsresult nsMsgDBView::FindNextUnread(nsMsgKey startId, nsMsgKey *pResultKey, if (resultThreadId) *resultThreadId = nsMsgKey_None; - for (; curIndex <= lastIndex && (*pResultKey == nsMsgKey_None); curIndex++) { + for (; curIndex <= lastIndex && (*pResultKey == nsMsgKey_None); curIndex++) + { char flags = m_flags.GetAt(curIndex); - if (!(flags & MSG_FLAG_READ) && (curIndex != startIndex)) { + if (!(flags & MSG_FLAG_READ) && (curIndex != startIndex)) + { *pResultKey = m_keys.GetAt(curIndex); break; } // check for collapsed thread with unread children - if (m_sortType == nsMsgViewSortType::byThread && flags & MSG_VIEW_FLAG_ISTHREAD && flags & MSG_FLAG_ELIDED) { + if ((m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay) && flags & MSG_VIEW_FLAG_ISTHREAD && flags & MSG_FLAG_ELIDED) + { nsCOMPtr thread; //nsMsgKey threadId = m_keys.GetAt(curIndex); rv = GetThreadFromMsgIndex(curIndex, getter_AddRefs(thread)); - if (NS_SUCCEEDED(rv) && thread) { + if (NS_SUCCEEDED(rv) && thread) + { nsCOMPtr unreadChild; rv = thread->GetFirstUnreadChild(getter_AddRefs(unreadChild)); if (NS_SUCCEEDED(rv) && unreadChild) @@ -5125,10 +5184,8 @@ nsresult nsMsgDBView::FindNextUnread(nsMsgKey startId, nsMsgKey *pResultKey, } } // found unread message but we don't know the thread - if (*pResultKey != nsMsgKey_None && resultThreadId && *resultThreadId == nsMsgKey_None) { - NS_ASSERTION(0,"fix this"); - //*resultThreadId = m_db->GetThreadIdForMsgId(*pResultKey); - } + NS_ASSERTION(!(*pResultKey != nsMsgKey_None && resultThreadId && *resultThreadId == nsMsgKey_None), + "fix this"); return rv; } @@ -5147,27 +5204,28 @@ nsresult nsMsgDBView::FindPrevUnread(nsMsgKey startKey, nsMsgKey *pResultKey, if (resultThreadId) *resultThreadId = nsMsgKey_None; - for (; (int) curIndex >= 0 && (*pResultKey == nsMsgKey_None); curIndex--) { + for (; (int) curIndex >= 0 && (*pResultKey == nsMsgKey_None); curIndex--) + { PRUint32 flags = m_flags.GetAt(curIndex); - if (curIndex != startIndex && flags & MSG_VIEW_FLAG_ISTHREAD && flags & MSG_FLAG_ELIDED) { + if (curIndex != startIndex && flags & MSG_VIEW_FLAG_ISTHREAD && flags & MSG_FLAG_ELIDED) + { NS_ASSERTION(0,"fix this"); //nsMsgKey threadId = m_keys.GetAt(curIndex); //rv = m_db->GetUnreadKeyInThread(threadId, pResultKey, resultThreadId); if (NS_SUCCEEDED(rv) && (*pResultKey != nsMsgKey_None)) break; } - if (!(flags & MSG_FLAG_READ) && (curIndex != startIndex)) { + if (!(flags & MSG_FLAG_READ) && (curIndex != startIndex)) + { *pResultKey = m_keys.GetAt(curIndex); rv = NS_OK; break; } } // found unread message but we don't know the thread - if (*pResultKey != nsMsgKey_None && resultThreadId && *resultThreadId == nsMsgKey_None) { - NS_ASSERTION(0,"fix this"); - //*resultThreadId = m_db->GetThreadIdForMsgId(*pResultKey); - } + NS_ASSERTION(!(*pResultKey != nsMsgKey_None && resultThreadId && *resultThreadId == nsMsgKey_None), + "fix this"); return rv; } @@ -5182,14 +5240,17 @@ nsresult nsMsgDBView::FindPrevFlagged(nsMsgViewIndex startIndex, nsMsgViewIndex *pResultIndex = nsMsgViewIndex_None; - if (GetSize() > 0 && IsValidIndex(startIndex)) { + if (GetSize() > 0 && IsValidIndex(startIndex)) + { curIndex = startIndex; - do { + do + { if (curIndex != 0) curIndex--; PRUint32 flags = m_flags.GetAt(curIndex); - if (flags & MSG_FLAG_MARKED) { + if (flags & MSG_FLAG_MARKED) + { *pResultIndex = curIndex; break; } @@ -5326,9 +5387,7 @@ nsresult nsMsgDBView::ToggleWatched( nsMsgViewIndex* indices, PRInt32 numIndices { nsMsgViewIndex threadIndex = GetThreadFromMsgIndex(*indices, getter_AddRefs(thread)); if (threadIndex != nsMsgViewIndex_None) - { rv = ToggleThreadWatched(thread, threadIndex); - } } else { @@ -5444,7 +5503,8 @@ nsMsgDBView::GetRemoveRowOnMoveOrDelete(PRBool *aRemoveRowOnMoveOrDelete) { NS_ENSURE_ARG_POINTER(aRemoveRowOnMoveOrDelete); nsCOMPtr imapFolder = do_QueryInterface(m_folder); - if (!imapFolder) { + if (!imapFolder) + { *aRemoveRowOnMoveOrDelete = PR_TRUE; return NS_OK; } @@ -5567,15 +5627,14 @@ nsMsgDBView::GetKeyForFirstSelectedMessage(nsMsgKey *key) PRInt32 endRange; nsresult rv = mTreeSelection->GetRangeAt(0, &startRange, &endRange); // don't assert, it is legal for nothing to be selected - if (NS_FAILED(rv)) return rv; + if (NS_FAILED(rv)) + return rv; // check that the first index is valid, it may not be if nothing is selected - if (startRange >= 0 && startRange < GetSize()) { + if (startRange >= 0 && startRange < GetSize()) *key = m_keys.GetAt(startRange); - } - else { + else return NS_ERROR_UNEXPECTED; - } return NS_OK; } @@ -5589,9 +5648,10 @@ nsresult nsMsgDBView::GetFolders(nsISupportsArray **aFolders) nsresult nsMsgDBView::AdjustRowCount(PRInt32 rowCountBeforeSort, PRInt32 rowCountAfterSort) { - PRInt32 rowChange = rowCountBeforeSort - rowCountAfterSort; + PRInt32 rowChange = rowCountAfterSort - rowCountBeforeSort; - if (rowChange) { + if (rowChange) + { // this is not safe to use when you have a selection // RowCountChanged() will call AdjustSelection() PRUint32 numSelected = 0; diff --git a/mozilla/mailnews/base/src/nsMsgDBView.h b/mozilla/mailnews/base/src/nsMsgDBView.h index 1ccf1d52af5..fc06a28035c 100644 --- a/mozilla/mailnews/base/src/nsMsgDBView.h +++ b/mozilla/mailnews/base/src/nsMsgDBView.h @@ -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;} diff --git a/mozilla/mailnews/base/src/nsMsgThreadedDBView.cpp b/mozilla/mailnews/base/src/nsMsgThreadedDBView.cpp index c03eb28397a..0f73496fbfb 100644 --- a/mozilla/mailnews/base/src/nsMsgThreadedDBView.cpp +++ b/mozilla/mailnews/base/src/nsMsgThreadedDBView.cpp @@ -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) diff --git a/mozilla/mailnews/base/src/nsMsgThreadedDBView.h b/mozilla/mailnews/base/src/nsMsgThreadedDBView.h index 4a9ea11f33e..6b3547000af 100644 --- a/mozilla/mailnews/base/src/nsMsgThreadedDBView.h +++ b/mozilla/mailnews/base/src/nsMsgThreadedDBView.h @@ -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);