!import
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
1 /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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 * HÃ¥kan Waara <hwaara@chello.se>
25 *
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
37 *
38 * ***** END LICENSE BLOCK ***** */
39
40 var rdfDatasourcePrefix = "@mozilla.org/rdf/datasource;1?name=";
41 var rdfServiceContractID = "@mozilla.org/rdf/rdf-service;1";
42 var searchSessionContractID = "@mozilla.org/messenger/searchSession;1";
43 var folderDSContractID = rdfDatasourcePrefix + "mailnewsfolders";
44 var gSearchView;
45 var gSearchSession;
46 var gCurrentFolder;
47
48 var nsIMsgFolder = Components.interfaces.nsIMsgFolder;
49 var nsIMsgWindow = Components.interfaces.nsIMsgWindow;
50 var nsIMsgRDFDataSource = Components.interfaces.nsIMsgRDFDataSource;
51 var nsMsgSearchScope = Components.interfaces.nsMsgSearchScope;
52
53 var gFolderDatasource;
54 var gFolderPicker;
55 var gStatusBar = null;
56 var gStatusFeedback = new nsMsgStatusFeedback();
57 var gTimelineEnabled = false;
58 var gMessengerBundle = null;
59 var RDF;
60 var gSearchBundle;
61 var gNextMessageViewIndexAfterDelete = -2;
62
63 // Datasource search listener -- made global as it has to be registered
64 // and unregistered in different functions.
65 var gDataSourceSearchListener;
66 var gViewSearchListener;
67
68 var gSearchStopButton;
69 var gMailSession;
70
71 // var MSG_FOLDER_FLAG_VIRTUAL = 0x0020; is defined in commandglue.js
72
73 // Controller object for search results thread pane
74 var nsSearchResultsController =
75 {
supportsCommand
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
76 supportsCommand: function(command)
77 {
78 switch(command) {
79 case "cmd_delete":
80 case "cmd_shiftDelete":
81 case "button_delete":
82 case "cmd_open":
83 case "file_message_button":
84 case "goto_folder_button":
85 case "saveas_vf_button":
86 case "cmd_selectAll":
87 return true;
88 default:
89 return false;
90 }
91 },
92
93 // this controller only handles commands
94 // that rely on items being selected in
95 // the search results pane.
isCommandEnabled
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
96 isCommandEnabled: function(command)
97 {
98 var enabled = true;
99
100 switch (command) {
101 case "goto_folder_button":
102 if (GetNumSelectedMessages() != 1)
103 enabled = false;
104 break;
105 case "cmd_delete":
106 case "cmd_shiftDelete":
107 case "button_delete":
108 // this assumes that advanced searches don't cross accounts
109 if (GetNumSelectedMessages() <= 0 || isNewsURI(gSearchView.getURIForViewIndex(0)))
110 enabled = false;
111 break;
112 case "saveas_vf_button":
113 // need someway to see if there are any search criteria...
114 return true;
115 case "cmd_selectAll":
116 return GetDBView() != null;
117 default:
118 if (GetNumSelectedMessages() <= 0)
119 enabled = false;
120 break;
121 }
122
123 return enabled;
124 },
125
doCommand
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
126 doCommand: function(command)
127 {
128 switch(command) {
129 case "cmd_open":
130 MsgOpenSelectedMessages();
131 return true;
132
133 case "cmd_delete":
134 case "button_delete":
135 MsgDeleteSelectedMessages(nsMsgViewCommandType.deleteMsg);
136 return true;
137 case "cmd_shiftDelete":
138 MsgDeleteSelectedMessages(nsMsgViewCommandType.deleteNoTrash);
139 return true;
140
141 case "goto_folder_button":
142 GoToFolder();
143 return true;
144
145 case "saveas_vf_button":
146 saveAsVirtualFolder();
147 return true;
148
149 case "cmd_selectAll":
150 // move the focus to the search results pane
151 GetThreadTree().focus();
152 GetDBView().doCommand(nsMsgViewCommandType.selectAll)
153 return true;
154
155 default:
156 return false;
157 }
158
159 },
160
onEvent
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
161 onEvent: function(event)
162 {
163 }
164 }
165
UpdateMailSearch
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
166 function UpdateMailSearch(caller)
167 {
168 //dump("XXX update mail-search " + caller + "\n");
169 document.commandDispatcher.updateCommands('mail-search');
170 }
171
SetAdvancedSearchStatusText
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
172 function SetAdvancedSearchStatusText(aNumHits)
173 {
174 var statusMsg;
175 // if there are no hits, it means no matches were found in the search.
176 if (aNumHits == 0)
177 statusMsg = gSearchBundle.getString("searchFailureMessage");
178 else
179 {
180 if (aNumHits == 1)
181 statusMsg = gSearchBundle.getString("searchSuccessMessage");
182 else
183 statusMsg = gSearchBundle.getFormattedString("searchSuccessMessages", [aNumHits]);
184 }
185
186 gStatusFeedback.showStatusString(statusMsg);
187 }
188
189 // nsIMsgSearchNotify object
190 var gSearchNotificationListener =
191 {
onSearchHit
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
192 onSearchHit: function(header, folder)
193 {
194 // XXX TODO
195 // update status text?
196 },
197
onSearchDone
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
198 onSearchDone: function(status)
199 {
200 gSearchStopButton.setAttribute("label", gSearchBundle.getString("labelForSearchButton"));
201 gSearchStopButton.setAttribute("accesskey", gSearchBundle.getString("labelForSearchButton.accesskey"));
202 gStatusFeedback._stopMeteors();
203 SetAdvancedSearchStatusText(gSearchView.QueryInterface(Components.interfaces.nsITreeView).rowCount);
204 },
205
onNewSearch
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
206 onNewSearch: function()
207 {
208 gSearchStopButton.setAttribute("label", gSearchBundle.getString("labelForStopButton"));
209 gSearchStopButton.setAttribute("accesskey", gSearchBundle.getString("labelForStopButton.accesskey"));
210 UpdateMailSearch("new-search");
211 gStatusFeedback._startMeteors();
212 gStatusFeedback.showStatusString(gSearchBundle.getString("searchingMessage"));
213 }
214 }
215
216 // the folderListener object
217 var gFolderListener = {
OnItemAdded
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
218 OnItemAdded: function(parentItem, item) {},
219
OnItemRemoved
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
220 OnItemRemoved: function(parentItem, item){},
221
OnItemPropertyChanged
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
222 OnItemPropertyChanged: function(item, property, oldValue, newValue) {},
223
OnItemIntPropertyChanged
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
224 OnItemIntPropertyChanged: function(item, property, oldValue, newValue) {},
225
OnItemBoolPropertyChanged
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
226 OnItemBoolPropertyChanged: function(item, property, oldValue, newValue) {},
227
OnItemUnicharPropertyChanged
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
228 OnItemUnicharPropertyChanged: function(item, property, oldValue, newValue){},
OnItemPropertyFlagChanged
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
229 OnItemPropertyFlagChanged: function(item, property, oldFlag, newFlag) {},
230
OnItemEvent
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
231 OnItemEvent: function(folder, event) {
232 var eventType = event.toString();
233
234 if (eventType == "DeleteOrMoveMsgCompleted") {
235 HandleDeleteOrMoveMessageCompleted(folder);
236 }
237 else if (eventType == "DeleteOrMoveMsgFailed") {
238 HandleDeleteOrMoveMessageFailed(folder);
239 }
240 }
241 }
242
HideSearchColumn
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
243 function HideSearchColumn(id)
244 {
245 var col = document.getElementById(id);
246 if (col) {
247 col.setAttribute("hidden","true");
248 col.setAttribute("ignoreincolumnpicker","true");
249 }
250 }
251
ShowSearchColumn
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
252 function ShowSearchColumn(id)
253 {
254 var col = document.getElementById(id);
255 if (col) {
256 col.removeAttribute("hidden");
257 col.removeAttribute("ignoreincolumnpicker");
258 }
259 }
260
searchOnLoad
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
261 function searchOnLoad()
262 {
263 initializeSearchWidgets();
264 initializeSearchWindowWidgets();
265 CreateMessenger();
266
267 gSearchBundle = document.getElementById("bundle_search");
268 gSearchStopButton.setAttribute("label", gSearchBundle.getString("labelForSearchButton"));
269 gSearchStopButton.setAttribute("accesskey", gSearchBundle.getString("labelForSearchButton.accesskey"));
270 gMessengerBundle = document.getElementById("bundle_messenger");
271 setupDatasource();
272 setupSearchListener();
273
274 if (window.arguments && window.arguments[0])
275 selectFolder(window.arguments[0].folder);
276
277 onMore(null);
278 UpdateMailSearch("onload");
279
280 // hide and remove these columns from the column picker. you can't thread search results
281 HideSearchColumn("threadCol"); // since you can't thread search results
282 HideSearchColumn("totalCol"); // since you can't thread search results
283 HideSearchColumn("unreadCol"); // since you can't thread search results
284 HideSearchColumn("unreadButtonColHeader");
285 HideSearchColumn("statusCol");
286 HideSearchColumn("flaggedCol");
287 HideSearchColumn("idCol");
288 HideSearchColumn("junkStatusCol");
289 HideSearchColumn("accountCol");
290
291 // we want to show the location column for search
292 ShowSearchColumn("locationCol");
293 }
294
searchOnUnload
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
295 function searchOnUnload()
296 {
297 // unregister listeners
298 gSearchSession.unregisterListener(gViewSearchListener);
299 gSearchSession.unregisterListener(gSearchNotificationListener);
300
301 gMailSession.RemoveFolderListener(gFolderListener);
302
303 if (gSearchView) {
304 gSearchView.close();
305 gSearchView = null;
306 }
307
308 // release this early because msgWindow holds a weak reference
309 msgWindow.rootDocShell = null;
310 }
311
initializeSearchWindowWidgets
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
312 function initializeSearchWindowWidgets()
313 {
314 gFolderPicker = document.getElementById("searchableFolders");
315 gSearchStopButton = document.getElementById("search-button");
316 gStatusBar = document.getElementById('statusbar-icon');
317 hideMatchAllItem();
318
319 msgWindow = Components.classes["@mozilla.org/messenger/msgwindow;1"]
320 .createInstance(nsIMsgWindow);
321 msgWindow.domWindow = window;
322 msgWindow.rootDocShell.appType = Components.interfaces.nsIDocShell.APP_TYPE_MAIL;
323 msgWindow.statusFeedback = gStatusFeedback;
324
325 // functionality to enable/disable buttons using nsSearchResultsController
326 // depending of whether items are selected in the search results thread pane.
327 top.controllers.insertControllerAt(0, nsSearchResultsController);
328 }
329
330
onSearchStop
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
331 function onSearchStop() {
332 gSearchSession.interruptSearch();
333 }
334
onResetSearch
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
335 function onResetSearch(event) {
336 onReset(event);
337
338 var tree = GetThreadTree();
339 tree.treeBoxObject.view = null;
340 gStatusFeedback.showStatusString("");
341 }
342
selectFolder
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
343 function selectFolder(folder)
344 {
345 var folderURI;
346
347 // if we can't search messages on this folder, just select the first one
348 if (!folder || !folder.server.canSearchMessages || (folder.flags & MSG_FOLDER_FLAG_VIRTUAL)) {
349 // find first item in our folder picker menu list
350 folderURI = gFolderPicker.firstChild.tree.builderView.getResourceAtIndex(0).Value;
351 } else {
352 folderURI = folder.URI;
353 }
354 updateSearchFolderPicker(folderURI);
355 }
356
updateSearchFolderPicker
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
357 function updateSearchFolderPicker(folderURI)
358 {
359 SetFolderPicker(folderURI, gFolderPicker.id);
360
361 // use the URI to get the real folder
362 gCurrentFolder =
363 RDF.GetResource(folderURI).QueryInterface(nsIMsgFolder);
364
365 var searchLocalSystem = document.getElementById("checkSearchLocalSystem");
366 if (searchLocalSystem)
367 searchLocalSystem.disabled = gCurrentFolder.server.searchScope == nsMsgSearchScope.offlineMail;
368 setSearchScope(GetScopeForFolder(gCurrentFolder));
369 }
370
updateSearchLocalSystem
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
371 function updateSearchLocalSystem()
372 {
373 setSearchScope(GetScopeForFolder(gCurrentFolder));
374 }
375
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
376 function UpdateAfterCustomHeaderChange()
377 {
378 updateSearchAttributes();
379 }
380
onChooseFolder
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
381 function onChooseFolder(event) {
382 var folderURI = event.id;
383 if (folderURI) {
384 updateSearchFolderPicker(folderURI);
385 }
386 }
387
onEnterInSearchTerm
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
388 function onEnterInSearchTerm()
389 {
390 // on enter
391 // if not searching, start the search
392 // if searching, stop and then start again
393 if (gSearchStopButton.getAttribute("label") == gSearchBundle.getString("labelForSearchButton")) {
394 onSearch();
395 }
396 else {
397 onSearchStop();
398 onSearch();
399 }
400 }
401
onSearch
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
402 function onSearch()
403 {
404 // set the view. do this on every search, to
405 // allow the tree to reset itself
406 var treeView = gSearchView.QueryInterface(Components.interfaces.nsITreeView);
407 if (treeView)
408 {
409 var tree = GetThreadTree();
410 tree.treeBoxObject.view = treeView;
411 }
412
413 gSearchSession.clearScopes();
414 // tell the search session what the new scope is
415 if (!gCurrentFolder.isServer && !gCurrentFolder.noSelect)
416 gSearchSession.addScopeTerm(GetScopeForFolder(gCurrentFolder),
417 gCurrentFolder);
418
419 var searchSubfolders = document.getElementById("checkSearchSubFolders").checked;
420 if (gCurrentFolder && (searchSubfolders || gCurrentFolder.isServer || gCurrentFolder.noSelect))
421 {
422 AddSubFolders(gCurrentFolder);
423 }
424 // reflect the search widgets back into the search session
425 saveSearchTerms(gSearchSession.searchTerms, gSearchSession);
426
427 try
428 {
429 gSearchSession.search(msgWindow);
430 }
431 catch(ex)
432 {
433 dump("Search Exception\n");
434 }
435 // refresh the tree after the search starts, because initiating the
436 // search will cause the datasource to clear itself
437 }
438
AddSubFolders
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
439 function AddSubFolders(folder) {
440 var subFolders = folder.subFolders;
441 while (subFolders.hasMoreElements())
442 {
443 var nextFolder =
444 subFolders.getNext().QueryInterface(Components.interfaces.nsIMsgFolder);
445
446 if (!(nextFolder.flags & MSG_FOLDER_FLAG_VIRTUAL))
447 {
448 if (!nextFolder.noSelect)
449 gSearchSession.addScopeTerm(GetScopeForFolder(nextFolder), nextFolder);
450
451 AddSubFolders(nextFolder);
452 }
453 }
454 }
455
AddSubFoldersToURI
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
456 function AddSubFoldersToURI(folder)
457 {
458 var returnString = "";
459
460 var subFolders = folder.subFolders;
461
462 while (subFolders.hasMoreElements())
463 {
464 var nextFolder =
465 subFolders.getNext().QueryInterface(Components.interfaces.nsIMsgFolder);
466
467 if (!(nextFolder.flags & MSG_FOLDER_FLAG_VIRTUAL))
468 {
469 if (!nextFolder.noSelect && !nextFolder.isServer)
470 {
471 if (returnString.length > 0)
472 returnString += '|';
473 returnString += nextFolder.URI;
474 }
475 var subFoldersString = AddSubFoldersToURI(nextFolder);
476 if (subFoldersString.length > 0)
477 {
478 if (returnString.length > 0)
479 returnString += '|';
480 returnString += subFoldersString;
481 }
482 }
483 }
484 return returnString;
485 }
486
487
GetScopeForFolder
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
488 function GetScopeForFolder(folder)
489 {
490 var searchLocalSystem = document.getElementById("checkSearchLocalSystem");
491 return searchLocalSystem && searchLocalSystem.checked ? nsMsgSearchScope.offlineMail : folder.server.searchScope;
492 }
493
494 var nsMsgViewSortType = Components.interfaces.nsMsgViewSortType;
495 var nsMsgViewSortOrder = Components.interfaces.nsMsgViewSortOrder;
496 var nsMsgViewFlagsType = Components.interfaces.nsMsgViewFlagsType;
497 var nsMsgViewCommandType = Components.interfaces.nsMsgViewCommandType;
498
goUpdateSearchItems
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
499 function goUpdateSearchItems(commandset)
500 {
501 for (var i = 0; i < commandset.childNodes.length; i++)
502 {
503 var commandID = commandset.childNodes[i].getAttribute("id");
504 if (commandID)
505 {
506 goUpdateCommand(commandID);
507 }
508 }
509 }
510
nsMsgSearchCommandUpdater
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
511 function nsMsgSearchCommandUpdater()
512 {}
513
514 nsMsgSearchCommandUpdater.prototype =
515 {
updateCommandStatus
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
516 updateCommandStatus : function()
517 {
518 // the back end is smart and is only telling us to update command status
519 // when the # of items in the selection has actually changed.
520 document.commandDispatcher.updateCommands('mail-search');
521 },
displayMessageChanged
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
522 displayMessageChanged : function(aFolder, aSubject, aKeywords)
523 {
524 },
525
updateNextMessageAfterDelete
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
526 updateNextMessageAfterDelete : function()
527 {
528 SetNextMessageAfterDelete();
529 },
530
QueryInterface
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
531 QueryInterface : function(iid)
532 {
533 if (iid.equals(Components.interfaces.nsIMsgDBViewCommandUpdater) ||
534 iid.equals(Components.interfaces.nsISupports))
535 return this;
536
537 throw Components.results.NS_NOINTERFACE;
538 }
539 }
540
setupDatasource
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
541 function setupDatasource() {
542
543 RDF = Components.classes[rdfServiceContractID].getService(Components.interfaces.nsIRDFService);
544 gSearchView = Components.classes["@mozilla.org/messenger/msgdbview;1?type=search"].createInstance(Components.interfaces.nsIMsgDBView);
545 var count = new Object;
546 var cmdupdator = new nsMsgSearchCommandUpdater();
547
548 gSearchView.init(messenger, msgWindow, cmdupdator);
549 gSearchView.open(null, nsMsgViewSortType.byId, nsMsgViewSortOrder.ascending, nsMsgViewFlagsType.kNone, count);
550
551 // the thread pane needs to use the search datasource (to get the
552 // actual list of messages) and the message datasource (to get any
553 // attributes about each message)
554 gSearchSession = Components.classes[searchSessionContractID].createInstance(Components.interfaces.nsIMsgSearchSession);
555
556 gMailSession = Components.classes[mailSessionContractID].getService(Components.interfaces.nsIMsgMailSession);
557 var nsIFolderListener = Components.interfaces.nsIFolderListener;
558 var notifyFlags = nsIFolderListener.event;
559 gMailSession.AddFolderListener(gFolderListener, notifyFlags);
560
561 // the datasource is a listener on the search results
562 gViewSearchListener = gSearchView.QueryInterface(Components.interfaces.nsIMsgSearchNotify);
563 gSearchSession.registerListener(gViewSearchListener);
564 }
565
566
setupSearchListener
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
567 function setupSearchListener()
568 {
569 // Setup the javascript object as a listener on the search results
570 gSearchSession.registerListener(gSearchNotificationListener);
571 }
572
573 // stuff after this is implemented to make the thread pane work
GetFolderDatasource
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
574 function GetFolderDatasource()
575 {
576 if (!gFolderDatasource)
577 gFolderDatasource = Components.classes[folderDSContractID].getService(Components.interfaces.nsIRDFDataSource);
578 return gFolderDatasource;
579 }
580
581 // used to determine if we should try to load a message
IsThreadAndMessagePaneSplitterCollapsed
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
582 function IsThreadAndMessagePaneSplitterCollapsed()
583 {
584 return true;
585 }
586
587 // used to toggle functionality for Search/Stop button.
onSearchButton
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
588 function onSearchButton(event)
589 {
590 if (event.target.label == gSearchBundle.getString("labelForSearchButton"))
591 onSearch();
592 else
593 onSearchStop();
594 }
595
596 // threadPane.js will be needing this, too
GetNumSelectedMessages
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
597 function GetNumSelectedMessages()
598 {
599 try {
600 return gSearchView.numSelected;
601 }
602 catch (ex) {
603 return 0;
604 }
605 }
606
GetDBView
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
607 function GetDBView()
608 {
609 return gSearchView;
610 }
611
MsgDeleteSelectedMessages
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
612 function MsgDeleteSelectedMessages(aCommandType)
613 {
614 // we don't delete news messages, we just return in that case
615 if (isNewsURI(gSearchView.getURIForViewIndex(0)))
616 return;
617
618 // if mail messages delete
619 SetNextMessageAfterDelete();
620 gSearchView.doCommand(aCommandType);
621 }
622
SetNextMessageAfterDelete
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
623 function SetNextMessageAfterDelete()
624 {
625 gNextMessageViewIndexAfterDelete = gSearchView.msgToSelectAfterDelete;
626 }
627
HandleDeleteOrMoveMessageFailed
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
628 function HandleDeleteOrMoveMessageFailed(folder)
629 {
630 gNextMessageViewIndexAfterDelete = -2;
631 }
632
HandleDeleteOrMoveMessageCompleted
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
633 function HandleDeleteOrMoveMessageCompleted(folder)
634 {
635 var treeView = gSearchView.QueryInterface(Components.interfaces.nsITreeView);
636 var treeSelection = treeView.selection;
637 var viewSize = treeView.rowCount;
638
639 if (gNextMessageViewIndexAfterDelete == -2) {
640 // a move or delete can cause our selection can change underneath us.
641 // this can happen when the user
642 // deletes message from the stand alone msg window
643 // or the three pane
644 if (!treeSelection) {
645 // this can happen if you open the search window
646 // and before you do any searches
647 // and you do delete from another mail window
648 return;
649 }
650 else if (treeSelection.count == 0) {
651 // this can happen if you double clicked a message
652 // in the thread pane, and deleted it from the stand alone msg window
653 // see bug #185147
654 treeSelection.clearSelection();
655
656 UpdateMailSearch("delete from another view, 0 rows now selected");
657 }
658 else if (treeSelection.count == 1) {
659 // this can happen if you had two messages selected
660 // in the search results pane, and you deleted one of them from another view
661 // (like the view in the stand alone msg window or the three pane)
662 // since one item is selected, we should load it.
663 var startIndex = {};
664 var endIndex = {};
665 treeSelection.getRangeAt(0, startIndex, endIndex);
666
667 // select the selected item, so we'll load it
668 treeSelection.select(startIndex.value);
669 treeView.selectionChanged();
670
671 EnsureRowInThreadTreeIsVisible(startIndex.value);
672 UpdateMailSearch("delete from another view, 1 row now selected");
673 }
674 else {
675 // this can happen if you have more than 2 messages selected
676 // in the search results pane, and you deleted one of them from another view
677 // (like the view in the stand alone msg window or the three pane)
678 // since multiple messages are still selected, do nothing.
679 }
680 }
681 else {
682 if (gNextMessageViewIndexAfterDelete != nsMsgViewIndex_None && gNextMessageViewIndexAfterDelete >= viewSize)
683 {
684 if (viewSize > 0)
685 gNextMessageViewIndexAfterDelete = viewSize - 1;
686 else
687 {
688 gNextMessageViewIndexAfterDelete = nsMsgViewIndex_None;
689
690 // there is nothing to select since viewSize is 0
691 treeSelection.clearSelection();
692
693 UpdateMailSearch("delete from current view, 0 rows left");
694 }
695 }
696
697 // if we are about to set the selection with a new element then DON'T clear
698 // the selection then add the next message to select. This just generates
699 // an extra round of command updating notifications that we are trying to
700 // optimize away.
701 if (gNextMessageViewIndexAfterDelete != nsMsgViewIndex_None)
702 {
703 treeSelection.select(gNextMessageViewIndexAfterDelete);
704 // since gNextMessageViewIndexAfterDelete probably has the same value
705 // as the last index we had selected, the tree isn't generating a new
706 // selectionChanged notification for the tree view. So we aren't loading the
707 // next message. to fix this, force the selection changed update.
708 if (treeView)
709 treeView.selectionChanged();
710
711 EnsureRowInThreadTreeIsVisible(gNextMessageViewIndexAfterDelete);
712
713 // XXX TODO
714 // I think there is a bug in the suppression code above.
715 // what if I have two rows selected, and I hit delete,
716 // and so we load the next row.
717 // what if I have commands that only enable where
718 // exactly one row is selected?
719 UpdateMailSearch("delete from current view, at least one row selected");
720 }
721 }
722
723 // default value after delete/move/copy is over
724 gNextMessageViewIndexAfterDelete = -2;
725
726 // something might have been deleted, so update the status text
727 SetAdvancedSearchStatusText(viewSize);
728 }
729
MoveMessageInSearch
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
730 function MoveMessageInSearch(destFolder)
731 {
732 try {
733 // get the msg folder we're moving messages into
734 // if the id (uri) is not set, use file-uri which is set for
735 // "File Here"
736 var destUri = destFolder.getAttribute('id');
737 if (destUri.length == 0) {
738 destUri = destFolder.getAttribute('file-uri')
739 }
740
741 var destResource = RDF.GetResource(destUri);
742
743 var destMsgFolder = destResource.QueryInterface(Components.interfaces.nsIMsgFolder);
744
745 // we don't move news messages, we copy them
746 if (isNewsURI(gSearchView.getURIForViewIndex(0))) {
747 gSearchView.doCommandWithFolder(nsMsgViewCommandType.copyMessages, destMsgFolder);
748 }
749 else {
750 SetNextMessageAfterDelete();
751 gSearchView.doCommandWithFolder(nsMsgViewCommandType.moveMessages, destMsgFolder);
752 }
753 }
754 catch (ex) {
755 dump("MsgMoveMessage failed: " + ex + "\n");
756 }
757 }
758
GoToFolder
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
759 function GoToFolder()
760 {
761 MsgOpenNewWindowForMsgHdr(gSearchView.hdrForFirstSelectedMessage);
762 }
763
BeginDragThreadPane
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
764 function BeginDragThreadPane(event)
765 {
766 // no search pane dnd yet
767 return false;
768 }
769
saveAsVirtualFolder
(0 calls, 0 incl. v-uS, 0 excl. v-uS)
770 function saveAsVirtualFolder()
771 {
772 var preselectedURI = gCurrentFolder.URI;
773 searchFolderURIs = preselectedURI;
774
775 var searchSubfolders = document.getElementById("checkSearchSubFolders").checked;
776 if (gCurrentFolder && (searchSubfolders || gCurrentFolder.isServer || gCurrentFolder.noSelect))
777 {
778 var subFolderURIs = AddSubFoldersToURI(gCurrentFolder);
779 if (subFolderURIs.length > 0)
780 searchFolderURIs += '|' + subFolderURIs;
781 }
782
783 var dialog = window.openDialog("chrome://messenger/content/virtualFolderProperties.xul", "",
784 "chrome,titlebar,modal,centerscreen",
785 {preselectedURI:preselectedURI,
786 searchTerms:gSearchSession.searchTerms,
787 searchFolderURIs: searchFolderURIs});
788 }