!import
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is Mozilla Communicator client code, released
16 * March 31, 1998.
17 *
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998-1999
21 * the Initial Developer. All Rights Reserved.
22 *
23 * Contributor(s):
24 *
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
36 *
37 * ***** END LICENSE BLOCK ***** */
38
39 /* This file contains the js functions necessary to implement view navigation within the 3 pane. */
40
41 //NOTE: gMessengerBundle must be defined and set or this Overlay won't work
42
43 var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService();
44 promptService = promptService.QueryInterface(Components.interfaces.nsIPromptService);
45 var accountManager = Components.classes["@mozilla.org/messenger/account-manager;1"].getService(Components.interfaces.nsIMsgAccountManager);
46
47 // we need the account manager datasource for when trying
48 // to figure out which account is next in the folder pane.
49 var gAccountManagerDataSource = Components.classes["@mozilla.org/rdf/datasource;1?name=msgaccountmanager"].getService(Components.interfaces.nsIRDFDataSource);
50
51 // we can't compare the name to determine the order in the folder pane
52 // we need to compare the value of the sort resource,
53 // as that's what we use to sort on in the folder pane
54 var gNameProperty = Components.classes["@mozilla.org/rdf/rdf-service;1"].getService(Components.interfaces.nsIRDFService).GetResource("http://home.netscape.com/NC-rdf#Name?sort=true");
55
compareServerSortOrder
56 function compareServerSortOrder(server1, server2)
57 {
58 var sortValue1, sortValue2;
59
60 try {
61 var res1 = RDF.GetResource(server1.URI);
62 sortValue1 = gAccountManagerDataSource.GetTarget(res1, gNameProperty, true).QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
63 }
64 catch (ex) {
65 dump("XXX ex ");
66 if (server1 && server1.URI)
67 dump(server1.URI + ",");
68 dump(ex + "\n");
69 sortValue1 = "";
70 }
71
72 try {
73 var res2 = RDF.GetResource(server2.URI);
74 sortValue2 = gAccountManagerDataSource.GetTarget(res2, gNameProperty, true).QueryInterface(Components.interfaces.nsIRDFLiteral).Value;
75 }
76 catch (ex) {
77 dump("XXX ex ");
78 if (server2 && server2.URI)
79 dump(server2.URI + ",");
80 dump(ex + "\n");
81 sortValue2 = "";
82 }
83
84 if (sortValue1 < sortValue2)
85 return -1;
86 else if (sortValue1 > sortValue2)
87 return 1;
88 else
89 return 0;
90 }
91
compareFolderSortKey
92 function compareFolderSortKey(folder1, folder2)
93 {
94 return folder1.compareSortKeys(folder2);
95 }
96
GetSubFoldersInFolderPaneOrder
97 function GetSubFoldersInFolderPaneOrder(folder)
98 {
99 var subFolderEnumerator = folder.subFoldersObsolete;
100 var done = false;
101 var msgFolders = Array();
102
103 // get all the subfolders
104 while (!done) {
105 try {
106 var element = subFolderEnumerator.currentItem();
107 var msgFolder = element.QueryInterface(Components.interfaces.nsIMsgFolder);
108 msgFolders[msgFolders.length] = msgFolder;
109
110 subFolderEnumerator.next();
111 }
112 catch (ex) {
113 done = true;
114 }
115 }
116
117 // sort the subfolders
118 msgFolders.sort(compareFolderSortKey);
119 return msgFolders;
120 }
121
FindNextChildFolder
122 function FindNextChildFolder(aParent, aAfter)
123 {
124 // Search the child folders of aParent for unread messages
125 // but in the case that we are working up from the current folder
126 // we need to skip up to and including the current folder
127 // we skip the current folder in case a mail view is hiding unread messages
128 if (aParent.getNumUnread(true) > 0) {
129 var subFolders = GetSubFoldersInFolderPaneOrder(aParent);
130 var i = 0;
131 var folder = null;
132
133 // Skip folders until after the specified child
134 while (folder != aAfter)
135 folder = subFolders[i++];
136
137 while (i < subFolders.length) {
138 folder = subFolders[i++];
139 // if there is unread mail in the trash, sent, drafts, unsent messages
140 // templates or junk special folder,
141 // we ignore it when doing cross folder "next" navigation
142 if (!IsSpecialFolder(folder, MSG_FOLDER_FLAG_TRASH | MSG_FOLDER_FLAG_SENTMAIL | MSG_FOLDER_FLAG_DRAFTS | MSG_FOLDER_FLAG_QUEUE | MSG_FOLDER_FLAG_TEMPLATES | MSG_FOLDER_FLAG_JUNK, true)) {
143 if (folder.getNumUnread(false) > 0)
144 return folder;
145
146 folder = FindNextChildFolder(folder, null);
147 if (folder)
148 return folder;
149 }
150 }
151 }
152
153 return null;
154 }
155
FindNextFolder
156 function FindNextFolder()
157 {
158 // look for the next folder, this will only look on the current account
159 // and below us, in the folder pane
160 // note use of gDBView restricts this function to message folders
161 // otherwise you could go next unread from a server
162 var folder = FindNextChildFolder(gDBView.msgFolder, null);
163 if (folder)
164 return folder;
165
166 // didn't find folder in children
167 // go up to the parent, and start at the folder after the current one
168 // unless we are at a server, in which case bail out.
169 for (folder = gDBView.msgFolder; !folder.isServer; ) {
170
171 var parent = folder.parentMsgFolder;
172 folder = FindNextChildFolder(parent, folder);
173 if (folder)
174 return folder;
175
176 // none at this level after the current folder. go up.
177 folder = parent;
178 }
179
180 // nothing in the current account, start with the next account (below)
181 // and try until we hit the bottom of the folder pane
182
183 // start at the account after the current account
184 var rootFolders = GetRootFoldersInFolderPaneOrder();
185 for (i = 0; i < rootFolders.length; i++) {
186 if (rootFolders[i].URI == gDBView.msgFolder.server.serverURI)
187 break;
188 }
189
190 for (var j = i + 1; j < rootFolders.length; j++) {
191 folder = FindNextChildFolder(rootFolders[j], null);
192 if (folder)
193 return folder;
194 }
195
196 // if nothing from the current account down to the bottom
197 // (of the folder pane), start again at the top.
198 for (j = 0; j <= i; j++) {
199 folder = FindNextChildFolder(rootFolders[j], null);
200 if (folder)
201 return folder;
202 }
203 return null;
204 }
205
GetRootFoldersInFolderPaneOrder
206 function GetRootFoldersInFolderPaneOrder()
207 {
208 var allServers = accountManager.allServers;
209 var numServers = allServers.Count();
210
211 var serversMsgFolders = Array(numServers);
212 for (var i = 0; i < numServers; i++)
213 serversMsgFolders[i] = allServers.GetElementAt(i).QueryInterface(Components.interfaces.nsIMsgIncomingServer).rootMsgFolder;
214
215 // sort accounts, so they are in the same order as folder pane
216 serversMsgFolders.sort(compareServerSortOrder);
217
218 return serversMsgFolders;
219 }
220
CrossFolderNavigation
221 function CrossFolderNavigation(type)
222 {
223 // do cross folder navigation for next unread message/thread and message history
224 if (type != nsMsgNavigationType.nextUnreadMessage &&
225 type != nsMsgNavigationType.nextUnreadThread &&
226 type != nsMsgNavigationType.forward &&
227 type != nsMsgNavigationType.back)
228 return;
229
230 if (type == nsMsgNavigationType.nextUnreadMessage ||
231 type == nsMsgNavigationType.nextUnreadThread)
232 {
233
234 var nextMode = pref.getIntPref("mailnews.nav_crosses_folders");
235 // 0: "next" goes to the next folder, without prompting
236 // 1: "next" goes to the next folder, and prompts (the default)
237 // 2: "next" does nothing when there are no unread messages
238
239 // not crossing folders, don't find next
240 if (nextMode == 2)
241 return;
242
243 var folder = FindNextFolder();
244 if (folder && (gDBView.msgFolder.URI != folder.URI))
245 {
246 switch (nextMode)
247 {
248 case 0:
249 // do this unconditionally
250 gNextMessageAfterLoad = type;
251 SelectFolder(folder.URI);
252 break;
253 case 1:
254 default:
255 var promptText = gMessengerBundle.getFormattedString("advanceNextPrompt", [ folder.name ], 1);
256 if (!promptService.confirmEx(window, promptText, promptText,
257 promptService.STD_YES_NO_BUTTONS,
258 null, null, null, null, {}))
259 {
260 gNextMessageAfterLoad = type;
261 SelectFolder(folder.URI);
262 }
263 break;
264 }
265 }
266 }
267 else
268 {
269 // if no message is loaded, relPos should be 0, to
270 // go back to the previously loaded message
271 var relPos = (type == nsMsgNavigationType.forward)
272 ? 1 : ((GetLoadedMessage()) ? -1 : 0);
273 var folderUri = messenger.getFolderUriAtNavigatePos(relPos);
274 var msgHdr = messenger.msgHdrFromURI(messenger.getMsgUriAtNavigatePos(relPos));
275 gStartMsgKey = msgHdr.messageKey;
276 var curPos = messenger.navigatePos;
277 curPos += relPos;
278 messenger.navigatePos = curPos;
279 SelectFolder(folderUri);
280 }
281 }
282
283
ScrollToMessage
284 function ScrollToMessage(type, wrap, selectMessage)
285 {
286 try {
287 var treeView = gDBView.QueryInterface(Components.interfaces.nsITreeView);
288 var treeSelection = treeView.selection;
289 var currentIndex = treeSelection.currentIndex;
290
291 var resultId = new Object;
292 var resultIndex = new Object;
293 var threadIndex = new Object;
294
295 gDBView.viewNavigate(type, resultId, resultIndex, threadIndex, true /* wrap */);
296
297 // only scroll and select if we found something
298 if ((resultId.value != nsMsgViewIndex_None) && (resultIndex.value != nsMsgViewIndex_None)) {
299 if (selectMessage){
300 treeSelection.select(resultIndex.value);
301 }
302 EnsureRowInThreadTreeIsVisible(resultIndex.value);
303 return true;
304 }
305 else {
306 return false;
307 }
308 }
309 catch (ex) {
310 return false;
311 }
312 }
313
GoNextMessage
314 function GoNextMessage(type, startFromBeginning)
315 {
316 try {
317 var succeeded = ScrollToMessage(type, startFromBeginning, true);
318 if (!succeeded) {
319 CrossFolderNavigation(type);
320 }
321 }
322 catch (ex) {
323 dump("GoNextMessage ex = " + ex + "\n");
324 }
325
326 SetFocusThreadPaneIfNotOnMessagePane();
327 }