fix 262319, new msg arriving in thread doesn't resort, when sorted by date, r=standard8, sr=neil
This commit is contained in:
Родитель
abe8f65901
Коммит
1ce2bd7835
|
@ -3225,7 +3225,7 @@ struct IdDWord
|
|||
nsMsgKey id;
|
||||
PRUint32 bits;
|
||||
PRUint32 dword;
|
||||
nsISupports* folder;
|
||||
nsIMsgFolder* folder;
|
||||
};
|
||||
|
||||
struct IdKey : public IdDWord
|
||||
|
@ -3959,8 +3959,7 @@ NS_IMETHODIMP nsMsgDBView::Sort(nsMsgViewSortTypeValue sortType, nsMsgViewSortOr
|
|||
|
||||
if (folders)
|
||||
{
|
||||
nsCOMPtr<nsISupports> curFolder;
|
||||
folders->GetElementAt(numSoFar, getter_AddRefs(curFolder));
|
||||
nsCOMPtr<nsIMsgFolder> curFolder = do_QueryElementAt(folders, numSoFar);
|
||||
info->folder = curFolder;
|
||||
}
|
||||
else
|
||||
|
@ -4487,61 +4486,122 @@ nsresult nsMsgDBView::GetThreadContainingIndex(nsMsgViewIndex index, nsIMsgThrea
|
|||
return GetThreadContainingMsgHdr(msgHdr, resultThread);
|
||||
}
|
||||
|
||||
nsMsgViewIndex nsMsgDBView::GetIndexForThread(nsIMsgDBHdr *hdr)
|
||||
nsMsgViewIndex
|
||||
nsMsgDBView::GetIndexForThread(nsIMsgDBHdr *msgHdr)
|
||||
{
|
||||
nsMsgViewIndex retIndex = nsMsgViewIndex_None;
|
||||
nsMsgViewIndex prevInsertIndex = nsMsgViewIndex_None;
|
||||
nsMsgKey insertKey;
|
||||
hdr->GetMessageKey(&insertKey);
|
||||
// Take advantage of the fact that we're already sorted
|
||||
// and find the insert index via a binary search, though expanded threads
|
||||
// make that tricky.
|
||||
|
||||
if (m_sortOrder == nsMsgViewSortOrder::ascending)
|
||||
nsMsgViewIndex highIndex = m_keys.Length();
|
||||
nsMsgViewIndex lowIndex = 0;
|
||||
IdKeyPtr EntryInfo1, EntryInfo2;
|
||||
EntryInfo1.key = nsnull;
|
||||
EntryInfo2.key = nsnull;
|
||||
|
||||
nsresult rv;
|
||||
PRUint16 maxLen;
|
||||
eFieldType fieldType;
|
||||
rv = GetFieldTypeAndLenForSort(m_sortType, &maxLen, &fieldType);
|
||||
const void *pValue1 = &EntryInfo1, *pValue2 = &EntryInfo2;
|
||||
|
||||
int retStatus = 0;
|
||||
msgHdr->GetMessageKey(&EntryInfo1.id);
|
||||
msgHdr->GetFolder(&EntryInfo1.folder);
|
||||
EntryInfo1.folder->Release();
|
||||
//check if a custom column handler exists. If it does then grab it and pass it in
|
||||
//to either GetCollationKey or GetLongField
|
||||
nsIMsgCustomColumnHandler* colHandler = GetCurColumnHandlerFromDBInfo();
|
||||
|
||||
viewSortInfo comparisonContext;
|
||||
comparisonContext.view = this;
|
||||
comparisonContext.isSecondarySort = PR_FALSE;
|
||||
comparisonContext.ascendingSort = (m_sortOrder == nsMsgViewSortOrder::ascending);
|
||||
nsCOMPtr <nsIMsgDatabase> hdrDB;
|
||||
EntryInfo1.folder->GetMsgDatabase(nsnull, getter_AddRefs(hdrDB));
|
||||
comparisonContext.db = hdrDB.get();
|
||||
switch (fieldType)
|
||||
{
|
||||
// loop backwards looking for top level message with id > id of header we're inserting
|
||||
// and put new header before found header, or at end.
|
||||
for (PRInt32 i = GetSize() - 1; i >= 0; i--)
|
||||
{
|
||||
if (m_levels[i] == 0)
|
||||
{
|
||||
if (insertKey < m_keys[i])
|
||||
prevInsertIndex = i;
|
||||
else if (insertKey >= m_keys[i])
|
||||
{
|
||||
retIndex = (prevInsertIndex == nsMsgViewIndex_None) ? nsMsgViewIndex_None : i + 1;
|
||||
if (prevInsertIndex == nsMsgViewIndex_None)
|
||||
{
|
||||
retIndex = nsMsgViewIndex_None;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (retIndex = i + 1; retIndex < (nsMsgViewIndex)GetSize(); retIndex++)
|
||||
{
|
||||
if (m_levels[retIndex] == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kCollationKey:
|
||||
rv = GetCollationKey(msgHdr, m_sortType, &EntryInfo1.key, &EntryInfo1.dword, colHandler);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv),"failed to create collation key");
|
||||
break;
|
||||
case kU32:
|
||||
if (m_sortType == nsMsgViewSortType::byId)
|
||||
EntryInfo1.dword = EntryInfo1.id;
|
||||
else
|
||||
GetLongField(msgHdr, m_sortType, &EntryInfo1.dword, colHandler);
|
||||
break;
|
||||
default:
|
||||
return highIndex;
|
||||
}
|
||||
while (highIndex > lowIndex)
|
||||
{
|
||||
nsMsgViewIndex tryIndex = (lowIndex + highIndex) / 2;
|
||||
// need to adjust tryIndex if it's not a thread.
|
||||
while (m_levels[tryIndex] && tryIndex)
|
||||
tryIndex--;
|
||||
|
||||
}
|
||||
if (tryIndex < lowIndex)
|
||||
{
|
||||
NS_ERROR("try index shouldn't be less than low index");
|
||||
break;
|
||||
}
|
||||
EntryInfo2.id = m_keys[tryIndex];
|
||||
GetFolderForViewIndex(tryIndex, &EntryInfo2.folder);
|
||||
EntryInfo2.folder->Release();
|
||||
|
||||
nsCOMPtr <nsIMsgDBHdr> tryHdr;
|
||||
nsCOMPtr <nsIMsgDatabase> db;
|
||||
// ### this should get the db from the folder...
|
||||
GetDBForViewIndex(tryIndex, getter_AddRefs(db));
|
||||
if (db)
|
||||
rv = db->GetMsgHdrForKey(EntryInfo2.id, getter_AddRefs(tryHdr));
|
||||
if (!tryHdr)
|
||||
break;
|
||||
if (tryHdr == msgHdr)
|
||||
{
|
||||
NS_WARNING("didn't expect header to already be in view");
|
||||
highIndex = tryIndex;
|
||||
break;
|
||||
}
|
||||
if (fieldType == kCollationKey)
|
||||
{
|
||||
PR_FREEIF(EntryInfo2.key);
|
||||
rv = GetCollationKey(tryHdr, m_sortType, &EntryInfo2.key, &EntryInfo2.dword, colHandler);
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv),"failed to create collation key");
|
||||
retStatus = FnSortIdKeyPtr(&pValue1, &pValue2, &comparisonContext);
|
||||
}
|
||||
else if (fieldType == kU32)
|
||||
{
|
||||
if (m_sortType == nsMsgViewSortType::byId)
|
||||
EntryInfo2.dword = EntryInfo2.id;
|
||||
else
|
||||
GetLongField(tryHdr, m_sortType, &EntryInfo2.dword, colHandler);
|
||||
retStatus = FnSortIdDWord(&pValue1, &pValue2, &comparisonContext);
|
||||
}
|
||||
if (retStatus == 0)
|
||||
{
|
||||
highIndex = tryIndex;
|
||||
break;
|
||||
}
|
||||
|
||||
if (retStatus < 0)
|
||||
{
|
||||
highIndex = tryIndex;
|
||||
// we already made sure tryIndex was at a thread at the top of the loop.
|
||||
}
|
||||
else
|
||||
{
|
||||
lowIndex = tryIndex + 1;
|
||||
while (lowIndex < GetSize() && m_levels[lowIndex])
|
||||
lowIndex++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// loop forwards looking for top level message with id < id of header we're inserting and put
|
||||
// new header before found header, or at beginning.
|
||||
for (PRInt32 i = 0; i < GetSize(); i++)
|
||||
{
|
||||
if (!m_levels[i])
|
||||
{
|
||||
if (insertKey > m_keys[i])
|
||||
{
|
||||
retIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return retIndex;
|
||||
|
||||
PR_Free(EntryInfo1.key);
|
||||
PR_Free(EntryInfo2.key);
|
||||
return highIndex;
|
||||
}
|
||||
|
||||
nsMsgViewIndex nsMsgDBView::GetInsertIndexHelper(nsIMsgDBHdr *msgHdr, nsTArray<nsMsgKey> &keys,
|
||||
|
@ -4599,9 +4659,8 @@ nsMsgViewIndex nsMsgDBView::GetInsertIndexHelper(nsIMsgDBHdr *msgHdr, nsTArray<n
|
|||
EntryInfo2.id = keys[tryIndex];
|
||||
if (folders)
|
||||
{
|
||||
nsCOMPtr<nsISupports> curFolder;
|
||||
folders->GetElementAt(tryIndex, getter_AddRefs(curFolder));
|
||||
EntryInfo2.folder = curFolder;
|
||||
nsCOMPtr<nsIMsgFolder> curFolder = do_QueryElementAt(folders, tryIndex);
|
||||
EntryInfo2.folder = curFolder;
|
||||
}
|
||||
else
|
||||
EntryInfo2.folder = m_folder;
|
||||
|
@ -4663,13 +4722,16 @@ nsMsgViewIndex nsMsgDBView::GetInsertIndex(nsIMsgDBHdr *msgHdr)
|
|||
return GetInsertIndexHelper(msgHdr, m_keys, m_sortOrder, m_sortType);
|
||||
}
|
||||
|
||||
nsresult nsMsgDBView::AddHdr(nsIMsgDBHdr *msgHdr)
|
||||
nsresult nsMsgDBView::AddHdr(nsIMsgDBHdr *msgHdr, nsMsgViewIndex *resultIndex)
|
||||
{
|
||||
PRUint32 flags = 0;
|
||||
#ifdef DEBUG_bienvenu
|
||||
NS_ASSERTION(m_keys.Length() == m_flags.Length() && (int) m_keys.Length() == m_levels.Length(), "view arrays out of sync!");
|
||||
#endif
|
||||
|
||||
if (resultIndex)
|
||||
*resultIndex = nsMsgViewIndex_None;
|
||||
|
||||
if (!GetShowingIgnored())
|
||||
{
|
||||
nsCOMPtr <nsIMsgThread> thread;
|
||||
|
@ -4708,6 +4770,8 @@ nsresult nsMsgDBView::AddHdr(nsIMsgDBHdr *msgHdr)
|
|||
m_keys.AppendElement(msgKey);
|
||||
m_flags.AppendElement(flags);
|
||||
m_levels.AppendElement(levelToAdd);
|
||||
if (resultIndex)
|
||||
*resultIndex = GetSize() - 1;
|
||||
|
||||
// the call to NoteChange() has to happen after we add the key
|
||||
// as NoteChange() will call RowCountChanged() which will call our GetRowCount()
|
||||
|
@ -4718,6 +4782,8 @@ nsresult nsMsgDBView::AddHdr(nsIMsgDBHdr *msgHdr)
|
|||
m_keys.InsertElementAt(0, msgKey);
|
||||
m_flags.InsertElementAt(0, flags);
|
||||
m_levels.InsertElementAt(0, levelToAdd);
|
||||
if (resultIndex)
|
||||
*resultIndex = 0;
|
||||
|
||||
// the call to NoteChange() has to happen after we insert the key
|
||||
// as NoteChange() will call RowCountChanged() which will call our GetRowCount()
|
||||
|
@ -4731,6 +4797,8 @@ nsresult nsMsgDBView::AddHdr(nsIMsgDBHdr *msgHdr)
|
|||
m_flags.InsertElementAt(insertIndex, flags);
|
||||
PRInt32 level = 0;
|
||||
m_levels.InsertElementAt(insertIndex, level);
|
||||
if (resultIndex)
|
||||
*resultIndex = insertIndex;
|
||||
|
||||
// the call to NoteChange() has to happen after we add the key
|
||||
// as NoteChange() will call RowCountChanged() which will call our GetRowCount()
|
||||
|
|
|
@ -220,13 +220,14 @@ protected:
|
|||
nsresult GenerateURIForMsgKey(nsMsgKey aMsgKey, nsIMsgFolder *folder, nsACString &aURI);
|
||||
// routines used in building up view
|
||||
virtual PRBool WantsThisThread(nsIMsgThread * thread);
|
||||
virtual nsresult AddHdr(nsIMsgDBHdr *msgHdr);
|
||||
virtual nsresult AddHdr(nsIMsgDBHdr *msgHdr, nsMsgViewIndex *resultIndex = nsnull);
|
||||
PRBool GetShowingIgnored() {return (m_viewFlags & nsMsgViewFlagsType::kShowIgnored) != 0;}
|
||||
virtual nsresult OnNewHeader(nsIMsgDBHdr *aNewHdr, nsMsgKey parentKey, PRBool ensureListed);
|
||||
virtual nsMsgViewIndex GetInsertIndex(nsIMsgDBHdr *msgHdr);
|
||||
nsMsgViewIndex GetIndexForThread(nsIMsgDBHdr *hdr);
|
||||
virtual nsresult GetThreadContainingIndex(nsMsgViewIndex index, nsIMsgThread **thread);
|
||||
virtual nsresult GetMsgHdrForViewIndex(nsMsgViewIndex index, nsIMsgDBHdr **msgHdr);
|
||||
nsMsgViewIndex FindIndexForThread(nsIMsgDBHdr *msgHdr, PRBool newThread);
|
||||
|
||||
nsresult ToggleExpansion(nsMsgViewIndex index, PRUint32 *numChanged);
|
||||
nsresult ExpandByIndex(nsMsgViewIndex index, PRUint32 *pNumExpanded);
|
||||
|
|
|
@ -607,7 +607,17 @@ nsresult nsMsgThreadedDBView::OnNewHeader(nsIMsgDBHdr *newHdr, nsMsgKey aParentK
|
|||
{ // Fix flags on thread header.
|
||||
PRInt32 threadCount;
|
||||
PRUint32 threadFlags;
|
||||
PRBool moveThread = PR_FALSE;
|
||||
nsMsgViewIndex threadIndex = ThreadIndexOfMsg(newKey, nsMsgViewIndex_None, &threadCount, &threadFlags);
|
||||
nsCOMPtr <nsIMsgThread> threadHdr;
|
||||
m_db->GetThreadContainingMsgHdr(newHdr, getter_AddRefs(threadHdr));
|
||||
if (threadHdr && m_sortType == nsMsgViewSortType::byDate)
|
||||
{
|
||||
PRUint32 newestMsgInThread = 0, msgDate = 0;
|
||||
threadHdr->GetNewestMsgDate(&newestMsgInThread);
|
||||
newHdr->GetDateInSeconds(&msgDate);
|
||||
moveThread = (msgDate == newestMsgInThread);
|
||||
}
|
||||
if (threadIndex != nsMsgViewIndex_None)
|
||||
{
|
||||
PRUint32 flags = m_flags[threadIndex];
|
||||
|
@ -658,13 +668,14 @@ nsresult nsMsgThreadedDBView::OnNewHeader(nsIMsgDBHdr *newHdr, nsMsgKey aParentK
|
|||
// top of thread, change the keys array.
|
||||
m_keys[threadIndex] = newKey;
|
||||
}
|
||||
if (moveThread)
|
||||
MoveThreadAt(threadIndex);
|
||||
else
|
||||
// note change, to update the parent thread's unread and total counts
|
||||
NoteChange(threadIndex, 1, nsMsgViewNotificationCode::changed);
|
||||
NoteChange(threadIndex, 1, nsMsgViewNotificationCode::changed);
|
||||
}
|
||||
else // adding msg to thread that's not in view.
|
||||
{
|
||||
nsCOMPtr <nsIMsgThread> threadHdr;
|
||||
m_db->GetThreadContainingMsgHdr(newHdr, getter_AddRefs(threadHdr));
|
||||
if (threadHdr)
|
||||
{
|
||||
AddMsgToThreadNotInView(threadHdr, newHdr, ensureListed);
|
||||
|
@ -722,6 +733,73 @@ nsMsgViewIndex nsMsgThreadedDBView::GetInsertInfoForNewHdr(nsIMsgDBHdr *newHdr,
|
|||
return parentIndex;
|
||||
}
|
||||
|
||||
// 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.
|
||||
DisableChangeUpdates();
|
||||
|
||||
nsCOMPtr <nsIMsgDBHdr> threadHdr;
|
||||
|
||||
GetMsgHdrForViewIndex(threadIndex, getter_AddRefs(threadHdr));
|
||||
PRInt32 childCount = 0;
|
||||
|
||||
nsMsgKey preservedKey;
|
||||
nsAutoTArray<nsMsgKey, 1> preservedSelection;
|
||||
SaveAndClearSelection(&preservedKey, preservedSelection);
|
||||
PRUint32 saveFlags = m_flags[threadIndex];
|
||||
PRBool threadIsExpanded = !(saveFlags & MSG_FLAG_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;
|
||||
AddHdr(threadHdr, &newIndex);
|
||||
|
||||
if (threadIsExpanded)
|
||||
{
|
||||
m_keys.InsertElementsAt(newIndex + 1, threadKeys);
|
||||
m_flags.InsertElementsAt(newIndex + 1, threadFlags);
|
||||
m_levels.InsertElementsAt(newIndex + 1, threadLevels);
|
||||
}
|
||||
m_flags[newIndex] = saveFlags;
|
||||
// unfreeze selection.
|
||||
RestoreSelection(preservedKey, preservedSelection);
|
||||
|
||||
EnableChangeUpdates();
|
||||
nsMsgViewIndex lowIndex = threadIndex < newIndex ? threadIndex : newIndex;
|
||||
nsMsgViewIndex highIndex = lowIndex == threadIndex ? newIndex : threadIndex;
|
||||
NoteChange(lowIndex, highIndex - lowIndex + childCount,
|
||||
nsMsgViewNotificationCode::changed);
|
||||
}
|
||||
nsresult nsMsgThreadedDBView::AddMsgToThreadNotInView(nsIMsgThread *threadHdr, nsIMsgDBHdr *msgHdr, PRBool ensureListed)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
|
|
@ -73,6 +73,7 @@ protected:
|
|||
void ClearPrevIdArray();
|
||||
virtual nsresult RemoveByIndex(nsMsgViewIndex index);
|
||||
nsMsgViewIndex GetInsertInfoForNewHdr(nsIMsgDBHdr *newHdr, nsMsgViewIndex threadIndex, PRInt32 targetLevel);
|
||||
void MoveThreadAt(nsMsgViewIndex threadIndex);
|
||||
|
||||
// these are used to save off the previous view so that bopping back and forth
|
||||
// between two views is quick (e.g., threaded and flat sorted by date).
|
||||
|
|
Загрузка…
Ссылка в новой задаче