548 lines
18 KiB
C++
548 lines
18 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 2001
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Navin Gupta <naving@netscape.com> (Original Author)
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
#include "msgCore.h"
|
|
#include "nsMsgQuickSearchDBView.h"
|
|
#include "nsMsgFolderFlags.h"
|
|
#include "nsIMsgHdr.h"
|
|
#include "nsMsgBaseCID.h"
|
|
#include "nsIMsgImapMailFolder.h"
|
|
#include "nsImapCore.h"
|
|
#include "nsIMsgHdr.h"
|
|
#include "nsIDBFolderInfo.h"
|
|
|
|
nsMsgQuickSearchDBView::nsMsgQuickSearchDBView()
|
|
{
|
|
m_usingCachedHits = PR_FALSE;
|
|
m_cacheEmpty = PR_TRUE;
|
|
}
|
|
|
|
nsMsgQuickSearchDBView::~nsMsgQuickSearchDBView()
|
|
{
|
|
/* destructor code */
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS_INHERITED2(nsMsgQuickSearchDBView, nsMsgDBView, nsIMsgDBView, nsIMsgSearchNotify)
|
|
|
|
NS_IMETHODIMP nsMsgQuickSearchDBView::Open(nsIMsgFolder *folder, nsMsgViewSortTypeValue sortType, nsMsgViewSortOrderValue sortOrder, nsMsgViewFlagsTypeValue viewFlags, PRInt32 *pCount)
|
|
{
|
|
nsresult rv = nsMsgDBView::Open(folder, sortType, sortOrder, viewFlags, pCount);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
if (!m_db)
|
|
return NS_ERROR_NULL_POINTER;
|
|
if (pCount)
|
|
*pCount = 0;
|
|
m_viewFolder = nsnull;
|
|
return InitThreadedView(pCount);
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgQuickSearchDBView::DoCommand(nsMsgViewCommandTypeValue aCommand)
|
|
{
|
|
if (aCommand == nsMsgViewCommandType::markAllRead)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
m_folder->EnableNotifications(nsIMsgFolder::allMessageCountNotifications, PR_FALSE, PR_TRUE /*dbBatching*/);
|
|
|
|
for (PRInt32 i=0;NS_SUCCEEDED(rv) && i < GetSize();i++)
|
|
{
|
|
nsCOMPtr<nsIMsgDBHdr> msgHdr;
|
|
m_db->GetMsgHdrForKey(m_keys[i],getter_AddRefs(msgHdr));
|
|
rv = m_db->MarkHdrRead(msgHdr, PR_TRUE, nsnull);
|
|
}
|
|
|
|
m_folder->EnableNotifications(nsIMsgFolder::allMessageCountNotifications, PR_TRUE, PR_TRUE /*dbBatching*/);
|
|
|
|
nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(m_folder);
|
|
if (NS_SUCCEEDED(rv) && imapFolder)
|
|
rv = imapFolder->StoreImapFlags(kImapMsgSeenFlag, PR_TRUE, m_keys.GetArray(),
|
|
m_keys.GetSize(), nsnull);
|
|
|
|
m_db->SetSummaryValid(PR_TRUE);
|
|
return rv;
|
|
}
|
|
else
|
|
return nsMsgDBView::DoCommand(aCommand);
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgQuickSearchDBView::GetViewType(nsMsgViewTypeValue *aViewType)
|
|
{
|
|
NS_ENSURE_ARG_POINTER(aViewType);
|
|
*aViewType = nsMsgViewType::eShowQuickSearchResults;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsMsgQuickSearchDBView::OnNewHeader(nsIMsgDBHdr *newHdr, nsMsgKey aParentKey, PRBool ensureListed)
|
|
{
|
|
if (newHdr)
|
|
{
|
|
PRBool match=PR_FALSE;
|
|
nsCOMPtr <nsIMsgSearchSession> searchSession = do_QueryReferent(m_searchSession);
|
|
if (searchSession)
|
|
searchSession->MatchHdr(newHdr, m_db, &match);
|
|
if (match)
|
|
{
|
|
// put the new header in m_origKeys, so that expanding a thread will
|
|
// show the newly added header.
|
|
nsMsgKey newKey;
|
|
(void) newHdr->GetMessageKey(&newKey);
|
|
nsMsgViewIndex insertIndex = GetInsertIndexHelper(newHdr, &m_origKeys,
|
|
nsMsgViewSortOrder::ascending, nsMsgViewSortType::byId);
|
|
m_origKeys.InsertAt(insertIndex, newKey);
|
|
nsMsgThreadedDBView::OnNewHeader(newHdr, aParentKey, ensureListed); // do not add a new message if there isn't a match.
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP nsMsgQuickSearchDBView::OnHdrChange(nsIMsgDBHdr *aHdrChanged, PRUint32 aOldFlags,
|
|
PRUint32 aNewFlags, nsIDBChangeListener *aInstigator)
|
|
{
|
|
nsresult rv = nsMsgDBView::OnHdrChange(aHdrChanged, aOldFlags, aNewFlags, aInstigator);
|
|
// flags haven't really changed - check if the message is newly classified as junk
|
|
if ((aOldFlags == aNewFlags) && (aOldFlags & MSG_FLAG_NEW))
|
|
{
|
|
if (aHdrChanged)
|
|
{
|
|
nsXPIDLCString junkScoreStr;
|
|
(void) aHdrChanged->GetStringProperty("junkscore", getter_Copies(junkScoreStr));
|
|
if (atoi(junkScoreStr.get()) > 50)
|
|
{
|
|
nsXPIDLCString originStr;
|
|
(void) aHdrChanged->GetStringProperty("junkscoreorigin",
|
|
getter_Copies(originStr));
|
|
|
|
// if this was classified by the plugin, see if we're supposed to
|
|
// show junk mail
|
|
if (originStr.get()[0] == 'p')
|
|
{
|
|
PRBool match=PR_FALSE;
|
|
nsCOMPtr <nsIMsgSearchSession> searchSession = do_QueryReferent(m_searchSession);
|
|
if (searchSession)
|
|
searchSession->MatchHdr(aHdrChanged, m_db, &match);
|
|
if (!match)
|
|
{
|
|
// remove hdr from view
|
|
nsMsgViewIndex deletedIndex = FindHdr(aHdrChanged);
|
|
if (deletedIndex != nsMsgViewIndex_None)
|
|
RemoveByIndex(deletedIndex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (m_viewFolder && (aOldFlags & MSG_FLAG_READ) != (aNewFlags & MSG_FLAG_READ))
|
|
{
|
|
// if we're displaying a single folder virtual folder for an imap folder,
|
|
// the search criteria might be on message body, and we might not have the
|
|
// message body offline, in which case we can't tell if the message
|
|
// matched or not. But if the unread flag changed, we need to update the
|
|
// unread counts. Normally, VirtualFolderChangeListener::OnHdrChange will
|
|
// handle this, but it won't work for body criteria when we don't have the
|
|
// body offline.
|
|
nsCOMPtr<nsIMsgImapMailFolder> imapFolder = do_QueryInterface(m_viewFolder);
|
|
if (imapFolder)
|
|
{
|
|
nsMsgViewIndex hdrIndex = FindHdr(aHdrChanged);
|
|
if (hdrIndex != nsMsgViewIndex_None)
|
|
{
|
|
nsCOMPtr <nsIMsgSearchSession> searchSession = do_QueryReferent(m_searchSession);
|
|
if (searchSession)
|
|
{
|
|
PRBool oldMatch, newMatch;
|
|
rv = searchSession->MatchHdr(aHdrChanged, m_db, &newMatch);
|
|
aHdrChanged->SetFlags(aOldFlags);
|
|
rv = searchSession->MatchHdr(aHdrChanged, m_db, &oldMatch);
|
|
aHdrChanged->SetFlags(aNewFlags);
|
|
// if it doesn't match the criteria, VirtualFolderChangeListener::OnHdrChange
|
|
// won't tweak the read/unread counts. So do it here:
|
|
if (!oldMatch && !newMatch)
|
|
{
|
|
nsCOMPtr <nsIMsgDatabase> virtDatabase;
|
|
nsCOMPtr <nsIDBFolderInfo> dbFolderInfo;
|
|
|
|
rv = m_viewFolder->GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo), getter_AddRefs(virtDatabase));
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
dbFolderInfo->ChangeNumUnreadMessages((aOldFlags & MSG_FLAG_READ) ? 1 : -1);
|
|
m_viewFolder->UpdateSummaryTotals(PR_TRUE); // force update from db.
|
|
virtDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgQuickSearchDBView::GetSearchSession(nsIMsgSearchSession* *aSession)
|
|
{
|
|
NS_ASSERTION(PR_FALSE, "GetSearchSession method is not implemented");
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgQuickSearchDBView::SetSearchSession(nsIMsgSearchSession *aSession)
|
|
{
|
|
m_searchSession = do_GetWeakReference(aSession);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgQuickSearchDBView::OnSearchHit(nsIMsgDBHdr* aMsgHdr, nsIMsgFolder *folder)
|
|
{
|
|
NS_ENSURE_ARG(aMsgHdr);
|
|
if (!m_db)
|
|
return NS_ERROR_NULL_POINTER;
|
|
// remember search hit and when search is done, reconcile cache
|
|
// with new hits;
|
|
m_hdrHits.AppendObject(aMsgHdr);
|
|
nsMsgKey key;
|
|
aMsgHdr->GetMessageKey(&key);
|
|
// is FindKey going to be expensive here? A lot of hits could make
|
|
// it a little bit slow to search through the view for every hit.
|
|
if (m_cacheEmpty || FindKey(key, PR_FALSE) == nsMsgViewIndex_None)
|
|
return AddHdr(aMsgHdr);
|
|
else
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgQuickSearchDBView::OnSearchDone(nsresult status)
|
|
{
|
|
if (m_viewFolder)
|
|
{
|
|
nsMsgKeyArray keyArray;
|
|
nsXPIDLCString searchUri;
|
|
m_viewFolder->GetURI(getter_Copies(searchUri));
|
|
PRUint32 count = m_hdrHits.Count();
|
|
// build up message keys.
|
|
PRUint32 i;
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
nsMsgKey key;
|
|
m_hdrHits[i]->GetMessageKey(&key);
|
|
keyArray.Add(key);
|
|
}
|
|
nsMsgKey *staleHits;
|
|
PRUint32 numBadHits;
|
|
if (m_db)
|
|
{
|
|
nsresult rv = m_db->RefreshCache(searchUri, m_hdrHits.Count(), keyArray.GetArray(), &numBadHits, &staleHits);
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
for (i = 0; i < numBadHits; i++)
|
|
{
|
|
nsMsgViewIndex staleHitIndex = FindKey(staleHits[i], PR_TRUE);
|
|
if (staleHitIndex != nsMsgViewIndex_None)
|
|
RemoveByIndex(staleHitIndex);
|
|
}
|
|
delete [] staleHits;
|
|
}
|
|
}
|
|
if (m_sortType != nsMsgViewSortType::byThread)//we do not find levels for the results.
|
|
{
|
|
m_sortValid = PR_FALSE; //sort the results
|
|
Sort(m_sortType, m_sortOrder);
|
|
}
|
|
if (m_viewFolder)
|
|
SetMRUTimeForFolder(m_viewFolder);
|
|
|
|
m_hdrHits.Clear();
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsMsgQuickSearchDBView::OnNewSearch()
|
|
{
|
|
PRInt32 oldSize = GetSize();
|
|
|
|
m_keys.RemoveAll();
|
|
m_levels.RemoveAll();
|
|
m_flags.RemoveAll();
|
|
m_hdrHits.Clear();
|
|
// this needs to happen after we remove all the keys, since RowCountChanged() will call our GetRowCount()
|
|
if (mTree)
|
|
mTree->RowCountChanged(0, -oldSize);
|
|
PRUint32 folderFlags = 0;
|
|
if (m_viewFolder)
|
|
m_viewFolder->GetFlags(&folderFlags);
|
|
// check if it's a virtual folder - if so, we should get the cached hits
|
|
// from the db, and set a flag saying that we're using cached values.
|
|
if (folderFlags & MSG_FOLDER_FLAG_VIRTUAL)
|
|
{
|
|
nsCOMPtr<nsISimpleEnumerator> cachedHits;
|
|
nsXPIDLCString searchUri;
|
|
m_viewFolder->GetURI(getter_Copies(searchUri));
|
|
m_db->GetCachedHits(searchUri, getter_AddRefs(cachedHits));
|
|
if (cachedHits)
|
|
{
|
|
PRBool hasMore;
|
|
|
|
m_usingCachedHits = PR_TRUE;
|
|
cachedHits->HasMoreElements(&hasMore);
|
|
m_cacheEmpty = !hasMore;
|
|
while (hasMore)
|
|
{
|
|
nsCOMPtr <nsIMsgDBHdr> pHeader;
|
|
nsresult rv = cachedHits->GetNext(getter_AddRefs(pHeader));
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "nsMsgDBEnumerator broken");
|
|
if (pHeader && NS_SUCCEEDED(rv))
|
|
AddHdr(pHeader);
|
|
else
|
|
break;
|
|
cachedHits->HasMoreElements(&hasMore);
|
|
}
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsMsgQuickSearchDBView::GetFirstMessageHdrToDisplayInThread(nsIMsgThread *threadHdr, nsIMsgDBHdr **result)
|
|
{
|
|
PRUint32 numChildren;
|
|
nsresult rv = NS_OK;
|
|
PRUint8 minLevel = 0xff;
|
|
nsMsgKey threadRootKey;
|
|
|
|
threadHdr->GetNumChildren(&numChildren);
|
|
threadHdr->GetThreadKey(&threadRootKey);
|
|
if ((PRInt32) numChildren < 0)
|
|
numChildren = 0;
|
|
|
|
nsCOMPtr <nsIMsgDBHdr> retHdr;
|
|
|
|
// iterate over thread, finding mgsHdr in view with the lowest level.
|
|
for (PRUint32 childIndex = 0; childIndex < numChildren; childIndex++)
|
|
{
|
|
nsCOMPtr <nsIMsgDBHdr> child;
|
|
rv = threadHdr->GetChildHdrAt(childIndex, getter_AddRefs(child));
|
|
if (NS_SUCCEEDED(rv) && child)
|
|
{
|
|
nsMsgKey msgKey;
|
|
child->GetMessageKey(&msgKey);
|
|
|
|
// this works because we've already sorted m_keys by id.
|
|
nsMsgViewIndex keyIndex = m_origKeys.IndexOfSorted(msgKey);
|
|
if (keyIndex != kNotFound)
|
|
{
|
|
// this is the root, so it's the best we're going to do.
|
|
if (msgKey == threadRootKey)
|
|
{
|
|
retHdr = child;
|
|
break;
|
|
}
|
|
PRUint8 level = 0;
|
|
nsMsgKey parentId;
|
|
child->GetThreadParent(&parentId);
|
|
nsCOMPtr <nsIMsgDBHdr> parent;
|
|
// count number of ancestors - that's our level
|
|
while (parentId != nsMsgKey_None)
|
|
{
|
|
rv = m_db->GetMsgHdrForKey(parentId, getter_AddRefs(parent));
|
|
if (parent)
|
|
{
|
|
nsMsgKey saveParentId = parentId;
|
|
parent->GetThreadParent(&parentId);
|
|
// message is it's own parent - bad, let's break out of here.
|
|
if (parentId == saveParentId)
|
|
break;
|
|
level++;
|
|
}
|
|
else // if we can't find the parent, don't loop forever.
|
|
break;
|
|
}
|
|
if (level < minLevel)
|
|
{
|
|
minLevel = level;
|
|
retHdr = child;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
NS_IF_ADDREF(*result = retHdr);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsMsgQuickSearchDBView::SortThreads(nsMsgViewSortTypeValue sortType, nsMsgViewSortOrderValue sortOrder)
|
|
{
|
|
if (m_viewFlags & nsMsgViewFlagsType::kGroupBySort)
|
|
return NS_OK;
|
|
|
|
// iterate over the messages in the view, getting the thread id's
|
|
// sort m_keys so we can quickly find if a key is in the view.
|
|
m_keys.QuickSort();
|
|
// array of the threads' root hdr keys.
|
|
nsMsgKeyArray threadRootIds;
|
|
nsCOMPtr <nsIMsgDBHdr> rootHdr;
|
|
nsCOMPtr <nsIMsgDBHdr> msgHdr;
|
|
nsCOMPtr <nsIMsgThread> threadHdr;
|
|
for (PRUint32 i = 0; i < m_keys.GetSize(); i++)
|
|
{
|
|
GetMsgHdrForViewIndex(i, getter_AddRefs(msgHdr));
|
|
m_db->GetThreadContainingMsgHdr(msgHdr, getter_AddRefs(threadHdr));
|
|
if (threadHdr)
|
|
{
|
|
nsMsgKey rootKey;
|
|
threadHdr->GetChildKeyAt(0, &rootKey);
|
|
nsMsgViewIndex threadRootIndex = threadRootIds.IndexOfSorted(rootKey);
|
|
// if we already have that id in top level threads, ignore this msg.
|
|
if (threadRootIndex != kNotFound)
|
|
continue;
|
|
// it would be nice if GetInsertIndexHelper always found the hdr, but it doesn't.
|
|
threadHdr->GetChildHdrAt(0, getter_AddRefs(rootHdr));
|
|
if (!rootHdr)
|
|
continue;
|
|
threadRootIndex = GetInsertIndexHelper(rootHdr, &threadRootIds, nsMsgViewSortOrder::ascending, nsMsgViewSortType::byId);
|
|
threadRootIds.InsertAt(threadRootIndex, rootKey);
|
|
}
|
|
}
|
|
m_origKeys.CopyArray(m_keys);
|
|
// need to sort the top level threads now by sort order, if it's not by id.
|
|
if (sortType != nsMsgViewSortType::byId)
|
|
{
|
|
m_keys.CopyArray(threadRootIds);
|
|
nsMsgDBView::Sort(sortType, sortOrder);
|
|
threadRootIds.CopyArray(m_keys);
|
|
}
|
|
m_keys.RemoveAll();
|
|
m_levels.RemoveAll();
|
|
m_flags.RemoveAll();
|
|
// now we've build up the list of thread ids - need to build the view
|
|
// from that. So for each thread id, we need to list the messages in the thread.
|
|
PRUint32 numThreads = threadRootIds.GetSize();
|
|
for (PRUint32 threadIndex = 0; threadIndex < numThreads; threadIndex++)
|
|
{
|
|
m_db->GetMsgHdrForKey(threadRootIds[threadIndex], getter_AddRefs(rootHdr));
|
|
if (rootHdr)
|
|
{
|
|
nsCOMPtr <nsIMsgDBHdr> displayRootHdr;
|
|
m_db->GetThreadContainingMsgHdr(rootHdr, getter_AddRefs(threadHdr));
|
|
if (threadHdr)
|
|
{
|
|
nsMsgKey rootKey;
|
|
PRUint32 rootFlags;
|
|
GetFirstMessageHdrToDisplayInThread(threadHdr, getter_AddRefs(displayRootHdr));
|
|
if (!displayRootHdr)
|
|
continue;
|
|
displayRootHdr->GetMessageKey(&rootKey);
|
|
displayRootHdr->GetFlags(&rootFlags);
|
|
rootFlags |= MSG_VIEW_FLAG_ISTHREAD;
|
|
m_keys.Add(rootKey);
|
|
m_flags.Add(rootFlags);
|
|
m_levels.Add(0);
|
|
|
|
nsMsgViewIndex startOfThreadViewIndex = m_keys.GetSize() - 1;
|
|
PRUint32 numListed;
|
|
ListIdsInThread(threadHdr, startOfThreadViewIndex, &numListed);
|
|
}
|
|
}
|
|
}
|
|
NS_ASSERTION(m_origKeys.GetSize() == m_keys.GetSize(), "problem threading quick search");
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsMsgQuickSearchDBView::ListIdsInThread(nsIMsgThread *threadHdr, nsMsgViewIndex startOfThreadViewIndex, PRUint32 *pNumListed)
|
|
{
|
|
PRUint32 numChildren;
|
|
threadHdr->GetNumChildren(&numChildren);
|
|
PRUint32 i;
|
|
PRUint32 viewIndex = startOfThreadViewIndex + 1;
|
|
nsCOMPtr <nsIMsgDBHdr> rootHdr;
|
|
nsMsgKey rootKey;
|
|
PRUint32 rootFlags = m_flags[startOfThreadViewIndex];
|
|
*pNumListed = 0;
|
|
GetMsgHdrForViewIndex(startOfThreadViewIndex, getter_AddRefs(rootHdr));
|
|
rootHdr->GetMessageKey(&rootKey);
|
|
for (i = 0; i < numChildren; i++)
|
|
{
|
|
nsCOMPtr <nsIMsgDBHdr> msgHdr;
|
|
threadHdr->GetChildHdrAt(i, getter_AddRefs(msgHdr));
|
|
if (msgHdr != nsnull)
|
|
{
|
|
nsMsgKey msgKey;
|
|
msgHdr->GetMessageKey(&msgKey);
|
|
if (msgKey != rootKey)
|
|
{
|
|
nsMsgViewIndex threadRootIndex = m_origKeys.IndexOfSorted(msgKey);
|
|
// if this hdr is in the original view, add it to new view.
|
|
if (threadRootIndex != kNotFound)
|
|
{
|
|
PRUint32 childFlags;
|
|
msgHdr->GetFlags(&childFlags);
|
|
PRUint8 levelToAdd;
|
|
m_keys.InsertAt(viewIndex, msgKey);
|
|
m_flags.InsertAt(viewIndex, childFlags);
|
|
if (! (rootFlags & MSG_VIEW_FLAG_HASCHILDREN))
|
|
{
|
|
rootFlags |= MSG_VIEW_FLAG_HASCHILDREN;
|
|
m_flags.SetAt(startOfThreadViewIndex, rootFlags);
|
|
}
|
|
levelToAdd = FindLevelInThread(msgHdr, startOfThreadViewIndex, viewIndex);
|
|
m_levels.InsertAt(viewIndex, levelToAdd);
|
|
viewIndex++;
|
|
(*pNumListed)++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult nsMsgQuickSearchDBView::ExpansionDelta(nsMsgViewIndex index, PRInt32 *expansionDelta)
|
|
{
|
|
*expansionDelta = 0;
|
|
if ( index > ((nsMsgViewIndex) m_keys.GetSize()))
|
|
return NS_MSG_MESSAGE_NOT_FOUND;
|
|
|
|
char flags = m_flags[index];
|
|
|
|
if (!(m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay))
|
|
return NS_OK;
|
|
|
|
// The client can pass in the key of any message
|
|
// in a thread and get the expansion delta for the thread.
|
|
|
|
PRInt32 numChildren = CountExpandedThread(index);
|
|
|
|
*expansionDelta = (flags & MSG_FLAG_ELIDED) ?
|
|
numChildren - 1 : - (PRInt32) (numChildren - 1);
|
|
return NS_OK;
|
|
}
|