File: threadPane.js

package info (click to toggle)
icedove 1%3A45.8.0-3~deb8u1
  • links: PTS, VCS
  • area: main
  • in suites: jessie
  • size: 1,488,584 kB
  • ctags: 1,068,813
  • sloc: cpp: 4,801,496; ansic: 1,929,291; python: 379,296; java: 252,018; xml: 173,182; asm: 146,741; sh: 89,229; makefile: 23,462; perl: 16,380; objc: 4,088; yacc: 1,841; lex: 1,222; exp: 499; php: 437; lisp: 228; awk: 152; pascal: 116; sed: 51; ruby: 47; csh: 31; ada: 16
file content (390 lines) | stat: -rw-r--r-- 12,132 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

var gLastMessageUriToLoad = null;
var gThreadPaneCommandUpdater = null;

function ThreadPaneOnClick(event)
{
  // We only care about button 0 (left click) events.
  if (event.button != 0) {
    event.stopPropagation();
    return;
  }

  // We already handle marking as read/flagged/junk cyclers in nsMsgDBView.cpp
  // so all we need to worry about here is doubleclicks and column header. We
  // get here for clicks on the "treecol" (headers) and the "scrollbarbutton"
  // (scrollbar buttons) and don't want those events to cause a doubleclick.

  let t = event.originalTarget;

  if (t.localName == "treecol") {
    HandleColumnClick(t.id);
    return;
  }

  if (t.localName != "treechildren")
    return;

  let row = {};
  let col = {};
  let elt = {};
  let tree = GetThreadTree();

  // Figure out what cell the click was in.
  tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, elt);
  if (row.value == -1)
   return;

  // Grouped By Sort dummy header row non cycler column doubleclick toggles the
  // thread's open/close state; tree.xml handles it. Cyclers are not currently
  // implemented in group header rows, a click/doubleclick there should
  // select/toggle thread state.
  if (gFolderDisplay.view.isGroupedByHeaderAtIndex(row.value)) {
    if (!col.value.cycler)
      return;

    if (event.detail == 1)
      gFolderDisplay.selectViewIndex(row.value);
    if (event.detail == 2)
      gFolderDisplay.view.dbView.toggleOpenState(row.value);

    event.stopPropagation();
    return;
  }

  // If the cell is in a cycler column or if the user doubleclicked on the
  // twisty, don't open the message in a new window.
  if (event.detail == 2 && !col.value.cycler && elt.value != "twisty") {
    ThreadPaneDoubleClick();
    // Doubleclicking should not toggle the open/close state of the thread.
    // This will happen if we don't prevent the event from bubbling to the
    // default handler in tree.xml.
    event.stopPropagation();
  }
  else if (col.value.id == "junkStatusCol") {
    MsgJunkMailInfo(true);
  }
  else if (col.value.id == "threadCol" && !event.shiftKey &&
           (event.ctrlKey || event.metaKey)) {
    gDBView.ExpandAndSelectThreadByIndex(row.value, true);
    event.stopPropagation();
  }
}

function HandleColumnClick(columnID)
{
  if (gFolderDisplay.COLUMNS_MAP_NOSORT.has(columnID))
    return;

  let sortType = gFolderDisplay.COLUMNS_MAP.get(columnID);
  let curCustomColumn = gDBView.curCustomColumn;
  if (!sortType) {
    // If the column isn't in the map, check if it's a custom column.
    try {
      // Test for the columnHandler (an error is thrown if it does not exist).
      gDBView.getColumnHandler(columnID);

      // Handler is registered - set column to be the current custom column.
      gDBView.curCustomColumn = columnID;
      sortType = "byCustom";
    }
    catch (ex) {
      dump("HandleColumnClick: No custom column handler registered for " +
           "columnID: " + columnID + " - " + ex + "\n");
      return;
    }
  }

  let viewWrapper = gFolderDisplay.view;
  let simpleColumns = false;
  try {
    simpleColumns = !Services.prefs.getBoolPref("mailnews.thread_pane_column_unthreads");
  }
  catch (ex) {
  }

  if (sortType == "byThread") {
    if (simpleColumns)
      MsgToggleThreaded();
    else if (viewWrapper.showThreaded)
      MsgReverseSortThreadPane();
    else
      MsgSortByThread();

    return;
  }

  if (!simpleColumns && viewWrapper.showThreaded) {
    viewWrapper.showUnthreaded = true;
    MsgSortThreadPane(sortType);
    return;
  }

  if (viewWrapper.primarySortType == nsMsgViewSortType[sortType] &&
      (viewWrapper.primarySortType != nsMsgViewSortType.byCustom ||
       curCustomColumn == columnID)) {
    MsgReverseSortThreadPane();
  }
  else {
    MsgSortThreadPane(sortType);
  }
}

function ThreadPaneDoubleClick()
{
  const nsMsgFolderFlags = Components.interfaces.nsMsgFolderFlags;
  if (IsSpecialFolderSelected(nsMsgFolderFlags.Drafts, true)) {
    MsgComposeDraftMessage();
  }
  else if(IsSpecialFolderSelected(nsMsgFolderFlags.Templates, true)) {
    ComposeMessage(Components.interfaces.nsIMsgCompType.Template,
                   Components.interfaces.nsIMsgCompFormat.Default,
                   gFolderDisplay.displayedFolder,
                   gFolderDisplay.selectedMessageUris);
  }
  else {
    MsgOpenSelectedMessages();
  }
}

function ThreadPaneKeyDown(event)
{
  if (event.keyCode != KeyEvent.DOM_VK_RETURN)
    return;

  // Grouped By Sort dummy header row <enter> toggles the thread's open/close
  // state. Let tree.xml handle it.
  if (gFolderDisplay.view.showGroupedBySort &&
      gFolderDisplay.treeSelection && gFolderDisplay.treeSelection.count == 1 &&
      gFolderDisplay.view.isGroupedByHeaderAtIndex(gFolderDisplay.treeSelection.currentIndex)) {
    return;
  }

  // Prevent any thread that happens to be last selected (currentIndex) in a
  // single or multi selection from toggling in tree.xml.
  event.stopImmediatePropagation();

  ThreadPaneDoubleClick();
}

function MsgSortByThread()
{
  gFolderDisplay.view.showThreaded = true;
  MsgSortThreadPane('byDate');
}

function MsgSortThreadPane(sortName)
{
  let sortType = nsMsgViewSortType[sortName];
  let grouped = gFolderDisplay.view.showGroupedBySort;
  gFolderDisplay.view._threadExpandAll =
    Boolean(gFolderDisplay.view._viewFlags & nsMsgViewFlagsType.kExpandAll);

  if (!grouped) {
    gFolderDisplay.view.sort(sortType, nsMsgViewSortOrder.ascending);
    // Respect user's last expandAll/collapseAll choice, post sort direction change.
    gFolderDisplay.restoreThreadState();
    return;
  }

  // legacy behavior dictates we un-group-by-sort if we were.  this probably
  //  deserves a UX call...

  // For non virtual folders, do not ungroup (which sorts by the going away
  // sort) and then sort, as it's a double sort.
  // For virtual folders, which are rebuilt in the backend in a grouped
  // change, create a new view upfront rather than applying viewFlags. There
  // are oddities just applying viewFlags, for example changing out of a
  // custom column grouped xfvf view with the threads collapsed works (doesn't)
  // differently than other variations.
  // So, first set the desired sortType and sortOrder, then set viewFlags in
  // batch mode, then apply it all (open a new view) with endViewUpdate().
  gFolderDisplay.view.beginViewUpdate();
  gFolderDisplay.view._sort = [[sortType, nsMsgViewSortOrder.ascending]];
  gFolderDisplay.view.showGroupedBySort = false;
  gFolderDisplay.view.endViewUpdate();

  // Virtual folders don't persist viewFlags well in the back end,
  // due to a virtual folder being either 'real' or synthetic, so make
  // sure it's done here.
  if (gFolderDisplay.view.isVirtual)
    gFolderDisplay.view.dbView.viewFlags = gFolderDisplay.view.viewFlags;
}

function MsgReverseSortThreadPane()
{
  let grouped = gFolderDisplay.view.showGroupedBySort;
  gFolderDisplay.view._threadExpandAll =
    Boolean(gFolderDisplay.view._viewFlags & nsMsgViewFlagsType.kExpandAll);

  // Grouped By view is special for column click sort direction changes.
  if (grouped) {
    if (gDBView.selection.count)
      gFolderDisplay._saveSelection();

    if (gFolderDisplay.view.isSingleFolder) {
      if (gFolderDisplay.view.isVirtual)
        gFolderDisplay.view.showGroupedBySort = false;
      else
       // Must ensure rows are collapsed and kExpandAll is unset.
       gFolderDisplay.doCommand(nsMsgViewCommandType.collapseAll);
    }
  }

  if (gFolderDisplay.view.isSortedAscending)
    gFolderDisplay.view.sortDescending();
  else
    gFolderDisplay.view.sortAscending();

  // Restore Grouped By state post sort direction change.
  if (grouped) {
    if (gFolderDisplay.view.isVirtual && gFolderDisplay.view.isSingleFolder)
      MsgGroupBySort();

    // Restore Grouped By selection post sort direction change.
    gFolderDisplay._restoreSelection();
  }

  // Respect user's last expandAll/collapseAll choice, for both threaded and grouped
  // views, post sort direction change.
  gFolderDisplay.restoreThreadState();
}

function MsgToggleThreaded()
{
  if (gFolderDisplay.view.showThreaded)
    gFolderDisplay.view.showUnthreaded = true;
  else
    gFolderDisplay.view.showThreaded = true;
}

function MsgSortThreaded()
{
  gFolderDisplay.view.showThreaded = true;
}

function MsgGroupBySort()
{
  gFolderDisplay.view.showGroupedBySort = true;
}

function MsgSortUnthreaded()
{
  gFolderDisplay.view.showUnthreaded = true;
}

function MsgSortAscending()
{
  if (gFolderDisplay.view.showGroupedBySort && gFolderDisplay.view.isSingleFolder) {
    if (gFolderDisplay.view.isSortedDescending)
       MsgReverseSortThreadPane();

    return;
  }

  gFolderDisplay.view.sortAscending();
}

function MsgSortDescending()
{
  if (gFolderDisplay.view.showGroupedBySort && gFolderDisplay.view.isSingleFolder) {
    if (gFolderDisplay.view.isSortedAscending)
       MsgReverseSortThreadPane();

    return;
  }

  gFolderDisplay.view.sortDescending();
}

// XXX this should probably migrate into FolderDisplayWidget, or whatever
//  FolderDisplayWidget ends up using if it refactors column management out.
function UpdateSortIndicators(sortType, sortOrder)
{
  // Remove the sort indicator from all the columns
  var treeColumns = document.getElementById('threadCols').childNodes;
  for (var i = 0; i < treeColumns.length; i++)
    treeColumns[i].removeAttribute("sortDirection");

  // show the twisties if the view is threaded
  var threadCol = document.getElementById("threadCol");
  var subjectCol = document.getElementById("subjectCol");
  var sortedColumn;
  // set the sort indicator on the column we are sorted by
  var colID = ConvertSortTypeToColumnID(sortType);
  if (colID)
    sortedColumn = document.getElementById(colID);

  var viewWrapper = gFolderDisplay.view;

  // the thread column is not visible when we are grouped by sort
  document.getElementById("threadCol").collapsed = viewWrapper.showGroupedBySort;

  // show twisties only when grouping or threading
  if (viewWrapper.showGroupedBySort || viewWrapper.showThreaded)
    subjectCol.setAttribute("primary", "true");
  else
    subjectCol.removeAttribute("primary");

  // If threading, set the sort direction on the thread column which causes it
  //  to be able to 'light up' or otherwise indicate threading is active.
  if (viewWrapper.showThreaded)
    threadCol.setAttribute("sortDirection", "ascending");

  if (sortedColumn)
    sortedColumn.setAttribute("sortDirection",
                              sortOrder == nsMsgViewSortOrder.ascending ?
                                "ascending" : "descending");
}

function IsSpecialFolderSelected(flags, checkAncestors)
{
  let folder = GetThreadPaneFolder();
  return folder && folder.isSpecialFolder(flags, checkAncestors);
}

function GetThreadTree()
{
  return document.getElementById("threadTree")
}

function GetThreadPaneFolder()
{
  try {
    return gDBView.msgFolder;
  }
  catch (ex) {
    return null;
  }
}

function ThreadPaneOnLoad()
{
  var tree = GetThreadTree();
  // We won't have the tree if we're in a message window, so exit silently
  if (!tree)
    return;

  tree.addEventListener("click",ThreadPaneOnClick,true);

  // The mousedown event listener below should only be added in the thread
  // pane of the mailnews 3pane window, not in the advanced search window.
  if(tree.parentNode.id == "searchResultListBox")
    return;

  tree.addEventListener("mousedown",TreeOnMouseDown,true);
  let delay = Services.prefs.getIntPref("mailnews.threadpane_select_delay");
  document.getElementById("threadTree")._selectDelay = delay;
}

function ThreadPaneSelectionChanged()
{
  UpdateStatusMessageCounts(gFolderDisplay.displayedFolder);
  GetThreadTree().view.selectionChanged();
}

addEventListener("load",ThreadPaneOnLoad,true);