Fix for 24768. r=alecf. Move view navigation code to C++ to speed up.

This commit is contained in:
putterman%netscape.com 2000-01-28 23:25:17 +00:00
Родитель 69cacae78b
Коммит d04174bf7b
20 изменённых файлов: 1575 добавлений и 514 удалений

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

@ -275,4 +275,14 @@
{ 0xbb460dff, 0x8bf0, 0x11d3, \
{ 0x8a, 0xfe, 0x0, 0x60, 0xb0, 0xfc, 0x4, 0xd2}}
//
//nsMsgViewNavigationService
//
#define NS_MSGVIEWNAVIGATIONSERVICE_PROGID \
"component://netscape/messenger/msgviewnavigationservice"
/* 60D34FB4-D031-11d3-8B2E-0060B0FC04D2*/
#define NS_MSGVIEWNAVIGATIONSERVICE_CID \
{ 0x60d34fb4, 0xd031, 0x11d3, \
{ 0x8b, 0x2e, 0x0, 0x60, 0xb0, 0xfc, 0x4, 0xd2}}
#endif // nsMessageBaseCID_h__

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

@ -70,6 +70,7 @@
#include "nsMsgFilterService.h"
#include "nsMessageView.h"
#include "nsMsgWindow.h"
#include "nsMsgViewNavigationService.h"
static NS_DEFINE_CID(kComponentManagerCID, NS_COMPONENTMANAGER_CID);
@ -123,6 +124,9 @@ static NS_DEFINE_CID(kMessageViewCID, NS_MESSAGEVIEW_CID);
//MsgWindow
static NS_DEFINE_CID(kMsgWindowCID, NS_MSGWINDOW_CID);
//MsgViewNavigationService
static NS_DEFINE_CID(kMsgViewNavigationServiceCID, NS_MSGVIEWNAVIGATIONSERVICE_CID);
// private factory declarations for each component we know how to produce
NS_GENERIC_FACTORY_CONSTRUCTOR(nsMessengerBootstrap)
@ -145,6 +149,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsMsgFolderCache)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsMsgStatusFeedback)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsMessageView,Init)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsMsgWindow,Init)
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsMsgViewNavigationService,Init)
// Module implementation for the sample library
class nsMsgBaseModule : public nsIModule
@ -184,6 +189,7 @@ protected:
nsCOMPtr<nsIGenericFactory> mMsgStatusFeedbackFactory;
nsCOMPtr<nsIGenericFactory> mMessageViewFactory;
nsCOMPtr<nsIGenericFactory> mMsgWindowFactory;
nsCOMPtr<nsIGenericFactory> mMsgViewNavigationServiceFactory;
};
nsMsgBaseModule::nsMsgBaseModule()
@ -233,6 +239,7 @@ void nsMsgBaseModule::Shutdown()
mMsgStatusFeedbackFactory = null_nsCOMPtr();
mMessageViewFactory = null_nsCOMPtr();
mMsgWindowFactory = null_nsCOMPtr();
mMsgViewNavigationServiceFactory = null_nsCOMPtr();
}
// Create a factory object for creating instances of aClass.
@ -381,6 +388,12 @@ NS_IMETHODIMP nsMsgBaseModule::GetClassObject(nsIComponentManager *aCompMgr,
rv = NS_NewGenericFactory(getter_AddRefs(mMsgWindowFactory), &nsMsgWindowConstructor);
fact = mMsgWindowFactory;
}
else if (aClass.Equals(kMsgViewNavigationServiceCID))
{
if (!mMsgViewNavigationServiceFactory)
rv = NS_NewGenericFactory(getter_AddRefs(mMsgViewNavigationServiceFactory), &nsMsgViewNavigationServiceConstructor);
fact = mMsgViewNavigationServiceFactory;
}
if (fact)
@ -436,7 +449,9 @@ static Components gComponents[] = {
{ "Mail/News MessageView", &kMessageViewCID,
NS_MESSAGEVIEW_PROGID},
{ "Mail/News MsgWindow", &kMsgWindowCID,
NS_MSGWINDOW_PROGID}
NS_MSGWINDOW_PROGID},
{ "Mail/News Message Navigation Service", &kMsgViewNavigationServiceCID,
NS_MSGVIEWNAVIGATIONSERVICE_PROGID}
};

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

@ -36,3 +36,5 @@ nsIMsgProtocolInfo.idl
nsIMsgRDFDataSource.idl
nsIIncomingServerListener.idl
nsIMsgHdr.idl
nsIMsgViewNavigationService.idl

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

@ -72,6 +72,7 @@ XPIDLSRCS = \
nsIIncomingServerListener.idl \
nsIMsgHdr.idl \
nsIMessengerMigrator.idl \
nsIMsgViewNavigationService.idl \
$(NULL)
include $(topsrcdir)/config/rules.mk

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

@ -57,6 +57,7 @@ XPIDLSRCS = \
.\nsIMsgRDFDataSource.idl \
.\nsIIncomingServerListener.idl \
.\nsIMsgHdr.idl \
.\nsIMsgViewNavigationService.idl \
$(NULL)
################################################################################

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

@ -164,8 +164,27 @@ interface nsIMsgFolder : nsIFolder {
*/
long getTotalMessages(in boolean deep);
/**
* does this folder have new messages
*
*/
boolean hasNewMessages();
/**
* return the first new message in the folder
*
*/
readonly attribute nsIMessage firstNewMessage;
/**
* clear new status flag of all of the new messages
*
*/
void clearNewMessages();
readonly attribute unsigned long expungedBytesCount;
/**
* can this folder be deleted?
* for example, special folders cannot be deleted

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

@ -0,0 +1,60 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Netscape are
* Copyright (C) 2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsISupports.idl"
#include "xulstubs.idl"
#include "domstubs.idl"
#include "nsIRDFService.idl"
%{C++
#include "nsIDOMXULTreeElement.h"
#include "nsIDOMXULElement.h"
#include "nsIDOMXULDocument.h"
#include "nsIDOMNode.h"
%}
[scriptable, uuid(BDD872B6-D02C-11d3-8B2E-0060B0FC04D2)]
interface nsIMsgViewNavigationService : nsISupports {
%{C++
enum { eNavigateAny =0, eNavigateUnread=1, eNavigateFlagged =2 , eNavigateNew =3};
%}
nsIDOMXULElement FindNextMessage(in long type, in nsIDOMXULTreeElement tree, in nsIDOMXULElement originalMessage,
in nsIRDFService rdfService, in nsIDOMXULDocument document,
in boolean wrapAround, in boolean isThreaded);
nsIDOMXULElement FindFirstMessage(in nsIDOMXULTreeElement tree);
nsIDOMXULElement FindNextThread(in long type, in nsIDOMXULTreeElement tree, in nsIDOMXULElement originalMessage,
in nsIRDFService rdfService, in nsIDOMXULDocument document, in boolean wrapAround,
in boolean checkOriginalMessage);
nsIDOMXULElement FindNextInThread(in long type, in nsIDOMXULTreeElement tree, in nsIDOMXULElement originalMessage,
in nsIRDFService rdfService, in nsIDOMXULDocument document);
nsIDOMXULElement FindPreviousMessage(in long type, in nsIDOMXULTreeElement tree, in nsIDOMXULElement originalMessage,
in nsIRDFService rdfService, in nsIDOMXULDocument document,
in boolean wrapAround, in boolean isThreaded);
void OpenTreeitemAndDescendants(in nsIDOMNode treeitem);
};

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

@ -36,6 +36,9 @@ var prefs = Components.classes['component://netscape/preferences'].getService();
prefs = prefs.QueryInterface(Components.interfaces.nsIPref);
var showPerformance = prefs.GetBoolPref('mail.showMessengerPerformance');
var msgNavigationService = Components.classes['component://netscape/messenger/msgviewnavigationservice'].getService();
msgNavigationService= msgNavigationService.QueryInterface(Components.interfaces.nsIMsgViewNavigationService);
var gBeforeFolderLoadTime;
function OpenURL(url)
@ -270,11 +273,6 @@ function RerootFolder(uri, newFolder, isThreaded, sortID)
folder.setAttribute('ref', uri);
UpdateStatusMessageCounts(newFolder);
var afterFolderLoadTime = new Date();
var timeToLoad = (afterFolderLoadTime.getTime() - gBeforeFolderLoadTime.getTime())/1000;
if(showPerformance)
dump("Time to load " + uri + " is " + timeToLoad + " seconds\n");
}
@ -614,10 +612,10 @@ function GetNextMessageAfterDelete(messages)
//search forward
while(curMessage)
{
nextMessage = GetNextMessageUnthreaded(tree, curMessage, GoMessage, false);
nextMessage = msgNavigationService.FindNextMessage(navigateAny, tree, curMessage, RDF, document, false, messageView.showThreads);
if(nextMessage)
{
if(!MessageInSelection(nextMessage, messages))
if(nextMessage.getAttribute("selected") != "true")
{
break;
}
@ -633,10 +631,10 @@ function GetNextMessageAfterDelete(messages)
//search forward
while(curMessage)
{
nextMessage = GetPreviousMessage(curMessage, GoMessage, false);
nextMessage = msgNavigationService.FindPreviousMessage(navigateAny, tree, curMessage, RDF, document, false, messageView.showThreads);
if(nextMessage)
{
if(!MessageInSelection(nextMessage, messages))
if(nextMessage.getAttribute("selected") != "true")
{
break;
}
@ -650,18 +648,6 @@ function GetNextMessageAfterDelete(messages)
return nextMessage;
}
function MessageInSelection(message, messages)
{
var count = messages.length;
for(var i = 0; i < count; i++)
{
if(message == messages[i])
return true;
}
return false;
}
function SelectNextMessage(nextMessage)
{

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

@ -137,7 +137,19 @@ var folderListener = {
{
gCurrentLoadingFolderURI = "";
//Now let's select the first new message if there is one
var beforeScrollToNew = new Date();
ScrollToFirstNewMessage();
var afterScrollToNew = new Date();
var timeToScroll = (afterScrollToNew.getTime() - beforeScrollToNew.getTime())/1000;
var afterFolderLoadTime = new Date();
var timeToLoad = (afterFolderLoadTime.getTime() - gBeforeFolderLoadTime.getTime())/1000;
if(showPerformance)
{
dump("Time to load " + uri + " is " + timeToLoad + " seconds\n");
dump("of which scrolling to new is" + timeToScroll + "seconds\n");
}
}
}
@ -162,6 +174,7 @@ function OnLoadMessenger()
AddToSession();
//need to add to session before trying to load start folder otherwise listeners aren't
//set up correctly.
dump('Before load start folder\n');
loadStartFolder();
// FIX ME - later we will be able to use onload from the overlay
@ -281,19 +294,22 @@ function loadStartFolder()
//now find Inbox
var outNumFolders = new Object();
dump('Before getting inbox\n');
var inboxFolder = rootMsgFolder.getFoldersWithFlag(0x1000, 1, outNumFolders);
if(!inboxFolder) return;
dump('We have an inbox\n');
var resource = inboxFolder.QueryInterface(Components.interfaces.nsIRDFResource);
var inboxURI = resource.Value;
dump('InboxURI = ' + inboxURI + '\n');
//first, let's see if it's already in the dom. This will make life easier.
var inbox = document.getElementById(inboxURI);
//if it's not here we will have to make sure it's open.
if(!inbox)
{
dump('There isnt an inbox in the tree yet\n');
}
@ -554,59 +570,13 @@ function ThreadPaneOnClick(event)
if(open == "true")
{
//open all of the children of the treeitem
OpenThread(treeitem);
msgNavigationService.OpenTreeitemAndDescendants(treeitem);
}
dump('clicked on a twisty\n');
}
}
function OpenThread(treeitem)
{
treeitem.setAttribute('notreadytodisplay', 'true');
OpenTreeItemAndDescendants(treeitem);
treeitem.setAttribute('notreadytodisplay', 'false');
}
function OpenTreeItemAndDescendants(treeitem)
{
var open = treeitem.getAttribute('open');
if(open != "true")
{
treeitem.setAttribute('open', 'true');
}
var treeitemChildNodes = treeitem.childNodes;
var numTreeitemChildren = treeitemChildNodes.length;
//if there's only one child then there are no treechildren so close it.
if(numTreeitemChildren == 1)
treeitem.setAttribute('open', '');
else
{
for(var i = 0; i < numTreeitemChildren; i++)
{
var treeitemChild = treeitemChildNodes[i];
if(treeitemChild.nodeName == 'treechildren')
{
var treechildrenChildNodes = treeitemChildNodes[i].childNodes;
var numTreechildrenChildren = treechildrenChildNodes.length;
for(var j = 0; j < numTreechildrenChildren; j++)
{
var treechildrenChild = treechildrenChildNodes[j];
if(treechildrenChild.nodeName == 'treeitem')
{
treechildrenChild.setAttribute('open', 'true');
//Open up all of this items
OpenTreeItemAndDescendants(treechildrenChild);
}
}
}
}
}
}
function ChangeSelection(tree, newSelection)
{

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

@ -20,76 +20,12 @@
/* This file contains the js functions necessary to implement view navigation within the 3 pane. */
function GoMessage(message)
{
return true;
}
// These are the types of navigation you can do
var navigateAny=0;
var navigateUnread = 1;
var navigateFlagged = 2;
var navigateNew = 3;
function ResourceGoMessage(message)
{
return true;
}
function GoUnreadMessage(message)
{
var isUnread = message.getAttribute('IsUnread');
return(isUnread == 'true');
}
function ResourceGoUnreadMessage(message)
{
var isUnreadValue = GetMessageValue(message, "http://home.netscape.com/NC-rdf#IsUnread");
return(isUnreadValue == 'true');
}
function GoFlaggedMessage(message)
{
var flagged = message.getAttribute('Flagged');
return(flagged == 'flagged');
}
function ResourceGoFlaggedMessage(message)
{
var flaggedValue = GetMessageValue(message, "http://home.netscape.com/NC-rdf#Flagged");
return(flaggedValue == 'flagged');
}
function GoNewMessage(message)
{
var status = message.getAttribute('Status');
return(status == 'new');
}
function ResourceGoNewMessage(message)
{
var StatusValue = GetMessageValue(message, "http://home.netscape.com/NC-rdf#Status");
return(StatusValue == 'new');
}
function GetMessageValue(message, propertyURI)
{
var db = GetThreadTree().database;
var propertyResource = RDF.GetResource(propertyURI);
var node = db.GetTarget(message, propertyResource, true);
var literal = node.QueryInterface(Components.interfaces.nsIRDFLiteral);
if(literal)
{
return literal.Value;
}
return null;
}
function GoUnreadThread(messageElement)
{
var messageuri = messageElement.getAttribute('id');
var messageResource = RDF.GetResource(messageuri);
var message = messageResource.QueryInterface(Components.interfaces.nsIMessage);
var folder = message.GetMsgFolder();
var thread = folder.getThreadForMessage(message);
return(thread.numUnreadChildren != 0);
}
/*GoNextMessage finds the message that matches criteria and selects it.
nextFunction is the function that will be used to detertime if a message matches criteria.
@ -101,8 +37,10 @@ function GoUnreadThread(messageElement)
startFromBeginning is a boolean that states whether or not we should start looking at the beginning
if we reach the end
*/
function GoNextMessage(nextFunction, nextResourceFunction, nextThreadFunction, startFromBeginning)
function GoNextMessage(type, startFromBeginning )
{
var beforeGoNextMessage = new Date();
var tree = GetThreadTree();
var selArray = tree.selectedItems;
@ -117,251 +55,18 @@ function GoNextMessage(nextFunction, nextResourceFunction, nextThreadFunction, s
else
currentMessage = selArray[0];
var nextMessage = GetNextMessage(tree, currentMessage, nextFunction, nextResourceFunction, nextThreadFunction, startFromBeginning);
var nextMessage = msgNavigationService.FindNextMessage(type, tree, currentMessage, RDF, document, startFromBeginning, messageView.showThreads);
//Only change the selection if there's a valid nextMessage
if(nextMessage && (nextMessage != currentMessage))
ChangeSelection(tree, nextMessage);
}
}
function GetNextMessage(tree, currentMessage, nextFunction, nextResourceFunction, nextThreadFunction, startFromBeginning)
{
var nextMessage;
if(messageView.showThreads)
{
nextMessage = GetNextMessageInThreads(tree, currentMessage, nextFunction, nextResourceFunction, nextThreadFunction, startFromBeginning);
}
else
{
nextMessage = GetNextMessageUnthreaded(tree, currentMessage, nextFunction, startFromBeginning);
}
return nextMessage;
}
/*GetNextMessageUnthreaded does the iterating for the Next menu item.
currentMessage is the message we are starting from.
nextFunction is the function that will be used to detertime if a message matches criteria.
It must take a node and return a boolean.
startFromBeginning is a boolean that states whether or not we should start looking at the beginning
if we reach then end
*/
function GetNextMessageUnthreaded(tree, currentMessage, nextFunction, startFromBeginning)
{
var foundMessage = false;
var nextMessage;
if(currentMessage)
nextMessage = currentMessage.nextSibling;
else
nextMessage = FindFirstMessage(tree);
//In case we are currently the bottom message
if(!nextMessage && startFromBeginning)
{
dump('no next message\n');
var parent = currentMessage.parentNode;
nextMessage = parent.firstChild;
}
while(nextMessage && (nextMessage != currentMessage))
{
if(nextFunction(nextMessage))
break;
nextMessage = nextMessage.nextSibling;
/*If there's no nextMessage we may have to start from top.*/
if(!nextMessage && (nextMessage!= currentMessage) && startFromBeginning)
{
dump('We need to start from the top\n');
var parent = currentMessage.parentNode;
nextMessage = parent.firstChild;
}
}
if(nextMessage)
{
var id = nextMessage.getAttribute('id');
dump(id + '\n');
}
else
dump('No next message\n');
return nextMessage;
}
function GetNextMessageInThreads(tree, currentMessage, nextFunction, nextResourceFunction, nextThreadFunction, startFromBeginning)
{
var checkStartMessage = false;
//In the case where nothing is selected
if(currentMessage == null)
{
currentMessage = FindFirstMessage(tree);
checkStartMessage = true;
}
return FindNextMessageInThreads(currentMessage, currentMessage, nextFunction, nextResourceFunction, nextThreadFunction, startFromBeginning, checkStartMessage);
}
function FindNextMessageInThreads(startMessage, originalStartMessage, nextFunction, nextResourceFunction, nextThreadFunction, startFromBeginning, checkStartMessage)
{
var nextMessage;
var nextChildMessage;
//First check startMessage if we are supposed to
if(checkStartMessage)
{
if(nextFunction(startMessage))
return startMessage;
}
//if we're on the top level and a thread function has been passed in, we might be able to search faster.
if(startMessage.parentNode.parentNode.nodeName != "treeitem" && nextThreadFunction)
{
var nextTopMessage = FindNextThread(startMessage, nextThreadFunction, startFromBeginning, true);
nextMessage = GetNextInThread(nextTopMessage, nextFunction, nextResourceFunction);
if(nextMessage)
{
return nextMessage;
}
}
//Next, search the current messages children.
nextChildMessage = FindNextInChildren(startMessage, originalStartMessage, nextFunction, nextResourceFunction);
if(nextChildMessage)
return nextChildMessage;
//Next we need to search the current messages siblings
nextMessage = startMessage.nextSibling;
while(nextMessage)
{
//In case we've already been here before
if(nextMessage == originalStartMessage)
return nextMessage;
if(nextFunction(nextMessage))
return nextMessage;
var nextChildMessage = FindNextInChildren(nextMessage, originalStartMessage, nextFunction, nextResourceFunction);
if(nextChildMessage)
return nextChildMessage;
nextMessage = nextMessage.nextSibling;
}
//Finally, we need to find the next of the start message's ancestors that has a sibling
var parentMessage = startMessage.parentNode.parentNode;
while(parentMessage.nodeName == 'treeitem')
{
if(parentMessage.nextSibling != null)
{
nextMessage = FindNextMessageInThreads(parentMessage.nextSibling, originalStartMessage, nextFunction, nextResourceFunction, nextThreadFunction, startFromBeginning, true);
return nextMessage;
}
parentMessage = parentMessage.parentNode.parentNode;
}
//otherwise it's the tree so we need to stop and potentially start from the beginning
if(startFromBeginning)
{
nextMessage = FindNextMessageInThreads(FindFirstMessage(parentMessage), originalStartMessage, nextFunction, nextResourceFunction, nextThreadFunction, false, true);
return nextMessage;
}
return null;
var afterGoNextMessage = new Date();
var timeToGetNext = (afterGoNextMessage.getTime() - beforeGoNextMessage.getTime())/1000;
dump("time to GoNextMessage is " + timeToGetNext + "seconds\n");
}
//Searches children messages in thread navigation.
function FindNextInChildren(parentMessage, originalStartMessage, nextFunction, nextResourceFunction)
{
var isParentOpen = parentMessage.getAttribute('open') == 'true';
//First we'll deal with the case where the parent is open. In this case we can use DOM calls.
if(isParentOpen)
{
//In this case we have treechildren
if(parentMessage.childNodes.length == 2)
{
var treechildren = parentMessage.childNodes[1];
var childMessages = treechildren.childNodes;
var numChildMessages = childMessages.length;
for(var i = 0; i < numChildMessages; i++)
{
var childMessage = childMessages[i];
//If we're at the original message again then stop.
if(childMessage == originalStartMessage)
return childMessage;
if(nextFunction(childMessage))
return childMessage;
else
{
//if this child isn't the message, perhaps one of its children is.
var nextChildMessage = FindNextInChildren(childMessage, originalStartMessage, nextFunction);
if(nextChildMessage)
return nextChildMessage;
}
}
}
}
else
{
//We need to traverse the graph in rdf looking for the next resource that fits what we're searching for.
var parentUri = parentMessage.getAttribute('id');
var parentResource = RDF.GetResource(parentUri);
//If we find one, then we get the id and open up the parent and all of it's children. Then we find the element
//with the id in the document and return that.
if(parentResource)
{
var nextResource = FindNextInChildrenResources(parentResource, nextResourceFunction);
if(nextResource)
{
OpenThread(parentMessage);
var nextUri = nextResource.Value;
var nextChildMessage = document.getElementById(nextUri);
return nextChildMessage;
}
}
}
return null;
}
function FindNextInChildrenResources(parentResource, nextResourceFunction)
{
var db = GetThreadTree().database;
var childrenResource = RDF.GetResource("http://home.netscape.com/NC-rdf#MessageChild");
var childrenEnumerator = db.GetTargets(parentResource, childrenResource, true);
if(childrenEnumerator)
{
while(childrenEnumerator.HasMoreElements())
{
var childResource = childrenEnumerator.GetNext().QueryInterface(Components.interfaces.nsIRDFResource);
if(childResource)
{
if(nextResourceFunction(childResource))
return childResource;
var nextMessageResource = FindNextInChildrenResources(childResource, nextResourceFunction);
if(nextMessageResource)
return nextMessageResource;
}
}
}
return null;
}
/*GoPreviousMessage finds the message that matches criteria and selects it.
previousFunction is the function that will be used to detertime if a message matches criteria.
@ -369,7 +74,7 @@ function FindNextInChildrenResources(parentResource, nextResourceFunction)
startFromEnd is a boolean that states whether or not we should start looking at the end
if we reach the beginning
*/
function GoPreviousMessage(previousFunction, startFromEnd)
function GoPreviousMessage(type, startFromEnd)
{
var tree = GetThreadTree();
@ -377,85 +82,21 @@ function GoPreviousMessage(previousFunction, startFromEnd)
if ( selArray && (selArray.length == 1) )
{
var currentMessage = selArray[0];
var previousMessage = GetPreviousMessage(currentMessage, previousFunction, startFromEnd);
var previousMessage = msgNavigationService.FindPreviousMessage(type, tree, currentMessage, RDF, document, startFromEnd, messageView.showThreads);
//Only change selection if there's a valid previous message.
if(previousMessage && (previousMessage != currentMessage))
ChangeSelection(tree, previousMessage);
}
}
/*GetPreviousMessage does the iterating for the Previous menu item.
currentMessage is the message we are starting from.
previousFunction is the function that will be used to detertime if a message matches criteria.
It must take a node and return a boolean.
startFromEnd is a boolean that states whether or not we should start looking at the end
if we reach then beginning
*/
function GetPreviousMessage(currentMessage, previousFunction, startFromEnd)
{
var foundMessage = false;
var previousMessage = currentMessage.previousSibling;
//In case we're already at the top
if(!previousMessage && startFromEnd)
{
var parent = currentMessage.parentNode;
previousMessage = parent.lastChild;
}
while(previousMessage && (previousMessage != currentMessage))
{
if(previousFunction(previousMessage))
break;
previousMessage = previousMessage.previousSibling;
if(!previousMessage && startFromEnd)
{
var parent = currentMessage.parentNode;
previousMessage = parent.lastChild;
}
}
if(previousMessage)
{
var id = previousMessage.getAttribute('id');
dump(id + '\n');
}
else
dump('No previous message\n');
return previousMessage;
}
function FindFirstMessage(tree)
{
//getElementsByTagName is too slow which is why I'm using this loop. Just find the first
//child of the tree that has the 'treechildren' tag and return it's first child. This will
//be the first message.
var children = tree.childNodes;
var numChildren = children.length;
for(var i = 0; i < numChildren; i++)
{
if(children[i].nodeName == 'treechildren')
{
return children[i].firstChild;
}
}
return null;
}
// nextThreadFunction is the function that determines whether a top level message is part of a thread that fits criteria.
// nextMessageFunction is the function that would be used to find the next message in a thread if gotoNextInThread is true
// nextResourceFunction is the function that would be used to find the next message in a thread as a resource if gotoNextInThread is true
// type is the the type of the next thread we are looking for.
// startFromBeginning is true if we should start looking from the beginning after we get to the end of the thread pane.
// gotoNextInThread is true if once we find an unrad thread we should select the first message in that thread that fits criteria
function GoNextThread(nextThreadFunction, nextMessageFunction, nextResourceFunction, startFromBeginning, gotoNextInThread)
function GoNextThread(type, startFromBeginning, gotoNextInThread)
{
if(messageView.showThreads)
@ -491,13 +132,14 @@ function GoNextThread(nextThreadFunction, nextMessageFunction, nextResourceFunct
checkCurrentTopMessage = true;
}
var nextTopMessage = FindNextThread(currentTopMessage, nextThreadFunction, startFromBeginning, checkCurrentTopMessage);
var nextTopMessage = msgNavigationService.FindNextThread(type, tree, currentTopMessage, RDF, document, startFromBeginning, checkCurrentTopMessage);
var changeSelection = (nextTopMessage != null && ((currentTopMessage != nextTopMessage) || checkCurrentTopMessage));
if(changeSelection)
{
if(gotoNextInThread)
{
nextMessage = GetNextInThread(nextTopMessage, nextMessageFunction, nextResourceFunction);
nextMessage = msgNavigationService.FindNextInThread(type, tree, nextTopMessage, RDF, document);
ChangeSelection(tree, nextMessage);
}
else
@ -508,22 +150,6 @@ function GoNextThread(nextThreadFunction, nextMessageFunction, nextResourceFunct
}
//Given the top level message of a thread and the searching functions, this returns the first message in that thread that matches
//the criteria
function GetNextInThread(topMessage, nextMessageFunction, nextResourceFunction)
{
var nextMessage;
if(nextMessageFunction(topMessage))
nextMessage = topMessage;
else
{
nextMessage = FindNextInChildren(topMessage, null, nextMessageFunction, nextResourceFunction);
}
return nextMessage;
}
function FindTopLevelMessage(startMessage)
{
var currentTop = startMessage;
@ -538,54 +164,58 @@ function FindTopLevelMessage(startMessage)
return currentTop;
}
function FindNextThread(startThread, nextThreadFunction, startFromBeginning, checkStartThread)
{
if(checkStartThread)
{
if(nextThreadFunction(startThread))
return startThread;
dump("start thread doesn't match\n");
}
var nextThread = startThread.nextSibling;
//In case we are currently the bottom message
if(!nextThread && startFromBeginning)
{
var parent = startThread.parentNode;
nextThread = parent.firstChild;
}
while(nextThread && (nextThread != startThread))
{
if(nextThreadFunction(nextThread))
{
break;
}
nextThread = nextThread.nextSibling;
/*If there's no nextMessage we may have to start from top.*/
if(!nextThread && (nextThread!= startThread) && startFromBeginning)
{
var parent = startThread.parentNode;
nextThread = parent.firstChild;
}
}
return nextThread;
}
function ScrollToFirstNewMessage()
{
var tree = GetThreadTree();
var treeFolder = GetThreadTreeFolder();
var newMessage = GetNextMessage(tree, null, GoNewMessage, ResourceGoNewMessage, null, false)
if(newMessage)
var folderURI = treeFolder.getAttribute('ref');
var folderResource = RDF.GetResource(folderURI);
var folder = folderResource.QueryInterface(Components.interfaces.nsIMsgFolder);
var hasNew = folder.hasNewMessages();
if(hasNew)
{
tree.ensureElementIsVisible(newMessage);
var newMessage = folder.firstNewMessage;
if(messageView.showThreads)
{
//if we're in thread mode, then we need to actually make sure the message is showing.
var topLevelMessage = GetTopLevelMessageForMessage(newMessage, folder);
var topLevelResource = topLevelMessage.QueryInterface(Components.interfaces.nsIRDFResource);
var topLevelURI = topLevelResource.Value;
var topElement = document.getElementById(topLevelURI);
if(topElement)
{
msgNavigationService.OpenTreeitemAndDescendants(topElement);
}
}
var messageResource = newMessage.QueryInterface(Components.interfaces.nsIRDFResource);
var messageURI = messageResource.Value;
var messageElement = document.getElementById(messageURI);
if(messageElement)
{
tree.ensureElementIsVisible(messageElement);
}
}
}
function GetTopLevelMessageForMessage(message, folder)
{
if(!folder)
folder = message.GetMsgFolder();
var thread = folder.getThreadForMessage(message);
var outIndex = new Object();
var rootHdr = thread.GetRootHdr(outIndex);
var topMessage = folder.createMessageFromMsgDBHdr(rootHdr);
return topMessage;
}

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

@ -958,36 +958,36 @@ function MsgStop() {
function MsgNextMessage()
{
GoNextMessage(GoMessage, ResourceGoMessage, null, false);
GoNextMessage(navigateAny, false );
}
function MsgNextUnreadMessage()
{
GoNextMessage(GoUnreadMessage, ResourceGoUnreadMessage, GoUnreadThread, true);
GoNextMessage(navigateUnread, true);
}
function MsgNextFlaggedMessage()
{
GoNextMessage(GoFlaggedMessage, ResourceGoFlaggedMessage, null, true);
GoNextMessage(navigateFlagged, true);
}
function MsgNextUnreadThread()
{
GoNextThread(GoUnreadThread, GoUnreadMessage, ResourceGoUnreadMessage, true, true);
GoNextThread(navigateUnread, true, true);
}
function MsgPreviousMessage()
{
GoPreviousMessage(GoMessage, false);
GoPreviousMessage(navigateAny, false);
}
function MsgPreviousUnreadMessage()
{
GoPreviousMessage(GoUnreadMessage, true);
GoPreviousMessage(navigateUnread, true);
}
function MsgPreviousFlaggedMessage()
{
GoPreviousMessage(GoFlaggedMessage, true);
GoPreviousMessage(navigateFlagged, true);
}
function MsgGoBack() {}

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

@ -39,6 +39,8 @@ nsMsgCopyService.h
nsMsgStatusFeedback.h
nsMessageView.h
nsMsgWindow.h
nsMsgViewNavigationService.h

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

@ -54,6 +54,7 @@ CPPSRCS = \
nsMessageView.cpp \
nsMsgWindow.cpp \
nsMessengerMigrator.cpp \
nsMsgViewNavigationService.cpp \
$(NULL)
EXPORTS = \
@ -78,6 +79,7 @@ EXPORTS = \
nsMessageView.h \
nsMsgWindow.h \
nsMessengerMigrator.h \
nsMsgViewNavigationService.h \
$(NULL)
# we don't want the shared lib, but we want to force the creation of a static lib.

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

@ -50,6 +50,7 @@ EXPORTS= \
nsMessageView.h \
nsMsgWindow.h \
nsMessengerMigrator.h \
nsMsgViewNavigationService.h \
$(NULL)
@ -82,6 +83,7 @@ CPP_OBJS= \
.\$(OBJDIR)\nsMessageView.obj \
.\$(OBJDIR)\nsMsgWindow.obj \
.\$(OBJDIR)\nsMessengerMigrator.obj \
.\$(OBJDIR)\nsMsgViewNavigationService.obj \
$(NULL)

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -0,0 +1,59 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Netscape are
* Copyright (C) 2000 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#ifndef _nsMsgViewNavigationService_h
#define _nsMsgViewNavigationService_h
#include "nsIMsgViewNavigationService.h"
typedef struct infoStruct *navigationInfoPtr;
class nsMsgViewNavigationService : public nsIMsgViewNavigationService {
public:
NS_DECL_ISUPPORTS
nsMsgViewNavigationService();
virtual ~nsMsgViewNavigationService();
nsresult Init();
NS_DECL_NSIMSGVIEWNAVIGATIONSERVICE
protected:
nsresult CreateNavigationInfo(PRInt32 type, nsIDOMXULTreeElement *tree, nsIDOMNode *originalMessage, nsIRDFService *rdfService, nsIDOMXULDocument *document, PRBool wrapAround, PRBool isThreaded, PRBool checkStartMessage, navigationInfoPtr *info);
nsresult FindNextMessageUnthreaded(navigationInfoPtr info, nsIDOMNode **nextMessage);
nsresult FindNextMessageInThreads(nsIDOMNode *startMessage, navigationInfoPtr info, nsIDOMNode **nextMessage);
nsresult FindNextInChildren(nsIDOMNode *parent, navigationInfoPtr info, nsIDOMNode **nextMessage);
nsresult FindNextInChildrenResources(nsIRDFResource *parentResource, navigationInfoPtr info, nsIRDFResource **nextResource);
nsresult GetParentMessage(nsIDOMNode *message, nsIDOMNode **parentMessage);
nsresult GetNextThread(navigationInfoPtr info, nsIDOMNode **nextThread);
nsresult GetNextInThread(navigationInfoPtr info, nsIDOMNode **nextMessage);
nsresult FindPreviousMessage(navigationInfoPtr info, nsIDOMNode **previousMessage);
nsresult GetNextMessageByThread(nsIDOMXULElement *startElement, navigationInfoPtr info, nsIDOMNode **nextMessage);
nsresult FindNextInThreadSiblings(nsIDOMNode *startMessage, navigationInfoPtr info, nsIDOMNode **nextMessage);
nsresult FindNextInAncestors(nsIDOMNode *startMessage, navigationInfoPtr info, nsIDOMNode **nextMessage);
};
#endif

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

@ -236,6 +236,55 @@ NS_IMETHODIMP nsMsgDBFolder::SetCharset(const PRUnichar * aCharset)
return rv;
}
NS_IMETHODIMP nsMsgDBFolder::HasNewMessages(PRBool *hasNewMessages)
{
if(!hasNewMessages)
return NS_ERROR_NULL_POINTER;
nsresult rv = GetDatabase(nsnull);
if(NS_SUCCEEDED(rv))
{
rv = mDatabase->HasNew(hasNewMessages);
}
return rv;
}
NS_IMETHODIMP nsMsgDBFolder::GetFirstNewMessage(nsIMessage **firstNewMessage)
{
nsresult rv = GetDatabase(nsnull);
if(NS_SUCCEEDED(rv))
{
nsMsgKey key;
rv = mDatabase->GetFirstNew(&key);
if(NS_FAILED(rv))
return rv;
nsCOMPtr<nsIMsgDBHdr> hdr;
rv = mDatabase->GetMsgHdrForKey(key, getter_AddRefs(hdr));
if(NS_FAILED(rv))
return rv;
rv = CreateMessageFromMsgDBHdr(hdr, firstNewMessage);
if(NS_FAILED(rv))
return rv;
}
return rv;
}
NS_IMETHODIMP nsMsgDBFolder::ClearNewMessages()
{
nsresult rv = GetDatabase(nsnull);
if(NS_SUCCEEDED(rv))
{
rv = mDatabase->ClearNewList(PR_FALSE);
}
return rv;
}
nsresult nsMsgDBFolder::ReadDBFolderInfo(PRBool force)
{
// Since it turns out to be pretty expensive to open and close

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

@ -54,6 +54,9 @@ public:
NS_IMETHOD HasMessage(nsIMessage *message, PRBool *hasMessage);
NS_IMETHOD GetCharset(PRUnichar * *aCharset);
NS_IMETHOD SetCharset(const PRUnichar * aCharset);
NS_IMETHOD HasNewMessages(PRBool *hasNewMessages);
NS_IMETHOD GetFirstNewMessage(nsIMessage **firstNewMessage);
NS_IMETHOD ClearNewMessages();
NS_IMETHOD GetMsgDatabase(nsIMsgDatabase** aMsgDatabase);

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

@ -1214,10 +1214,6 @@ NS_IMETHODIMP nsMsgFolder::GetTotalMessages(PRBool deep, PRInt32 *totalMessages)
return NS_OK;
}
#ifdef HAVE_DB
NS_IMETHOD GetTotalMessagesInDB(PRUint32 *totalMessages) const; // How many messages in database.
#endif
PRInt32 nsMsgFolder::GetNumPendingUnread()
{
return mNumPendingUnreadMessages;
@ -1228,6 +1224,23 @@ PRInt32 nsMsgFolder::GetNumPendingTotalMessages()
return mNumPendingTotalMessages;
}
NS_IMETHODIMP nsMsgFolder::HasNewMessages(PRBool *hasNewMessages)
{
//we don't support this
return NS_OK;
}
NS_IMETHODIMP nsMsgFolder::GetFirstNewMessage(nsIMessage **firstNewMessage)
{
//we don't support this
return NS_OK;
}
NS_IMETHODIMP nsMsgFolder::ClearNewMessages()
{
//we don't support this
return NS_OK;
}
void nsMsgFolder::ChangeNumPendingUnread(PRInt32 delta)
{

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

@ -101,6 +101,9 @@ public:
NS_IMETHOD SummaryChanged(void);
NS_IMETHOD GetNumUnread(PRBool deep, PRInt32 *_retval);
NS_IMETHOD GetTotalMessages(PRBool deep, PRInt32 *_retval);
NS_IMETHOD HasNewMessages(PRBool *hasNewMessages);
NS_IMETHOD GetFirstNewMessage(nsIMessage **firstNewMessage);
NS_IMETHOD ClearNewMessages();
NS_IMETHOD GetExpungedBytesCount(PRUint32 *aExpungedBytesCount);
NS_IMETHOD GetDeletable(PRBool *aDeletable);
NS_IMETHOD GetRequiresCleanup(PRBool *aRequiresCleanup);