fix 262319, new msg arriving in thread doesn't resort, when sorted by date, r=standard8, sr=neil

This commit is contained in:
David Bienvenu 2008-09-18 10:32:39 -07:00
Родитель abe8f65901
Коммит 1ce2bd7835
4 изменённых файлов: 208 добавлений и 60 удалений

Просмотреть файл

@ -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;
}
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)
{
for (retIndex = i + 1; retIndex < (nsMsgViewIndex)GetSize(); retIndex++)
nsMsgViewIndex tryIndex = (lowIndex + highIndex) / 2;
// need to adjust tryIndex if it's not a thread.
while (m_levels[tryIndex] && tryIndex)
tryIndex--;
if (tryIndex < lowIndex)
{
if (m_levels[retIndex] == 0)
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
{
// 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;
lowIndex = tryIndex + 1;
while (lowIndex < GetSize() && m_levels[lowIndex])
lowIndex++;
}
}
}
}
return retIndex;
PR_Free(EntryInfo1.key);
PR_Free(EntryInfo2.key);
return highIndex;
}
nsMsgViewIndex nsMsgDBView::GetInsertIndexHelper(nsIMsgDBHdr *msgHdr, nsTArray<nsMsgKey> &keys,
@ -4599,8 +4659,7 @@ nsMsgViewIndex nsMsgDBView::GetInsertIndexHelper(nsIMsgDBHdr *msgHdr, nsTArray<n
EntryInfo2.id = keys[tryIndex];
if (folders)
{
nsCOMPtr<nsISupports> curFolder;
folders->GetElementAt(tryIndex, getter_AddRefs(curFolder));
nsCOMPtr<nsIMsgFolder> curFolder = do_QueryElementAt(folders, tryIndex);
EntryInfo2.folder = curFolder;
}
else
@ -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);
}
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).