gecko-dev/mailnews/base/resources/content/msgViewNavigation.js
sspitzer%netscape.com 08c99a8091 fix for #60139 and #72821. implement proper next folder navigation.
now we should find the right "next" folder when you hit next and there
are no more unread messages in the current folder.  we respect the folder
pane sort order for determining what's next, which is what's "below" in the folder pane.

r/sr=bienvenu
2001-10-08 20:56:52 +00:00

344 lines
11 KiB
JavaScript

/* -*- 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 Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998-1999 Netscape Communications Corporation. All
* Rights Reserved.
*/
/* This file contains the js functions necessary to implement view navigation within the 3 pane. */
//NOTE: gMessengerBundle must be defined and set or this Overlay won't work
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService();
promptService = promptService.QueryInterface(Components.interfaces.nsIPromptService);
var accountManager = Components.classes["@mozilla.org/messenger/account-manager;1"].getService(Components.interfaces.nsIMsgAccountManager);
// we need the folder datasource and account manager datasource
// for when trying to figure out which folder (or account) is next
// in the folder pane.
var gFolderDataSource = Components.classes["@mozilla.org/rdf/datasource;1?name=mailnewsfolders"].createInstance().QueryInterface(Components.interfaces.nsIRDFDataSource);
var gAccountManagerDataSource = Components.classes["@mozilla.org/rdf/datasource;1?name=msgaccountmanager"].createInstance().QueryInterface(Components.interfaces.nsIRDFDataSource);
// we can't compare the name to determine the order in the folder pane
// we need to compare the value of the sort resource,
// as that's what we use to sort on in the folder pane
var gNameProperty = Components.classes["@mozilla.org/rdf/rdf-service;1"].getService(Components.interfaces.nsIRDFService).GetResource("http://home.netscape.com/NC-rdf#Name?sort=true");
function compareServerSortOrder(a,b)
{
return compareSortOrder(a,b, gAccountManagerDataSource);
}
function compareFolderSortOrder(a,b)
{
return compareSortOrder(a,b, gFolderDataSource);
}
function compareSortOrder(folder1, folder2, datasource)
{
var sortValue1, sortValue2;
try {
var res1 = RDF.GetResource(folder1.URI);
sortValue1 = datasource.GetTarget(res1, gNameProperty, true).QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
}
catch (ex) {
dump("XXX ex " + folder1.URI + "," + ex + "\n");
sortValue1 = "";
}
try {
var res2 = RDF.GetResource(folder2.URI);
sortValue2 = datasource.GetTarget(res2, gNameProperty, true).QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
}
catch (ex) {
dump("XXX ex " + folder2.URI + "," + ex + "\n");
sortValue2 = "";
}
if (sortValue1 < sortValue2)
return -1;
else if (sortValue1 > sortValue2)
return 1;
else
return 0;
}
function GetSubFoldersInFolderPaneOrder(folder)
{
var subFolderEnumerator = folder.GetSubFolders();
var done = false;
var msgFolders = Array();
// get all the subfolders
while (!done) {
try {
var element = subFolderEnumerator.currentItem();
msgFolders[msgFolders.length] = element.QueryInterface(Components.interfaces.nsIMsgFolder);
subFolderEnumerator.next();
}
catch (ex) {
done = true;
}
}
// sort the subfolders
msgFolders.sort(compareFolderSortOrder);
return msgFolders;
}
function IgnoreFolderForNextNavigation(folder)
{
// if there is unread mail in the trash, sent, drafts, unsent messages
// or templates folders, we ignore it
// when doing cross folder "next" navigation
return IsSpecialFolder(folder, MSG_FOLDER_FLAG_TRASH | MSG_FOLDER_FLAG_SENTMAIL | MSG_FOLDER_FLAG_DRAFTS | MSG_FOLDER_FLAG_QUEUE | MSG_FOLDER_FLAG_TEMPLATES)
}
function FindNextFolder(originalFolderURI)
{
if (!originalFolderURI) return null;
var originalFolderResource = RDF.GetResource(originalFolderURI);
var folder = originalFolderResource.QueryInterface(Components.interfaces.nsIFolder);
if (!folder) return null;
var originalMsgFolder = folder.QueryInterface(Components.interfaces.nsIMsgFolder);
// don't land on folders or subfolder of folders
// that we don't care about
if(!IgnoreFolderForNextNavigation(originalMsgFolder)) {
if (originalMsgFolder.getNumUnread(false /* don't descend */) > 0) {
return originalMsgFolder.URI;
}
}
// first check the children
var msgFolders = GetSubFoldersInFolderPaneOrder(folder);
var i;
for (i=0;i<msgFolders.length;i++) {
var currentSubFolder = msgFolders[i];
// don't land on folders we don't care about
if(!IgnoreFolderForNextNavigation(currentSubFolder)) {
if (currentSubFolder.getNumUnread(false /* don't descend */) > 0) {
// if the child has unread, use it.
return currentSubFolder.URI;
}
else if (currentSubFolder.getNumUnread(true /* descend */) > 0) {
// if the child doesn't have any unread, but it's children do, recurse
return FindNextFolder(currentSubFolder.URI);
}
}
}
// didn't find folder in children
// go up to the parent, and start at the folder after the current one
// unless we are at a server, in which case bail out.
if (originalMsgFolder.isServer) {
return null;
}
msgFolders = GetSubFoldersInFolderPaneOrder(folder.parent);
for (i=0;i<msgFolders.length;i++) {
if (msgFolders[i].URI == folder.URI)
break;
}
// the current folder is at index i
// start at the next folder after that, if there is one
if (i+1 < msgFolders.length) {
return FindNextFolder(msgFolders[i+1].URI);
}
try {
// none at this level after the current folder. go up.
if (folder.parent && folder.parent.URI) {
var parentMsgFolder = folder.parent.QueryInterface(Components.interfaces.nsIMsgFolder);
if (parentMsgFolder.isServer) {
// we've already search the parent's children below us.
// don't search the server again, return null and let
// the caller search the next server
return null;
}
else {
FindNextFolder(folder.parent.URI);
}
}
}
catch (ex) {
dump("XXX ex " + ex + "\n");
}
return null;
}
function GetTopLevelMessageForMessage(message, folder)
{
if(!folder)
folder = message.msgFolder;
var thread = folder.getThreadForMessage(message);
var outIndex = new Object();
var rootHdr = thread.GetRootHdr(outIndex);
var topMessage = folder.createMessageFromMsgDBHdr(rootHdr);
return topMessage;
}
function GetRootFoldersInFolderPaneOrder()
{
var allServers = accountManager.allServers;
var numServers = allServers.Count();
var i;
var serversMsgFolders = Array(numServers);
for (i=0;i<numServers;i++) {
serversMsgFolders[i] = allServers.GetElementAt(i).QueryInterface(Components.interfaces.nsIMsgIncomingServer).RootFolder.QueryInterface(Components.interfaces.nsIMsgFolder);
}
// sort accounts, so they are in the same order as folder pane
serversMsgFolders.sort(compareServerSortOrder);
return serversMsgFolders;
}
function CrossFolderNavigation(type, supportsFolderPane )
{
if (type != nsMsgNavigationType.nextUnreadMessage) {
// only do cross folder navigation for "next unread message"
return null;
}
var nextMode = pref.GetIntPref("mailnews.nav_crosses_folders");
// 0: "next" goes to the next folder, without prompting
// 1: "next" goes to the next folder, and prompts (the default)
// 2: "next" does nothing when there are no unread messages
// not crossing folders, don't find next
if (nextMode == 2) return null;
var originalFolderURI = gDBView.msgFolder.URI;
var nextFolderURI = null;
var done = false;
var startAtURI = originalFolderURI;
var i,j;
var rootFolders;
// look for the next folder, this will only look on the current account
// and below us, in the folder pane
nextFolderURI = FindNextFolder(startAtURI);
// if nothing in the current account, start with the next account (below)
// and try until we hit the bottom of the folder pane
if (!nextFolderURI) {
// start at the account after the current account
rootFolders = GetRootFoldersInFolderPaneOrder();
for (i=0;i<rootFolders.length;i++) {
if (rootFolders[i].URI == gDBView.msgFolder.server.serverURI)
break;
}
for (j=i+1; j<rootFolders.length; j++) {
nextFolderURI = FindNextFolder(rootFolders[j].URI);
if (nextFolderURI)
break;
}
// if nothing from the current account down to the bottom
// (of the folder pane), start again at the top.
if (!nextFolderURI) {
for (j=0; j<rootFolders.length; j++) {
nextFolderURI = FindNextFolder(rootFolders[j].URI);
if (nextFolderURI)
break;
}
}
}
if (nextFolderURI && (originalFolderURI != nextFolderURI)) {
var nextFolderResource = RDF.GetResource(nextFolderURI);
var nextFolder = nextFolderResource.QueryInterface(Components.interfaces.nsIMsgFolder);
switch (nextMode) {
case 0:
// do this unconditionally
gNextMessageAfterLoad = type;
if (supportsFolderPane)
SelectFolder(nextFolderURI);
break;
case 1:
default:
var promptText = gMessengerBundle.getFormattedString("advanceNextPrompt", [ nextFolder.name ], 1);
if (promptService.confirm(window, promptText, promptText)) {
gNextMessageAfterLoad = type;
if (supportsFolderPane)
SelectFolder(nextFolderURI);
}
break;
}
}
return nextFolderURI;
}
// from MailNewsTypes.h
const nsMsgViewIndex_None = 0xFFFFFFFF;
function ScrollToMessage(type, wrap, selectMessage)
{
try {
var outlinerView = gDBView.QueryInterface(Components.interfaces.nsIOutlinerView);
var outlinerSelection = outlinerView.selection;
var currentIndex = outlinerSelection.currentIndex;
var resultId = new Object;
var resultIndex = new Object;
var threadIndex = new Object;
gDBView.viewNavigate(type, resultId, resultIndex, threadIndex, true /* wrap */);
// only scroll and select if we found something
if ((resultId.value != nsMsgViewIndex_None) && (resultIndex.value != nsMsgViewIndex_None)) {
if (selectMessage) {
outlinerSelection.select(resultIndex.value);
}
EnsureRowInThreadOutlinerIsVisible(resultIndex.value);
return true;
}
else {
return false;
}
}
catch (ex) {
return false;
}
}
function GoNextMessage(type, startFromBeginning)
{
try {
var succeeded = ScrollToMessage(type, startFromBeginning, true);
if (!succeeded) {
CrossFolderNavigation(type, true);
}
}
catch (ex) {
dump("GoNextMessage ex = " + ex + "\n");
}
}