diff --git a/mailnews/base/build/nsMsgBaseCID.h b/mailnews/base/build/nsMsgBaseCID.h index 5961e633ecca..37823f705dfa 100644 --- a/mailnews/base/build/nsMsgBaseCID.h +++ b/mailnews/base/build/nsMsgBaseCID.h @@ -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__ diff --git a/mailnews/base/build/nsMsgFactory.cpp b/mailnews/base/build/nsMsgFactory.cpp index 50774391d45a..394932bb7aa9 100644 --- a/mailnews/base/build/nsMsgFactory.cpp +++ b/mailnews/base/build/nsMsgFactory.cpp @@ -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 mMsgStatusFeedbackFactory; nsCOMPtr mMessageViewFactory; nsCOMPtr mMsgWindowFactory; + nsCOMPtr 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} }; diff --git a/mailnews/base/public/MANIFEST_IDL b/mailnews/base/public/MANIFEST_IDL index 8251fa340189..f72f44075aa7 100644 --- a/mailnews/base/public/MANIFEST_IDL +++ b/mailnews/base/public/MANIFEST_IDL @@ -36,3 +36,5 @@ nsIMsgProtocolInfo.idl nsIMsgRDFDataSource.idl nsIIncomingServerListener.idl nsIMsgHdr.idl +nsIMsgViewNavigationService.idl + diff --git a/mailnews/base/public/Makefile.in b/mailnews/base/public/Makefile.in index ad0be0c6f9cc..9ae955c23621 100644 --- a/mailnews/base/public/Makefile.in +++ b/mailnews/base/public/Makefile.in @@ -72,6 +72,7 @@ XPIDLSRCS = \ nsIIncomingServerListener.idl \ nsIMsgHdr.idl \ nsIMessengerMigrator.idl \ + nsIMsgViewNavigationService.idl \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/mailnews/base/public/makefile.win b/mailnews/base/public/makefile.win index 21a31b6666a5..f0a73ef70a7b 100644 --- a/mailnews/base/public/makefile.win +++ b/mailnews/base/public/makefile.win @@ -57,6 +57,7 @@ XPIDLSRCS = \ .\nsIMsgRDFDataSource.idl \ .\nsIIncomingServerListener.idl \ .\nsIMsgHdr.idl \ + .\nsIMsgViewNavigationService.idl \ $(NULL) ################################################################################ diff --git a/mailnews/base/public/nsIMsgFolder.idl b/mailnews/base/public/nsIMsgFolder.idl index 94969314afa1..f461d9a60057 100644 --- a/mailnews/base/public/nsIMsgFolder.idl +++ b/mailnews/base/public/nsIMsgFolder.idl @@ -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 diff --git a/mailnews/base/public/nsIMsgViewNavigationService.idl b/mailnews/base/public/nsIMsgViewNavigationService.idl new file mode 100644 index 000000000000..a911cc8dc432 --- /dev/null +++ b/mailnews/base/public/nsIMsgViewNavigationService.idl @@ -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); +}; + diff --git a/mailnews/base/resources/content/commandglue.js b/mailnews/base/resources/content/commandglue.js index c466a61522a0..3a14a7e6c72c 100644 --- a/mailnews/base/resources/content/commandglue.js +++ b/mailnews/base/resources/content/commandglue.js @@ -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) { diff --git a/mailnews/base/resources/content/msgMail3PaneWindow.js b/mailnews/base/resources/content/msgMail3PaneWindow.js index d23ef2e56586..bffbd59cf77e 100644 --- a/mailnews/base/resources/content/msgMail3PaneWindow.js +++ b/mailnews/base/resources/content/msgMail3PaneWindow.js @@ -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) { diff --git a/mailnews/base/resources/content/msgViewNavigation.js b/mailnews/base/resources/content/msgViewNavigation.js index ce7bfd35491a..d5d45092c5e2 100644 --- a/mailnews/base/resources/content/msgViewNavigation.js +++ b/mailnews/base/resources/content/msgViewNavigation.js @@ -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; + +} diff --git a/mailnews/base/resources/content/widgetglue.js b/mailnews/base/resources/content/widgetglue.js index c142ca9fbec5..670bded3dedc 100644 --- a/mailnews/base/resources/content/widgetglue.js +++ b/mailnews/base/resources/content/widgetglue.js @@ -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() {} diff --git a/mailnews/base/src/MANIFEST b/mailnews/base/src/MANIFEST index e05a6d3a9195..679d7206801e 100644 --- a/mailnews/base/src/MANIFEST +++ b/mailnews/base/src/MANIFEST @@ -39,6 +39,8 @@ nsMsgCopyService.h nsMsgStatusFeedback.h nsMessageView.h nsMsgWindow.h +nsMsgViewNavigationService.h + diff --git a/mailnews/base/src/Makefile.in b/mailnews/base/src/Makefile.in index e40673536d0d..2a45cbe77be8 100644 --- a/mailnews/base/src/Makefile.in +++ b/mailnews/base/src/Makefile.in @@ -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. diff --git a/mailnews/base/src/makefile.win b/mailnews/base/src/makefile.win index 367fb069e030..754a08f76d36 100644 --- a/mailnews/base/src/makefile.win +++ b/mailnews/base/src/makefile.win @@ -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) diff --git a/mailnews/base/src/nsMsgViewNavigationService.cpp b/mailnews/base/src/nsMsgViewNavigationService.cpp new file mode 100644 index 000000000000..96d9e952cfc9 --- /dev/null +++ b/mailnews/base/src/nsMsgViewNavigationService.cpp @@ -0,0 +1,1234 @@ +/* -*- 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): + */ + +#include "nsMsgViewNavigationService.h" +#include "nsCOMPtr.h" +#include "nsIDOMNodeList.h" +#include "nsIRDFCompositeDataSource.h" +#include "nsXPIDLString.h" +#include "nsIMessage.h" +#include "nsIMsgFolder.h" +#include "nsIMsgThread.h" + + +typedef PRBool (*navigationFunction)(nsIDOMXULElement *message, navigationInfoPtr info); +typedef PRBool (*navigationResourceFunction)(nsIRDFResource *message, navigationInfoPtr info); +//struct for keeping track of all info related to a navigation request. +typedef struct infoStruct +{ + navigationFunction navFunction; + navigationFunction navThreadFunction; + navigationResourceFunction navResourceFunction; + PRBool isThreaded; + nsCOMPtr tree; + nsCOMPtr originalMessage; + PRBool wrapAround; + PRBool checkStartMessage; + PRInt32 type; + nsCOMPtr rdfService; + nsCOMPtr document; +} navigationInfo; + +//navigation functions +static PRBool AnyMessageNavigationFunction(nsIDOMXULElement *message, navigationInfoPtr info) +{ + return PR_TRUE; +} + +static PRBool UnreadMessageNavigationFunction(nsIDOMXULElement *message, navigationInfoPtr info) +{ + nsAutoString unreadStr("IsUnread"); + nsAutoString resultStr; + + message->GetAttribute(unreadStr, resultStr); + return(resultStr == "true"); +} + +static PRBool FlaggedMessageNavigationFunction(nsIDOMXULElement *message, navigationInfoPtr info) +{ + nsAutoString flaggedStr("Flagged"); + nsAutoString resultStr; + + message->GetAttribute(flaggedStr, resultStr); + return(resultStr == "flagged"); +} + +static PRBool NewMessageNavigationFunction(nsIDOMXULElement *message, navigationInfoPtr info) +{ + nsAutoString statusStr("Status"); + nsAutoString resultStr; + + message->GetAttribute(statusStr, resultStr); + return(resultStr == "new"); +} + +//resource functions +static nsresult GetMessageValue(nsIRDFResource *message, nsString& propertyURI, nsString& value, navigationInfoPtr info) +{ + nsresult rv; + + nsCOMPtr db; + rv = info->tree->GetDatabase(getter_AddRefs(db)); + if(NS_FAILED(rv)) + return rv; + + nsCOMPtr propertyResource; + rv = info->rdfService->GetResource(nsCAutoString(propertyURI), getter_AddRefs(propertyResource)); + if(NS_FAILED(rv)) + return rv; + + nsCOMPtr node; + rv = db->GetTarget(message, propertyResource, PR_TRUE, getter_AddRefs(node)); + if(NS_FAILED(rv)) + return rv; + + nsCOMPtr literal = do_QueryInterface(node); + if(!literal) + return NS_ERROR_FAILURE; + + if(literal) + { + nsXPIDLString valueStr; + + rv = literal->GetValue(getter_Copies(valueStr)); + if(NS_SUCCEEDED(rv)) + value = valueStr; + } + return rv; +} + +static PRBool AnyMessageNavigationResourceFunction(nsIRDFResource *message, navigationInfoPtr info) +{ + + return PR_TRUE; +} + +static PRBool UnreadMessageNavigationResourceFunction(nsIRDFResource *message, navigationInfoPtr info) +{ + nsresult rv; + nsAutoString isUnreadValue; + nsAutoString unreadProperty("http://home.netscape.com/NC-rdf#IsUnread"); + + rv = GetMessageValue(message, unreadProperty, isUnreadValue, info); + if(NS_FAILED(rv)) + return PR_FALSE; + + return(isUnreadValue == "true"); +} + +static PRBool FlaggedMessageNavigationResourceFunction(nsIRDFResource *message, navigationInfoPtr info) +{ + nsresult rv; + nsAutoString flaggedValue; + nsAutoString flaggedProperty("http://home.netscape.com/NC-rdf#Flagged"); + + rv = GetMessageValue(message, flaggedProperty, flaggedValue, info); + if(NS_FAILED(rv)) + return PR_FALSE; + + return(flaggedValue == "flagged"); +} + +static PRBool NewMessageNavigationResourceFunction(nsIRDFResource *message, navigationInfoPtr info) +{ + nsresult rv; + nsAutoString statusValue; + nsAutoString statusProperty("http://home.netscape.com/NC-rdf#Status"); + + rv = GetMessageValue(message, statusProperty, statusValue, info); + if(NS_FAILED(rv)) + return PR_FALSE; + + return(statusValue == "new"); +} + +//Thread navigation functions. +static PRBool UnreadThreadNavigationFunction(nsIDOMXULElement *messageElement, navigationInfoPtr info) +{ + nsresult rv; + + nsAutoString idAttribute("id"); + nsAutoString idResult; + + rv = messageElement->GetAttribute(idAttribute, idResult); + if(NS_FAILED(rv)) + return PR_FALSE; + + nsCOMPtr messageResource; + rv = info->rdfService->GetResource(nsCAutoString(idResult), getter_AddRefs(messageResource)); + if(NS_FAILED(rv)) + return PR_FALSE; + + nsCOMPtr message = do_QueryInterface(messageResource); + if(!message) + return PR_FALSE; + + nsCOMPtr folder; + rv = message->GetMsgFolder(getter_AddRefs(folder)); + if(NS_FAILED(rv)) + return PR_FALSE; + + nsCOMPtr thread; + rv = folder->GetThreadForMessage(message, getter_AddRefs(thread)); + if(NS_FAILED(rv)) + return PR_FALSE; + + PRUint32 numUnreadChildren; + rv = thread->GetNumUnreadChildren(&numUnreadChildren); + if(NS_FAILED(rv)) + return PR_FALSE; + + return(numUnreadChildren != 0); +} + +NS_IMPL_ISUPPORTS1(nsMsgViewNavigationService, nsIMsgViewNavigationService) + +nsMsgViewNavigationService::nsMsgViewNavigationService() +{ + NS_INIT_ISUPPORTS(); +} + +nsMsgViewNavigationService::~nsMsgViewNavigationService() +{ +} + +nsresult nsMsgViewNavigationService::Init() +{ + return NS_OK; +} + +//Finds the next message after originalMessage based on the type +NS_IMETHODIMP nsMsgViewNavigationService::FindNextMessage(PRInt32 type, nsIDOMXULTreeElement *tree, nsIDOMXULElement *originalMessage, nsIRDFService *rdfService, nsIDOMXULDocument *document, PRBool wrapAround, PRBool isThreaded, nsIDOMXULElement ** nextMessage) +{ + nsresult rv = NS_OK; + + PRBool checkStartMessage; + nsCOMPtr originalMessageNode; + + if(originalMessage) + { + checkStartMessage = PR_FALSE; + rv = originalMessage->QueryInterface(nsIDOMNode::GetIID(), getter_AddRefs(originalMessageNode)); + if(NS_FAILED(rv)) + return rv; + } + else + { + nsCOMPtr firstMessage; + rv = FindFirstMessage(tree, getter_AddRefs(firstMessage)); + if(NS_FAILED(rv)) + return rv; + + originalMessageNode = do_QueryInterface(firstMessage); + if(!originalMessageNode) + return NS_ERROR_FAILURE; + + checkStartMessage = PR_TRUE; + } + + navigationInfoPtr info; + rv = CreateNavigationInfo(type, tree, originalMessageNode, rdfService, document, wrapAround, isThreaded, checkStartMessage, &info); + if(NS_FAILED(rv)) + return rv; + *nextMessage = nsnull; + + nsCOMPtr next; + if(!isThreaded) + { + rv = FindNextMessageUnthreaded(info, getter_AddRefs(next)); + } + else + { + rv = FindNextMessageInThreads(info->originalMessage, info, getter_AddRefs(next)); + } + + if(next) + { + rv = next->QueryInterface(nsIDOMXULElement::GetIID(), (void**) nextMessage); + if(NS_FAILED(rv)) + { + delete info; + return rv; + } + } + + delete info; + return rv; +} + +//Finds the previous message before the original message based on the type. +NS_IMETHODIMP nsMsgViewNavigationService::FindPreviousMessage(PRInt32 type, nsIDOMXULTreeElement *tree, nsIDOMXULElement *originalMessage, nsIRDFService *rdfService, nsIDOMXULDocument *document, PRBool wrapAround, PRBool isThreaded, nsIDOMXULElement ** previousMessage) +{ + nsresult rv = NS_OK; + + PRBool checkStartMessage; + nsCOMPtr originalMessageNode; + + if(originalMessage) + { + checkStartMessage = PR_FALSE; + rv = originalMessage->QueryInterface(nsIDOMNode::GetIID(), getter_AddRefs(originalMessageNode)); + if(NS_FAILED(rv)) + return rv; + } + + navigationInfoPtr info; + rv = CreateNavigationInfo(type, tree, originalMessageNode, rdfService, document, wrapAround, isThreaded, checkStartMessage, &info); + if(NS_FAILED(rv)) + return rv; + *previousMessage = nsnull; + + nsCOMPtr previous; + + rv = FindPreviousMessage(info, getter_AddRefs(previous)); + + if(previous) + { + rv = previous->QueryInterface(nsIDOMXULElement::GetIID(), (void**) previousMessage); + if(NS_FAILED(rv)) + { + delete info; + return rv; + } + } + + delete info; + return rv; +} + +//Finds the next thread after the thread the original Message is in based on the type. +NS_IMETHODIMP nsMsgViewNavigationService::FindNextThread(PRInt32 type, nsIDOMXULTreeElement *tree, nsIDOMXULElement *originalMessage, nsIRDFService *rdfService, nsIDOMXULDocument *document, PRBool wrapAround, PRBool checkOriginalMessage, nsIDOMXULElement ** nextThread) +{ + nsresult rv = NS_OK; + nsCOMPtr originalMessageNode = do_QueryInterface(originalMessage); + if(!originalMessageNode) + return NS_ERROR_FAILURE; + + navigationInfoPtr info; + rv = CreateNavigationInfo(type, tree, originalMessageNode, rdfService, document, wrapAround, PR_TRUE, checkOriginalMessage, &info); + if(NS_FAILED(rv)) + return rv; + *nextThread = nsnull; + + nsCOMPtr next; + rv = GetNextThread(info, getter_AddRefs(next)); + if(NS_FAILED(rv)) + { + delete info; + return rv; + } + + if(next) + { + rv = next->QueryInterface(nsIDOMXULElement::GetIID(), (void**)nextThread); + if(NS_FAILED(rv)) + { + delete info; + return rv; + } + } + delete info; + return NS_OK; + +} + +//Given a top level message, returns the first message in the thread that matches the type. +NS_IMETHODIMP nsMsgViewNavigationService::FindNextInThread(PRInt32 type, nsIDOMXULTreeElement *tree, nsIDOMXULElement *originalMessage, nsIRDFService *rdfService, nsIDOMXULDocument *document, nsIDOMXULElement ** nextMessage) +{ + nsresult rv = NS_OK; + nsCOMPtr originalMessageNode = do_QueryInterface(originalMessage); + if(!originalMessageNode) + return NS_ERROR_FAILURE; + + navigationInfoPtr info; + rv = CreateNavigationInfo(type, tree, originalMessageNode, rdfService, document, PR_FALSE, PR_TRUE, PR_TRUE, &info); + if(NS_FAILED(rv)) + return rv; + *nextMessage = nsnull; + + nsCOMPtr next; + rv = GetNextInThread(info, getter_AddRefs(next)); + if(NS_FAILED(rv)) + { + delete info; + return rv; + } + + if(next) + { + rv = next->QueryInterface(nsIDOMXULElement::GetIID(), (void**)nextMessage); + if(NS_FAILED(rv)) + { + delete info; + return rv; + } + } + delete info; + return NS_OK; +} + +//Finds the first message in the tree. +NS_IMETHODIMP nsMsgViewNavigationService::FindFirstMessage(nsIDOMXULTreeElement *tree, nsIDOMXULElement ** firstMessage) +{ + nsresult rv; + nsCOMPtr children; + PRUint32 numChildren; + + rv = tree->GetChildNodes(getter_AddRefs(children)); + if(NS_FAILED(rv)) + return rv; + + rv = children->GetLength(&numChildren); + if(NS_FAILED(rv)) + return rv; + + for(PRUint32 i = 0; i < numChildren; i++) + { + nsCOMPtr child; + rv = children->Item(i, getter_AddRefs(child)); + if(NS_FAILED(rv)) + return rv; + + nsAutoString nodeName; + rv = child->GetNodeName(nodeName); + if(NS_FAILED(rv)) + return rv; + + if(nodeName == "treechildren") + { + //Get the treechildren's first child. This is the first message + nsCOMPtr firstChild; + rv = child->GetFirstChild(getter_AddRefs(firstChild)); + if(NS_FAILED(rv)) + return rv; + + rv = firstChild->QueryInterface(nsIDOMXULElement::GetIID(), (void**)firstMessage); + return rv; + } + + } + + *firstMessage = nsnull; + return NS_OK; + + +} + +//Creates the navigationInfo structure that contains all of the data necessary to do the navigation. +nsresult nsMsgViewNavigationService::CreateNavigationInfo(PRInt32 type, nsIDOMXULTreeElement *tree, nsIDOMNode *originalMessage, + nsIRDFService *rdfService, nsIDOMXULDocument *document, + PRBool wrapAround, PRBool isThreaded, PRBool checkStartMessage, + navigationInfoPtr *info) +{ + + navigationInfoPtr navInfo = new navigationInfo; + if(!navInfo) + return NS_ERROR_OUT_OF_MEMORY; + + navInfo->type = type; + navInfo->tree = tree; + + navInfo->wrapAround = wrapAround; + navInfo->isThreaded = isThreaded; + navInfo->navFunction = nsnull; + navInfo->navThreadFunction = nsnull; + navInfo->navResourceFunction = nsnull; + navInfo->rdfService = rdfService; + navInfo->document = document; + navInfo->checkStartMessage = checkStartMessage; + navInfo->originalMessage = originalMessage; + + if(type == nsIMsgViewNavigationService::eNavigateUnread) + { + navInfo->navFunction = UnreadMessageNavigationFunction; + navInfo->navResourceFunction = UnreadMessageNavigationResourceFunction; + navInfo->navThreadFunction = UnreadThreadNavigationFunction; + } + else if(type == nsIMsgViewNavigationService::eNavigateNew) + { + navInfo->navFunction = NewMessageNavigationFunction; + navInfo->navResourceFunction = NewMessageNavigationResourceFunction; + } + else if(type == nsIMsgViewNavigationService::eNavigateFlagged) + { + navInfo->navFunction = FlaggedMessageNavigationFunction; + navInfo->navResourceFunction = FlaggedMessageNavigationResourceFunction; + } + else if(type == nsIMsgViewNavigationService::eNavigateAny) + { + navInfo->navFunction = AnyMessageNavigationFunction; + navInfo->navResourceFunction = AnyMessageNavigationResourceFunction; + } + + *info = navInfo; + return NS_OK; +} + +//Finds the next message in an unthreaded view. +nsresult nsMsgViewNavigationService::FindNextMessageUnthreaded(navigationInfoPtr info, nsIDOMNode **nextMessage) +{ + + nsCOMPtr next; + nsresult rv; + + //if we have to check the start message, check it now. + if(info->checkStartMessage) + { + nsCOMPtr originalElement = do_QueryInterface(info->originalMessage); + if(!originalElement) + return NS_ERROR_FAILURE; + + if(info->navFunction(originalElement, info)) + { + *nextMessage = info->originalMessage; + NS_IF_ADDREF(*nextMessage); + return NS_OK; + } + } + + rv = info->originalMessage->GetNextSibling(getter_AddRefs(next)); + if(NS_FAILED(rv)) + return rv; + + //In case we are currently the bottom message + if(!next && info->wrapAround) + { + nsCOMPtr parent; + rv = info->originalMessage->GetParentNode(getter_AddRefs(parent)); + if(NS_FAILED(rv)) + return rv; + + rv = parent->GetFirstChild(getter_AddRefs(next)); + if(NS_FAILED(rv)) + return rv; + } + + while(next && (next.get() != info->originalMessage.get())) + { + nsCOMPtr nextElement = do_QueryInterface(next); + if(!nextElement) + return NS_ERROR_FAILURE; + + if(info->navFunction(nextElement, info)) + break; + + nsCOMPtr nextSibling; + rv = next->GetNextSibling(getter_AddRefs(nextSibling)); + if(NS_FAILED(rv)) + return rv; + next = nextSibling; + + /*If there's no nextMessage we may have to start from top.*/ + if(!next && (next.get()!= info->originalMessage.get()) && info->wrapAround) + { + nsCOMPtr parent; + rv = info->originalMessage->GetParentNode(getter_AddRefs(parent)); + if(NS_FAILED(rv)) + return rv; + + rv = parent->GetFirstChild(getter_AddRefs(next)); + if(NS_FAILED(rv)) + return rv; + } + } + + if(next.get() != info->originalMessage.get()) + *nextMessage = next; + NS_IF_ADDREF(*nextMessage); + return NS_OK; +} + +//Finds the next message in a threaded view. +nsresult nsMsgViewNavigationService::FindNextMessageInThreads(nsIDOMNode *startMessage, navigationInfoPtr info, nsIDOMNode **nextMessage) +{ + nsCOMPtr next; + nsCOMPtr nextChildMessage; + nsresult rv; + + nsCOMPtr startElement = do_QueryInterface(startMessage); + if(!startElement) + return NS_ERROR_FAILURE; + + //First check startMessage if we are supposed to + if(info->checkStartMessage) + { + if(info->navFunction(startElement, info)) + { + *nextMessage = startMessage; + NS_IF_ADDREF(*nextMessage); + return NS_OK; + } + } + + nsCOMPtr parent; + rv = GetParentMessage(startMessage, getter_AddRefs(parent)); + if(NS_FAILED(rv)) + return rv; + + nsAutoString parentNodeName; + rv = parent->GetNodeName(parentNodeName); + if(NS_FAILED(rv)) + return rv; + + //if we're on the top level and a thread function has been passed in, we might be able to search faster. + if(parentNodeName != "treeitem" && info->navThreadFunction) + { + return GetNextMessageByThread(startElement, info, nextMessage); + + } + //Next, search the current messages children. + rv = FindNextInChildren(startMessage, info, getter_AddRefs(nextChildMessage)); + if(NS_FAILED(rv)) + return rv; + + if(nextChildMessage) + { + *nextMessage = nextChildMessage; + NS_IF_ADDREF(*nextMessage); + return NS_OK; + } + + //Next we need to search the current messages siblings + + rv = FindNextInThreadSiblings(startMessage, info, nextMessage); + if(NS_FAILED(rv)) + return rv; + + if(*nextMessage) + return NS_OK; + + + //Finally, we need to find the next of the start message's ancestors that has a sibling + rv = FindNextInAncestors(startMessage, info, nextMessage); + if(NS_FAILED(rv)) + return rv; + + if(*nextMessage) + return NS_OK; + + //otherwise it's the tree so we need to stop and potentially start from the beginning + if(info->wrapAround) + { + nsCOMPtr firstElement; + rv = FindFirstMessage(info->tree, getter_AddRefs(firstElement)); + if(NS_FAILED(rv)) + return rv; + + info->wrapAround = PR_FALSE; + info->checkStartMessage = PR_TRUE; + + nsCOMPtr firstMessage = do_QueryInterface(firstElement); + if(!firstMessage) + return NS_ERROR_FAILURE; + + rv = FindNextMessageInThreads(firstMessage, info, nextMessage); + return rv; + } + return NS_OK; +} + +//Finds the next message in a message's children. Has to take different steps depending on if parent is open or closed. +nsresult nsMsgViewNavigationService::FindNextInChildren(nsIDOMNode *parent, navigationInfoPtr info, nsIDOMNode **nextMessage) +{ + nsresult rv; + + *nextMessage = nsnull; + + nsCOMPtr parentElement = do_QueryInterface(parent); + if(!parentElement) + return NS_ERROR_FAILURE; + + PRBool isParentOpen; + nsAutoString openAttr("open"); + nsAutoString openResult; + + rv = parentElement->GetAttribute(openAttr, openResult); + if(NS_FAILED(rv)) + return rv; + + isParentOpen = (openResult == "true"); + //First we'll deal with the case where the parent is open. In this case we can use DOM calls. + if(isParentOpen) + { + nsCOMPtr children; + rv = parent->GetChildNodes(getter_AddRefs(children)); + if(NS_FAILED(rv)) + return rv; + + PRUint32 numChildren; + rv = children->GetLength(&numChildren); + if(NS_FAILED(rv)) + return rv; + + //In this case we have treechildren + if(numChildren == 2) + { + nsCOMPtr treechildren; + + rv = children->Item(1, getter_AddRefs(treechildren)); + if(NS_FAILED(rv)) + return rv; + + nsCOMPtr childMessages; + rv = treechildren->GetChildNodes(getter_AddRefs(childMessages)); + PRUint32 numChildMessages; + + rv = childMessages->GetLength(&numChildMessages); + if(NS_FAILED(rv)) + return rv; + + for(PRUint32 i = 0; i < numChildMessages; i++) + { + nsCOMPtr childMessage; + rv = childMessages->Item(i, getter_AddRefs(childMessage)); + if(NS_FAILED(rv)) + return rv; + + //If we're at the original message again then stop. + if(childMessage.get() == info->originalMessage.get()) + { + *nextMessage = childMessage; + NS_IF_ADDREF(*nextMessage); + return NS_OK; + } + + nsCOMPtr childElement = do_QueryInterface(childMessage); + if(!childElement) + return NS_ERROR_FAILURE; + + if(info->navFunction(childElement, info)) + { + *nextMessage = childMessage; + NS_IF_ADDREF(*nextMessage); + return NS_OK; + } + else + { + //if this child isn't the message, perhaps one of its children is. + rv = FindNextInChildren(childMessage, info, nextMessage); + if(NS_FAILED(rv)) + return rv; + + if(*nextMessage) + return NS_OK; + } + } + } + } + else + { + //We need to traverse the graph in rdf looking for the next resource that fits what we're searching for. + nsAutoString idAttribute("id"); + nsAutoString parentURI; + + rv = parentElement->GetAttribute(idAttribute, parentURI); + if(NS_FAILED(rv)) + return rv; + + nsCOMPtr parentResource; + rv = info->rdfService->GetResource(nsCAutoString(parentURI), getter_AddRefs(parentResource)); + if(NS_FAILED(rv)) + return rv; + + //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) + { + nsCOMPtr nextResource; + rv = FindNextInChildrenResources(parentResource, info, getter_AddRefs(nextResource)); + if(NS_FAILED(rv)) + return rv; + + if(nextResource) + { + rv = OpenTreeitemAndDescendants(parentElement); + if(NS_FAILED(rv)) + return rv; + + nsXPIDLCString nextURI; + rv = nextResource->GetValue(getter_Copies(nextURI)); + if(NS_FAILED(rv)) + return rv; + + nsCOMPtr nextElement; + rv = info->document->GetElementById((const char*)nextURI, getter_AddRefs(nextElement)); + if(NS_FAILED(rv)) + return rv; + if(nextElement) + { + rv = nextElement->QueryInterface(nsIDOMNode::GetIID(), (void**)nextMessage); + return rv; + } + } + } + } + return NS_OK; +} + +//Finds the next message in the parentResource's children using RDF interfaces. +nsresult nsMsgViewNavigationService::FindNextInChildrenResources(nsIRDFResource *parentResource, navigationInfoPtr info, nsIRDFResource **nextResource) +{ + + nsresult rv; + + *nextResource = nsnull; + + nsCOMPtr db; + rv = info->tree->GetDatabase(getter_AddRefs(db)); + if(NS_FAILED(rv)) + return rv; + + nsCOMPtr childrenResource; + rv = info->rdfService->GetResource("http://home.netscape.com/NC-rdf#MessageChild", getter_AddRefs(childrenResource)); + if(NS_FAILED(rv)) + return rv; + + nsCOMPtr childrenEnumerator; + rv = db->GetTargets(parentResource, childrenResource, true, getter_AddRefs(childrenEnumerator)); + if(NS_FAILED(rv)) + return rv; + + if(childrenEnumerator) + { + PRBool hasMoreElements; + while(NS_SUCCEEDED(childrenEnumerator->HasMoreElements(&hasMoreElements)) && hasMoreElements) + { + nsCOMPtr childSupports; + rv = childrenEnumerator->GetNext(getter_AddRefs(childSupports)); + if(NS_FAILED(rv)) + return rv; + + nsCOMPtr childResource = do_QueryInterface(childSupports); + if(!childResource) + return NS_ERROR_FAILURE; + + if(info->navResourceFunction(childResource, info)) + { + *nextResource = childResource; + NS_IF_ADDREF(*nextResource); + return NS_OK; + } + + rv = FindNextInChildrenResources(childResource, info, nextResource); + if(NS_FAILED(rv)) + return rv; + if(*nextResource) + return NS_OK; + + } + + } + + return NS_OK; +} + +//Given a treeitem, opens up the treeitem and opens up all of its descendants that have children. +NS_IMETHODIMP nsMsgViewNavigationService::OpenTreeitemAndDescendants(nsIDOMNode *treeitem) +{ + nsresult rv; + nsCOMPtr treeitemElement = do_QueryInterface(treeitem); + if(!treeitemElement) + return NS_ERROR_FAILURE; + + nsAutoString openAttribute("open"); + nsAutoString openResult; + nsAutoString trueValue("true"); + + rv = treeitemElement->GetAttribute(openAttribute, openResult); + if(NS_FAILED(rv)) + return rv; + + if(openResult != trueValue) + { + rv = treeitemElement->SetAttribute(openAttribute, trueValue); + if(NS_FAILED(rv)) + return rv; + } + + nsCOMPtr treeitemChildNodes; + rv = treeitem->GetChildNodes(getter_AddRefs(treeitemChildNodes)); + if(NS_FAILED(rv)) + return rv; + + PRUint32 numTreeitemChildren; + rv = treeitemChildNodes->GetLength(&numTreeitemChildren); + + //if there's only one child then there are no treechildren so close it. + if(numTreeitemChildren == 1) + { + rv = treeitemElement->SetAttribute(openAttribute, ""); + if(NS_FAILED(rv)) + return rv; + } + else + { + for(PRUint32 i = 0; i < numTreeitemChildren; i++) + { + nsCOMPtr treeitemChild; + rv = treeitemChildNodes->Item(i, getter_AddRefs(treeitemChild)); + if(NS_FAILED(rv)) + return rv; + + nsAutoString nodeName; + rv = treeitemChild->GetNodeName(nodeName); + if(NS_FAILED(rv)) + return rv; + + if(nodeName == "treechildren") + { + nsCOMPtr treechildrenChildNodes; + rv = treeitemChild->GetChildNodes(getter_AddRefs(treechildrenChildNodes)); + if(NS_FAILED(rv)) + return rv; + + PRUint32 numTreechildrenChildren; + rv = treechildrenChildNodes->GetLength(&numTreechildrenChildren); + if(NS_FAILED(rv)) + return rv; + + for(PRUint32 j = 0; j < numTreechildrenChildren; j++) + { + nsCOMPtr treechildrenChild; + rv = treechildrenChildNodes->Item(j, getter_AddRefs(treechildrenChild)); + if(NS_FAILED(rv)) + return rv; + + nsAutoString treechildrenChildNodeName; + rv = treechildrenChild->GetNodeName(treechildrenChildNodeName); + if(treechildrenChildNodeName == "treeitem") + { + nsCOMPtr childElement = do_QueryInterface(treechildrenChild); + if(!childElement) + return NS_ERROR_FAILURE; + + rv = childElement->SetAttribute(openAttribute, trueValue); + if(NS_FAILED(rv)) + return rv; + //Open up all of this items + rv = OpenTreeitemAndDescendants(treechildrenChild); + } + } + } + } + } + + + return NS_OK; +} + +//Given a message, returns its parent message. +nsresult nsMsgViewNavigationService::GetParentMessage(nsIDOMNode *message, nsIDOMNode **parentMessage) +{ + nsresult rv; + //The message parent is the parent node of the parent node of the message + nsCOMPtr firstParent; + rv = message->GetParentNode(getter_AddRefs(firstParent)); + if(NS_FAILED(rv)) + return rv; + + rv = firstParent->GetParentNode(parentMessage); + + return rv; +} + +//Finds the next thread based on info. +nsresult nsMsgViewNavigationService::GetNextThread(navigationInfoPtr info, nsIDOMNode **nextThread) +{ + nsresult rv; + *nextThread = nsnull; + if(info->checkStartMessage) + { + nsCOMPtr originalElement = do_QueryInterface(info->originalMessage); + if(!originalElement) + return NS_ERROR_FAILURE; + + if(info->navThreadFunction(originalElement, info)) + { + *nextThread = info->originalMessage; + NS_IF_ADDREF(*nextThread); + return NS_OK; + } + } + + nsCOMPtr next; + rv = info->originalMessage->GetNextSibling(getter_AddRefs(next)); + if(NS_FAILED(rv)) + return rv; + + nsCOMPtr parent; + nsCOMPtr firstChild; + + //Get the parent and first child of the parent in case we need to use it later. + rv = info->originalMessage->GetParentNode(getter_AddRefs(parent)); + if(NS_FAILED(rv)) + return rv; + + rv = parent->GetFirstChild(getter_AddRefs(firstChild)); + if(NS_FAILED(rv)) + return rv; + + //In case we are currently the bottom message + if(!next && info->wrapAround) + { + next = firstChild; + } + + + while(next && (next.get() != info->originalMessage.get())) + { + nsCOMPtr nextElement = do_QueryInterface(next); + if(!nextElement) + return NS_ERROR_FAILURE; + + if(info->navThreadFunction(nextElement, info)) + { + break; + } + + nsCOMPtr nextSibling; + rv = next->GetNextSibling(getter_AddRefs(nextSibling)); + if(NS_FAILED(rv)) + return rv; + next = nextSibling; + + //If there's no nextMessage we may have to start from top. + if(!next && (next.get()!= info->originalMessage.get()) && info->wrapAround) + { + next = firstChild; + } + } + + *nextThread = next; + NS_IF_ADDREF(*nextThread); + return NS_OK; + +} + +//Finds the next message in a thread based on info. +nsresult nsMsgViewNavigationService::GetNextInThread(navigationInfoPtr info, nsIDOMNode **nextMessage) +{ + nsresult rv; + *nextMessage = nsnull; + + nsCOMPtr originalNode = do_QueryInterface(info->originalMessage); + if(!originalNode) + return NS_ERROR_FAILURE; + + if(info->navFunction(originalNode, info)) + { + *nextMessage = info->originalMessage; + NS_IF_ADDREF(*nextMessage); + return NS_OK; + } + else + { + rv = FindNextInChildren(info->originalMessage, info, nextMessage); + if(NS_FAILED(rv)) + return rv; + } + + return NS_OK; +} + +//Finds the previous message based on info. +nsresult nsMsgViewNavigationService::FindPreviousMessage(navigationInfoPtr info, nsIDOMNode **previousMessage) +{ + nsresult rv; + *previousMessage = nsnull; + + nsCOMPtr previous; + rv = info->originalMessage->GetPreviousSibling(getter_AddRefs(previous)); + if(NS_FAILED(rv)) + return rv; + + nsCOMPtr parent; + nsCOMPtr lastChild; + + rv = info->originalMessage->GetParentNode(getter_AddRefs(parent)); + if(NS_FAILED(rv)) + return rv; + + rv = parent->GetLastChild(getter_AddRefs(lastChild)); + if(NS_FAILED(rv)) + return rv; + + //In case we're already at the top + if(!previous && info->wrapAround) + { + previous = lastChild; + } + + while(previous && (previous.get() != info->originalMessage.get())) + { + nsCOMPtr previousElement = do_QueryInterface(previous); + if(!previousElement) + return NS_ERROR_FAILURE; + + if(info->navFunction(previousElement, info)) + break; + + nsCOMPtr previousSibling; + rv = previous->GetPreviousSibling(getter_AddRefs(previousSibling)); + if(NS_FAILED(rv)) + return rv; + previous = previousSibling; + + if(!previous && info->wrapAround) + { + previous = lastChild; + } + } + + *previousMessage = previous; + NS_IF_ADDREF(*previousMessage); + return NS_OK; + +} + +//Finds the next message by looking at each thread and determining if a thread has that type of message. If it does, +//it returns the first message in the thread that fits criteria specified in info. +nsresult nsMsgViewNavigationService::GetNextMessageByThread(nsIDOMXULElement *startElement, navigationInfoPtr info, + nsIDOMNode **nextMessage) +{ + nsresult rv; + nsCOMPtr nextTopMessage; + rv = FindNextThread(info->type, info->tree, startElement, info->rdfService, + info->document, info->wrapAround, PR_TRUE, getter_AddRefs(nextTopMessage)); + if(NS_FAILED(rv)) + return rv; + + nsCOMPtr nextMessageElement; + + rv = FindNextInThread(info->type, info->tree, nextTopMessage, info->rdfService, + info->document, getter_AddRefs(nextMessageElement)); + if(NS_FAILED(rv)) + return rv; + + if(nextMessageElement) + { + rv = nextMessageElement->QueryInterface(nsIDOMNode::GetIID(), (void**)nextMessage); + return rv; + } + else + { + return NS_OK; + } + +} + +//Given a message, searches from siblings onward to find the next message. This is for the threaded view. +nsresult nsMsgViewNavigationService::FindNextInThreadSiblings(nsIDOMNode *startMessage, navigationInfoPtr info, + nsIDOMNode **nextMessage) +{ + nsresult rv; + *nextMessage = nsnull; + + nsCOMPtr next; + nsCOMPtr nextChildMessage; + + rv = startMessage->GetNextSibling(getter_AddRefs(next)); + if(NS_FAILED(rv)) + return rv; + + while(next) + { + //In case we've already been here before + if(next.get() == info->originalMessage.get()) + { + *nextMessage = next; + NS_IF_ADDREF(*nextMessage); + return NS_OK; + } + + nsCOMPtr nextElement = do_QueryInterface(next); + if(!nextElement) + return NS_ERROR_FAILURE; + + if(info->navFunction(nextElement, info)) + { + *nextMessage = next; + NS_IF_ADDREF(*nextMessage); + return NS_OK; + } + + rv = FindNextInChildren(next, info, getter_AddRefs(nextChildMessage)); + if(NS_FAILED(rv)) + return rv; + + if(nextChildMessage) + { + *nextMessage = nextChildMessage; + NS_IF_ADDREF(*nextMessage); + return NS_OK; + } + + nsCOMPtr nextSibling; + rv = next->GetNextSibling(getter_AddRefs(nextSibling)); + if(NS_FAILED(rv)) + return rv; + next = nextSibling; + } + return NS_OK; +} + +//Looks through startMessage's ancestors to find the next message. +nsresult nsMsgViewNavigationService::FindNextInAncestors(nsIDOMNode *startMessage, navigationInfoPtr info, nsIDOMNode **nextMessage) +{ + nsresult rv; + nsCOMPtr parent; + rv = GetParentMessage(startMessage, getter_AddRefs(parent)); + if(NS_FAILED(rv)) + return rv; + + nsAutoString parentNodeName; + rv = parent->GetNodeName(parentNodeName); + if(NS_FAILED(rv)) + return rv; + + while(parentNodeName == "treeitem") + { + nsCOMPtr nextSibling; + rv = parent->GetNextSibling(getter_AddRefs(nextSibling)); + if(NS_FAILED(rv)) + return rv; + + if(nextSibling) + { + info->checkStartMessage = PR_TRUE; + rv = FindNextMessageInThreads(nextSibling, info, nextMessage); + return rv; + } + + nsCOMPtr newParent; + rv = GetParentMessage(parent, getter_AddRefs(newParent)); + if(NS_FAILED(rv)) + return rv; + + parent = newParent; + rv = parent->GetNodeName(parentNodeName); + if(NS_FAILED(rv)) + return rv; + } + + return NS_OK; +} + diff --git a/mailnews/base/src/nsMsgViewNavigationService.h b/mailnews/base/src/nsMsgViewNavigationService.h new file mode 100644 index 000000000000..e65a6ffbf92d --- /dev/null +++ b/mailnews/base/src/nsMsgViewNavigationService.h @@ -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 + diff --git a/mailnews/base/util/nsMsgDBFolder.cpp b/mailnews/base/util/nsMsgDBFolder.cpp index 7e1c935f39af..1c77963d36be 100644 --- a/mailnews/base/util/nsMsgDBFolder.cpp +++ b/mailnews/base/util/nsMsgDBFolder.cpp @@ -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 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 diff --git a/mailnews/base/util/nsMsgDBFolder.h b/mailnews/base/util/nsMsgDBFolder.h index fe45a8a3cb85..279e60db9509 100644 --- a/mailnews/base/util/nsMsgDBFolder.h +++ b/mailnews/base/util/nsMsgDBFolder.h @@ -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); diff --git a/mailnews/base/util/nsMsgFolder.cpp b/mailnews/base/util/nsMsgFolder.cpp index 80cc466a00e2..1d08fe7fd939 100644 --- a/mailnews/base/util/nsMsgFolder.cpp +++ b/mailnews/base/util/nsMsgFolder.cpp @@ -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) { diff --git a/mailnews/base/util/nsMsgFolder.h b/mailnews/base/util/nsMsgFolder.h index 537f344dcf0b..2eaa9f8db739 100644 --- a/mailnews/base/util/nsMsgFolder.h +++ b/mailnews/base/util/nsMsgFolder.h @@ -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);