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; nsMsgKey id;
PRUint32 bits; PRUint32 bits;
PRUint32 dword; PRUint32 dword;
nsISupports* folder; nsIMsgFolder* folder;
}; };
struct IdKey : public IdDWord struct IdKey : public IdDWord
@ -3959,8 +3959,7 @@ NS_IMETHODIMP nsMsgDBView::Sort(nsMsgViewSortTypeValue sortType, nsMsgViewSortOr
if (folders) if (folders)
{ {
nsCOMPtr<nsISupports> curFolder; nsCOMPtr<nsIMsgFolder> curFolder = do_QueryElementAt(folders, numSoFar);
folders->GetElementAt(numSoFar, getter_AddRefs(curFolder));
info->folder = curFolder; info->folder = curFolder;
} }
else else
@ -4487,61 +4486,122 @@ nsresult nsMsgDBView::GetThreadContainingIndex(nsMsgViewIndex index, nsIMsgThrea
return GetThreadContainingMsgHdr(msgHdr, resultThread); return GetThreadContainingMsgHdr(msgHdr, resultThread);
} }
nsMsgViewIndex nsMsgDBView::GetIndexForThread(nsIMsgDBHdr *hdr) nsMsgViewIndex
nsMsgDBView::GetIndexForThread(nsIMsgDBHdr *msgHdr)
{ {
nsMsgViewIndex retIndex = nsMsgViewIndex_None; // Take advantage of the fact that we're already sorted
nsMsgViewIndex prevInsertIndex = nsMsgViewIndex_None; // and find the insert index via a binary search, though expanded threads
nsMsgKey insertKey; // make that tricky.
hdr->GetMessageKey(&insertKey);
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 case kCollationKey:
// and put new header before found header, or at end. rv = GetCollationKey(msgHdr, m_sortType, &EntryInfo1.key, &EntryInfo1.dword, colHandler);
for (PRInt32 i = GetSize() - 1; i >= 0; i--) NS_ASSERTION(NS_SUCCEEDED(rv),"failed to create collation key");
{ break;
if (m_levels[i] == 0) case kU32:
{ if (m_sortType == nsMsgViewSortType::byId)
if (insertKey < m_keys[i]) EntryInfo1.dword = EntryInfo1.id;
prevInsertIndex = i; else
else if (insertKey >= m_keys[i]) GetLongField(msgHdr, m_sortType, &EntryInfo1.dword, colHandler);
{ break;
retIndex = (prevInsertIndex == nsMsgViewIndex_None) ? nsMsgViewIndex_None : i + 1; default:
if (prevInsertIndex == nsMsgViewIndex_None) return highIndex;
{ }
retIndex = nsMsgViewIndex_None; while (highIndex > lowIndex)
} {
else nsMsgViewIndex tryIndex = (lowIndex + highIndex) / 2;
{ // need to adjust tryIndex if it's not a thread.
for (retIndex = i + 1; retIndex < (nsMsgViewIndex)GetSize(); retIndex++) while (m_levels[tryIndex] && tryIndex)
{ tryIndex--;
if (m_levels[retIndex] == 0)
break;
}
}
break;
}
} 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
{ PR_Free(EntryInfo1.key);
// loop forwards looking for top level message with id < id of header we're inserting and put PR_Free(EntryInfo2.key);
// new header before found header, or at beginning. return highIndex;
for (PRInt32 i = 0; i < GetSize(); i++)
{
if (!m_levels[i])
{
if (insertKey > m_keys[i])
{
retIndex = i;
break;
}
}
}
}
return retIndex;
} }
nsMsgViewIndex nsMsgDBView::GetInsertIndexHelper(nsIMsgDBHdr *msgHdr, nsTArray<nsMsgKey> &keys, nsMsgViewIndex nsMsgDBView::GetInsertIndexHelper(nsIMsgDBHdr *msgHdr, nsTArray<nsMsgKey> &keys,
@ -4599,9 +4659,8 @@ nsMsgViewIndex nsMsgDBView::GetInsertIndexHelper(nsIMsgDBHdr *msgHdr, nsTArray<n
EntryInfo2.id = keys[tryIndex]; EntryInfo2.id = keys[tryIndex];
if (folders) if (folders)
{ {
nsCOMPtr<nsISupports> curFolder; nsCOMPtr<nsIMsgFolder> curFolder = do_QueryElementAt(folders, tryIndex);
folders->GetElementAt(tryIndex, getter_AddRefs(curFolder)); EntryInfo2.folder = curFolder;
EntryInfo2.folder = curFolder;
} }
else else
EntryInfo2.folder = m_folder; EntryInfo2.folder = m_folder;
@ -4663,13 +4722,16 @@ nsMsgViewIndex nsMsgDBView::GetInsertIndex(nsIMsgDBHdr *msgHdr)
return GetInsertIndexHelper(msgHdr, m_keys, m_sortOrder, m_sortType); return GetInsertIndexHelper(msgHdr, m_keys, m_sortOrder, m_sortType);
} }
nsresult nsMsgDBView::AddHdr(nsIMsgDBHdr *msgHdr) nsresult nsMsgDBView::AddHdr(nsIMsgDBHdr *msgHdr, nsMsgViewIndex *resultIndex)
{ {
PRUint32 flags = 0; PRUint32 flags = 0;
#ifdef DEBUG_bienvenu #ifdef DEBUG_bienvenu
NS_ASSERTION(m_keys.Length() == m_flags.Length() && (int) m_keys.Length() == m_levels.Length(), "view arrays out of sync!"); NS_ASSERTION(m_keys.Length() == m_flags.Length() && (int) m_keys.Length() == m_levels.Length(), "view arrays out of sync!");
#endif #endif
if (resultIndex)
*resultIndex = nsMsgViewIndex_None;
if (!GetShowingIgnored()) if (!GetShowingIgnored())
{ {
nsCOMPtr <nsIMsgThread> thread; nsCOMPtr <nsIMsgThread> thread;
@ -4708,6 +4770,8 @@ nsresult nsMsgDBView::AddHdr(nsIMsgDBHdr *msgHdr)
m_keys.AppendElement(msgKey); m_keys.AppendElement(msgKey);
m_flags.AppendElement(flags); m_flags.AppendElement(flags);
m_levels.AppendElement(levelToAdd); m_levels.AppendElement(levelToAdd);
if (resultIndex)
*resultIndex = GetSize() - 1;
// the call to NoteChange() has to happen after we add the key // the call to NoteChange() has to happen after we add the key
// as NoteChange() will call RowCountChanged() which will call our GetRowCount() // 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_keys.InsertElementAt(0, msgKey);
m_flags.InsertElementAt(0, flags); m_flags.InsertElementAt(0, flags);
m_levels.InsertElementAt(0, levelToAdd); m_levels.InsertElementAt(0, levelToAdd);
if (resultIndex)
*resultIndex = 0;
// the call to NoteChange() has to happen after we insert the key // the call to NoteChange() has to happen after we insert the key
// as NoteChange() will call RowCountChanged() which will call our GetRowCount() // as NoteChange() will call RowCountChanged() which will call our GetRowCount()
@ -4731,6 +4797,8 @@ nsresult nsMsgDBView::AddHdr(nsIMsgDBHdr *msgHdr)
m_flags.InsertElementAt(insertIndex, flags); m_flags.InsertElementAt(insertIndex, flags);
PRInt32 level = 0; PRInt32 level = 0;
m_levels.InsertElementAt(insertIndex, level); m_levels.InsertElementAt(insertIndex, level);
if (resultIndex)
*resultIndex = insertIndex;
// the call to NoteChange() has to happen after we add the key // the call to NoteChange() has to happen after we add the key
// as NoteChange() will call RowCountChanged() which will call our GetRowCount() // as NoteChange() will call RowCountChanged() which will call our GetRowCount()

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

@ -220,13 +220,14 @@ protected:
nsresult GenerateURIForMsgKey(nsMsgKey aMsgKey, nsIMsgFolder *folder, nsACString &aURI); nsresult GenerateURIForMsgKey(nsMsgKey aMsgKey, nsIMsgFolder *folder, nsACString &aURI);
// routines used in building up view // routines used in building up view
virtual PRBool WantsThisThread(nsIMsgThread * thread); 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;} PRBool GetShowingIgnored() {return (m_viewFlags & nsMsgViewFlagsType::kShowIgnored) != 0;}
virtual nsresult OnNewHeader(nsIMsgDBHdr *aNewHdr, nsMsgKey parentKey, PRBool ensureListed); virtual nsresult OnNewHeader(nsIMsgDBHdr *aNewHdr, nsMsgKey parentKey, PRBool ensureListed);
virtual nsMsgViewIndex GetInsertIndex(nsIMsgDBHdr *msgHdr); virtual nsMsgViewIndex GetInsertIndex(nsIMsgDBHdr *msgHdr);
nsMsgViewIndex GetIndexForThread(nsIMsgDBHdr *hdr); nsMsgViewIndex GetIndexForThread(nsIMsgDBHdr *hdr);
virtual nsresult GetThreadContainingIndex(nsMsgViewIndex index, nsIMsgThread **thread); virtual nsresult GetThreadContainingIndex(nsMsgViewIndex index, nsIMsgThread **thread);
virtual nsresult GetMsgHdrForViewIndex(nsMsgViewIndex index, nsIMsgDBHdr **msgHdr); virtual nsresult GetMsgHdrForViewIndex(nsMsgViewIndex index, nsIMsgDBHdr **msgHdr);
nsMsgViewIndex FindIndexForThread(nsIMsgDBHdr *msgHdr, PRBool newThread);
nsresult ToggleExpansion(nsMsgViewIndex index, PRUint32 *numChanged); nsresult ToggleExpansion(nsMsgViewIndex index, PRUint32 *numChanged);
nsresult ExpandByIndex(nsMsgViewIndex index, PRUint32 *pNumExpanded); nsresult ExpandByIndex(nsMsgViewIndex index, PRUint32 *pNumExpanded);

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

@ -607,7 +607,17 @@ nsresult nsMsgThreadedDBView::OnNewHeader(nsIMsgDBHdr *newHdr, nsMsgKey aParentK
{ // Fix flags on thread header. { // Fix flags on thread header.
PRInt32 threadCount; PRInt32 threadCount;
PRUint32 threadFlags; PRUint32 threadFlags;
PRBool moveThread = PR_FALSE;
nsMsgViewIndex threadIndex = ThreadIndexOfMsg(newKey, nsMsgViewIndex_None, &threadCount, &threadFlags); 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) if (threadIndex != nsMsgViewIndex_None)
{ {
PRUint32 flags = m_flags[threadIndex]; PRUint32 flags = m_flags[threadIndex];
@ -658,13 +668,14 @@ nsresult nsMsgThreadedDBView::OnNewHeader(nsIMsgDBHdr *newHdr, nsMsgKey aParentK
// top of thread, change the keys array. // top of thread, change the keys array.
m_keys[threadIndex] = newKey; m_keys[threadIndex] = newKey;
} }
if (moveThread)
MoveThreadAt(threadIndex);
else
// note change, to update the parent thread's unread and total counts // 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. else // adding msg to thread that's not in view.
{ {
nsCOMPtr <nsIMsgThread> threadHdr;
m_db->GetThreadContainingMsgHdr(newHdr, getter_AddRefs(threadHdr));
if (threadHdr) if (threadHdr)
{ {
AddMsgToThreadNotInView(threadHdr, newHdr, ensureListed); AddMsgToThreadNotInView(threadHdr, newHdr, ensureListed);
@ -722,6 +733,73 @@ nsMsgViewIndex nsMsgThreadedDBView::GetInsertInfoForNewHdr(nsIMsgDBHdr *newHdr,
return parentIndex; 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 nsMsgThreadedDBView::AddMsgToThreadNotInView(nsIMsgThread *threadHdr, nsIMsgDBHdr *msgHdr, PRBool ensureListed)
{ {
nsresult rv = NS_OK; nsresult rv = NS_OK;

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

@ -73,6 +73,7 @@ protected:
void ClearPrevIdArray(); void ClearPrevIdArray();
virtual nsresult RemoveByIndex(nsMsgViewIndex index); virtual nsresult RemoveByIndex(nsMsgViewIndex index);
nsMsgViewIndex GetInsertInfoForNewHdr(nsIMsgDBHdr *newHdr, nsMsgViewIndex threadIndex, PRInt32 targetLevel); 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 // 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). // between two views is quick (e.g., threaded and flat sorted by date).