!import
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 *
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 var gLastMessageUriToLoad = null;
40 var gThreadPaneCommandUpdater = null;
41
ThreadPaneOnClick
42 function ThreadPaneOnClick(event)
43 {
44 // we only care about button 0 (left click) events
45 if (event.button != 0) return;
46
47 // we are already handling marking as read and flagging
48 // in nsMsgDBView.cpp
49 // so all we need to worry about here is double clicks
50 // and column header.
51 //
52 // we get in here for clicks on the "treecol" (headers)
53 // and the "scrollbarbutton" (scrollbar buttons)
54 // we don't want those events to cause a "double click"
55
56 var t = event.originalTarget;
57
58 if (t.localName == "treecol") {
59 HandleColumnClick(t.id);
60 }
61 else if (t.localName == "treechildren") {
62 var row = new Object;
63 var col = new Object;
64 var childElt = new Object;
65
66 var tree = GetThreadTree();
67 // figure out what cell the click was in
68 tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, childElt);
69 if (row.value == -1)
70 return;
71
72 // if the cell is in a "cycler" column
73 // or if the user double clicked on the twisty,
74 // don't open the message in a new window
75 if (event.detail == 2 && !col.value.cycler && (childElt.value != "twisty")) {
76 ThreadPaneDoubleClick();
77 // double clicking should not toggle the open / close state of the
78 // thread. this will happen if we don't prevent the event from
79 // bubbling to the default handler in tree.xml
80 event.stopPropagation();
81 }
82 else if (col.value.id == "junkStatusCol") {
83 MsgJunkMailInfo(true);
84 }
85 else if (col.value.id == "threadCol" && !event.shiftKey &&
86 (event.ctrlKey || event.metaKey)) {
87 gDBView.ExpandAndSelectThreadByIndex(row.value, true);
88 event.stopPropagation();
89 }
90 }
91 }
92
nsMsgDBViewCommandUpdater
93 function nsMsgDBViewCommandUpdater()
94 {}
95
96 nsMsgDBViewCommandUpdater.prototype =
97 {
updateCommandStatus
98 updateCommandStatus : function()
99 {
100 // the back end is smart and is only telling us to update command status
101 // when the # of items in the selection has actually changed.
102 UpdateMailToolbar("dbview driven, thread pane");
103 },
104
displayMessageChanged
105 displayMessageChanged : function(aFolder, aSubject, aKeywords)
106 {
107 if (!gDBView.suppressMsgDisplay)
108 setTitleFromFolder(aFolder, aSubject);
109 ClearPendingReadTimer(); // we are loading / selecting a new message so kill the mark as read timer for the currently viewed message
110 gHaveLoadedMessage = true;
111 goUpdateCommand("button_junk");
112 },
113
updateNextMessageAfterDelete
114 updateNextMessageAfterDelete : function()
115 {
116 SetNextMessageAfterDelete();
117 },
118
QueryInterface
119 QueryInterface : function(iid)
120 {
121 if (iid.equals(Components.interfaces.nsIMsgDBViewCommandUpdater) ||
122 iid.equals(Components.interfaces.nsISupports))
123 return this;
124
125 throw Components.results.NS_NOINTERFACE;
126 }
127 }
128
HandleColumnClick
129 function HandleColumnClick(columnID)
130 {
131 var sortType = ConvertColumnIDToSortType(columnID);
132
133 // if sortType is 0, this is an unsupported sort type
134 // return, since we can't sort by that column.
135 if (sortType == 0)
136 return;
137
138 var dbview = GetDBView();
139 var simpleColumns = false;
140 try {
141 simpleColumns = !pref.getBoolPref("mailnews.thread_pane_column_unthreads");
142 }
143 catch (ex) {
144 }
145 if (sortType == nsMsgViewSortType.byThread) {
146 if (!dbview.supportsThreading)
147 return;
148
149 if (simpleColumns)
150 MsgToggleThreaded();
151 else if (dbview.viewFlags & nsMsgViewFlagsType.kThreadedDisplay)
152 MsgReverseSortThreadPane();
153 else
154 MsgSortByThread();
155 }
156 else {
157 if (!simpleColumns && (dbview.viewFlags & nsMsgViewFlagsType.kThreadedDisplay)) {
158 dbview.viewFlags &= ~nsMsgViewFlagsType.kThreadedDisplay;
159 MsgSortThreadPane(sortType);
160 }
161 else if (dbview.sortType == sortType) {
162 MsgReverseSortThreadPane();
163 }
164 else {
165 MsgSortThreadPane(sortType);
166 }
167 }
168 }
169
MsgComposeDraftMessage
170 function MsgComposeDraftMessage()
171 {
172 var loadedFolder = GetLoadedMsgFolder();
173 var messageArray = GetSelectedMessages();
174
175 ComposeMessage(Components.interfaces.nsIMsgCompType.Draft,
176 Components.interfaces.nsIMsgCompFormat.Default,
177 loadedFolder, messageArray);
178 }
179
ThreadPaneDoubleClick
180 function ThreadPaneDoubleClick()
181 {
182 if (IsSpecialFolderSelected(MSG_FOLDER_FLAG_DRAFTS, true)) {
183 MsgComposeDraftMessage();
184 }
185 else if(IsSpecialFolderSelected(MSG_FOLDER_FLAG_TEMPLATES, true)) {
186 var loadedFolder = GetLoadedMsgFolder();
187 var messageArray = GetSelectedMessages();
188
189 ComposeMessage(Components.interfaces.nsIMsgCompType.Template,
190 Components.interfaces.nsIMsgCompFormat.Default,
191 loadedFolder, messageArray);
192 }
193 else {
194 MsgOpenSelectedMessages();
195 }
196 }
197
ThreadPaneKeyPress
198 function ThreadPaneKeyPress(event)
199 {
200 if (event.keyCode == 13)
201 ThreadPaneDoubleClick();
202 }
203
MsgSortByDate
204 function MsgSortByDate()
205 {
206 MsgSortThreadPane(nsMsgViewSortType.byDate);
207 }
208
MsgSortByReceived
209 function MsgSortByReceived()
210 {
211 MsgSortThreadPane(nsMsgViewSortType.byReceived);
212 }
213
MsgSortByFrom
214 function MsgSortByFrom()
215 {
216 MsgSortThreadPane(nsMsgViewSortType.byAuthor);
217 }
218
MsgSortByRecipient
219 function MsgSortByRecipient()
220 {
221 MsgSortThreadPane(nsMsgViewSortType.byRecipient);
222 }
223
MsgSortByStatus
224 function MsgSortByStatus()
225 {
226 MsgSortThreadPane(nsMsgViewSortType.byStatus);
227 }
228
MsgSortByTags
229 function MsgSortByTags()
230 {
231 MsgSortThreadPane(nsMsgViewSortType.byTags);
232 }
233
MsgSortByJunkStatus
234 function MsgSortByJunkStatus()
235 {
236 MsgSortThreadPane(nsMsgViewSortType.byJunkStatus);
237 }
238
MsgSortByAttachments
239 function MsgSortByAttachments()
240 {
241 MsgSortThreadPane(nsMsgViewSortType.byAttachments);
242 }
243
MsgSortBySubject
244 function MsgSortBySubject()
245 {
246 MsgSortThreadPane(nsMsgViewSortType.bySubject);
247 }
248
MsgSortByLocation
249 function MsgSortByLocation()
250 {
251 MsgSortThreadPane(nsMsgViewSortType.byLocation);
252 }
253
msgSortByAccount
254 function msgSortByAccount()
255 {
256 MsgSortThreadPane(nsMsgViewSortType.byAccount);
257 }
258
MsgSortByFlagged
259 function MsgSortByFlagged()
260 {
261 MsgSortThreadPane(nsMsgViewSortType.byFlagged);
262 }
263
MsgSortByPriority
264 function MsgSortByPriority()
265 {
266 MsgSortThreadPane(nsMsgViewSortType.byPriority);
267 }
268
MsgSortBySize
269 function MsgSortBySize()
270 {
271 MsgSortThreadPane(nsMsgViewSortType.bySize);
272 }
273
MsgSortByUnread
274 function MsgSortByUnread()
275 {
276 MsgSortThreadPane(nsMsgViewSortType.byUnread);
277 }
278
MsgSortByOrderReceived
279 function MsgSortByOrderReceived()
280 {
281 MsgSortThreadPane(nsMsgViewSortType.byId);
282 }
283
MsgSortByTotal
284 function MsgSortByTotal()
285 {
286 dump("XXX fix MsgSortByTotal\n");
287 //MsgSortThreadPane(nsMsgViewSortType.byTotal);
288 }
289
MsgSortByThread
290 function MsgSortByThread()
291 {
292 var dbview = GetDBView();
293 if(dbview && !dbview.supportsThreading)
294 return;
295 dbview.viewFlags |= nsMsgViewFlagsType.kThreadedDisplay;
296 dbview.viewFlags &= ~nsMsgViewFlagsType.kGroupBySort;
297 MsgSortThreadPane(nsMsgViewSortType.byDate);
298 }
299
MsgSortThreadPane
300 function MsgSortThreadPane(sortType)
301 {
302 var dbview = GetDBView();
303
304 if (dbview.viewFlags & nsMsgViewFlagsType.kGroupBySort)
305 {
306 dbview.viewFlags &= ~nsMsgViewFlagsType.kGroupBySort;
307 dbview.sortType = sortType; // save sort in current view
308 viewDebug("switching view to all msgs\n");
309 SwitchView("cmd_viewAllMsgs");
310 return;
311 }
312
313 dbview.sort(sortType, nsMsgViewSortOrder.ascending);
314 UpdateSortIndicators(sortType, nsMsgViewSortOrder.ascending);
315 }
316
MsgReverseSortThreadPane
317 function MsgReverseSortThreadPane()
318 {
319 var dbview = GetDBView();
320 if (dbview.sortOrder == nsMsgViewSortOrder.ascending) {
321 MsgSortDescending();
322 }
323 else {
324 MsgSortAscending();
325 }
326 }
327
MsgToggleThreaded
328 function MsgToggleThreaded()
329 {
330 var dbview = GetDBView();
331
332 dbview.viewFlags ^= nsMsgViewFlagsType.kThreadedDisplay;
333 if (dbview.viewFlags & nsMsgViewFlagsType.kGroupBySort)
334 {
335 dbview.viewFlags &= ~nsMsgViewFlagsType.kGroupBySort;
336 viewDebug("switching view to all msgs\n");
337 SwitchView("cmd_viewAllMsgs");
338 return;
339 }
340
341 dbview.sort(dbview.sortType, dbview.sortOrder);
342 UpdateSortIndicators(dbview.sortType, dbview.sortOrder);
343 }
344
MsgSortThreaded
345 function MsgSortThreaded()
346 {
347 var dbview = GetDBView();
348 var viewFlags = dbview.viewFlags;
349
350 if (viewFlags & nsMsgViewFlagsType.kGroupBySort)
351 {
352 dbview.viewFlags &= ~nsMsgViewFlagsType.kGroupBySort;
353 viewDebug("switching view to all msgs\n");
354 SwitchView("cmd_viewAllMsgs");
355 }
356 // Toggle if not already threaded.
357 else if ((viewFlags & nsMsgViewFlagsType.kThreadedDisplay) == 0)
358 MsgToggleThreaded();
359 }
360
MsgGroupBySort
361 function MsgGroupBySort()
362 {
363 var dbview = GetDBView();
364 var viewFlags = dbview.viewFlags;
365 var sortOrder = dbview.sortOrder;
366 var sortType = dbview.sortType;
367 var count = new Object;
368 var msgFolder = dbview.msgFolder;
369
370 var sortTypeSupportsGrouping = (sortType == nsMsgViewSortType.byAuthor
371 || sortType == nsMsgViewSortType.byDate || sortType == nsMsgViewSortType.byReceived || sortType == nsMsgViewSortType.byPriority
372 || sortType == nsMsgViewSortType.bySubject || sortType == nsMsgViewSortType.byTags
373 || sortType == nsMsgViewSortType.byStatus || sortType == nsMsgViewSortType.byRecipient
374 || sortType == nsMsgViewSortType.byAccount || sortType == nsMsgViewSortType.byFlagged
375 || sortType == nsMsgViewSortType.byAttachments);
376
377 if (!dbview.supportsThreading || !sortTypeSupportsGrouping)
378 return; // we shouldn't be trying to group something we don't support grouping for...
379
380 viewFlags |= nsMsgViewFlagsType.kThreadedDisplay | nsMsgViewFlagsType.kGroupBySort;
381 // null this out, so we don't try sort.
382 if (gDBView) {
383 gDBView.close();
384 gDBView = null;
385 }
386 var dbviewContractId = "@mozilla.org/messenger/msgdbview;1?type=group";
387 gDBView = Components.classes[dbviewContractId].createInstance(Components.interfaces.nsIMsgDBView);
388
389 if (!gThreadPaneCommandUpdater)
390 gThreadPaneCommandUpdater = new nsMsgDBViewCommandUpdater();
391
392
393 gDBView.init(messenger, msgWindow, gThreadPaneCommandUpdater);
394 gDBView.open(msgFolder, sortType, sortOrder, viewFlags, count);
395 RerootThreadPane();
396 UpdateSortIndicators(sortType, nsMsgViewSortOrder.ascending);
397 }
398
MsgSortUnthreaded
399 function MsgSortUnthreaded()
400 {
401 // Toggle if not already unthreaded.
402 if ((GetDBView().viewFlags & nsMsgViewFlagsType.kThreadedDisplay) != 0)
403 MsgToggleThreaded();
404 }
405
MsgSortAscending
406 function MsgSortAscending()
407 {
408 var dbview = GetDBView();
409 dbview.sort(dbview.sortType, nsMsgViewSortOrder.ascending);
410 UpdateSortIndicators(dbview.sortType, nsMsgViewSortOrder.ascending);
411 }
412
MsgSortDescending
413 function MsgSortDescending()
414 {
415 var dbview = GetDBView();
416 dbview.sort(dbview.sortType, nsMsgViewSortOrder.descending);
417 UpdateSortIndicators(dbview.sortType, nsMsgViewSortOrder.descending);
418 }
419
groupedBySortUsingDummyRow
420 function groupedBySortUsingDummyRow()
421 {
422 return (gDBView.viewFlags & nsMsgViewFlagsType.kGroupBySort) &&
423 (gDBView.sortType != nsMsgViewSortType.bySubject);
424 }
425
UpdateSortIndicators
426 function UpdateSortIndicators(sortType, sortOrder)
427 {
428 // Remove the sort indicator from all the columns
429 var treeColumns = document.getElementById('threadCols').childNodes;
430 for (var i = 0; i < treeColumns.length; i++)
431 treeColumns[i].removeAttribute('sortDirection');
432
433 // show the twisties if the view is threaded
434 var threadCol = document.getElementById("threadCol");
435 var sortedColumn;
436 // set the sort indicator on the column we are sorted by
437 var colID = ConvertSortTypeToColumnID(sortType);
438 if (colID)
439 sortedColumn = document.getElementById(colID);
440
441 var dbview = GetDBView();
442 var currCol = dbview.viewFlags & nsMsgViewFlagsType.kGroupBySort
443 ? sortedColumn : document.getElementById("subjectCol");
444
445 if (dbview.viewFlags & nsMsgViewFlagsType.kGroupBySort)
446 {
447 var threadTree = document.getElementById("threadTree");
448 var subjectCol = document.getElementById("subjectCol");
449
450 if (groupedBySortUsingDummyRow())
451 {
452 currCol.removeAttribute("primary");
453 subjectCol.setAttribute("primary", "true");
454 }
455
456 // hide the threaded column when in grouped view since you can't do
457 // threads inside of a group.
458 document.getElementById("threadCol").collapsed = true;
459 }
460
461 // clear primary attribute from group column if going to a non-grouped view.
462 if (!(dbview.viewFlags & nsMsgViewFlagsType.kGroupBySort))
463 document.getElementById("threadCol").collapsed = false;
464
465 if ((dbview.viewFlags & nsMsgViewFlagsType.kThreadedDisplay) && !groupedBySortUsingDummyRow()) {
466 threadCol.setAttribute("sortDirection", "ascending");
467 currCol.setAttribute("primary", "true");
468 }
469 else {
470 threadCol.removeAttribute("sortDirection");
471 currCol.removeAttribute("primary");
472 }
473
474 if (sortedColumn) {
475 if (sortOrder == nsMsgViewSortOrder.ascending) {
476 sortedColumn.setAttribute("sortDirection","ascending");
477 }
478 else {
479 sortedColumn.setAttribute("sortDirection","descending");
480 }
481 }
482 }
483
IsSpecialFolderSelected
484 function IsSpecialFolderSelected(flags, checkAncestors)
485 {
486 var selectedFolder = GetThreadPaneFolder();
487 return IsSpecialFolder(selectedFolder, flags, checkAncestors);
488 }
489
GetThreadTree
Called By: threadPane.js:ThreadPaneSelectionChanged (21 calls, 148 v-uS)
msgMail3PaneWindow.js:SetNextMessageAfterDelete (1 calls, 8 v-uS)
490 function GetThreadTree()
491 {
492 if (gThreadTree) return gThreadTree;
493 gThreadTree = document.getElementById('threadTree');
494 return gThreadTree;
495 }
496
GetThreadPaneFolder
Called By: msgMail3PaneWindow.js:IsCurrentLoadedFolder (1 calls, 19 v-uS)
497 function GetThreadPaneFolder()
498 {
499 try {
500 return gDBView.msgFolder;
501 }
502 catch (ex) {
503 return null;
504 }
505 }
506
EnsureRowInThreadTreeIsVisible
507 function EnsureRowInThreadTreeIsVisible(index)
508 {
509 if (index < 0)
510 return;
511
512 var tree = GetThreadTree();
513 tree.treeBoxObject.ensureRowIsVisible(index);
514 }
515
RerootThreadPane
516 function RerootThreadPane()
517 {
518 SetNewsFolderColumns();
519
520 var treeView = gDBView.QueryInterface(Components.interfaces.nsITreeView);
521 if (treeView)
522 {
523 var tree = GetThreadTree();
524 tree.boxObject.QueryInterface(Components.interfaces.nsITreeBoxObject).view = treeView;
525 }
526 }
527
ThreadPaneOnLoad
528 function ThreadPaneOnLoad()
529 {
530 var tree = GetThreadTree();
531
532 tree.addEventListener("click",ThreadPaneOnClick,true);
533
534 // The mousedown event listener below should only be added in the thread
535 // pane of the mailnews 3pane window, not in the advanced search window.
536 if(tree.parentNode.id == "searchResultListBox")
537 return;
538
539 tree.addEventListener("mousedown",TreeOnMouseDown,true);
540 }
541
ThreadPaneSelectionChanged
Called: ChromeWindow:selectionChanged (21 calls, 6796 v-uS)
XULElement:QueryInterface (21 calls, 252 v-uS)
commandglue.js:UpdateStatusMessageCounts (21 calls, 5787 v-uS)
threadPane.js:GetThreadTree (21 calls, 148 v-uS)
Called By: ChromeWindow:onDeleteCompleted (20 calls, 7302 v-uS)
ChromeWindow:clearSelection (1 calls, 7002 v-uS)
542 function ThreadPaneSelectionChanged()
543 {
544 UpdateStatusMessageCounts(gMsgFolderSelected);
545 if (!gRightMouseButtonDown)
546 GetThreadTree().view.selectionChanged();
547 }
548
549 addEventListener("load",ThreadPaneOnLoad,true);