|
1 <?xml version="1.0"?> |
|
2 <!-- This Source Code Form is subject to the terms of the Mozilla Public |
|
3 - License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> |
|
5 |
|
6 |
|
7 <!DOCTYPE bindings [ |
|
8 <!ENTITY % treeDTD SYSTEM "chrome://global/locale/tree.dtd"> |
|
9 %treeDTD; |
|
10 ]> |
|
11 |
|
12 <bindings id="treeBindings" |
|
13 xmlns="http://www.mozilla.org/xbl" |
|
14 xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" |
|
15 xmlns:xbl="http://www.mozilla.org/xbl"> |
|
16 |
|
17 <binding id="tree-base" extends="chrome://global/content/bindings/general.xml#basecontrol"> |
|
18 <resources> |
|
19 <stylesheet src="chrome://global/skin/tree.css"/> |
|
20 </resources> |
|
21 <implementation> |
|
22 <method name="_isAccelPressed"> |
|
23 <parameter name="aEvent"/> |
|
24 <body><![CDATA[ |
|
25 # Workaround until bug 302174 is fixed |
|
26 #ifdef XP_MACOSX |
|
27 return aEvent.metaKey; |
|
28 #else |
|
29 return aEvent.ctrlKey; |
|
30 #endif |
|
31 ]]></body> |
|
32 </method> |
|
33 </implementation> |
|
34 </binding> |
|
35 |
|
36 <binding id="tree" extends="chrome://global/content/bindings/tree.xml#tree-base" role="xul:tree"> |
|
37 <content hidevscroll="true" hidehscroll="true" clickthrough="never"> |
|
38 <children includes="treecols"/> |
|
39 <xul:stack class="tree-stack" flex="1"> |
|
40 <xul:treerows class="tree-rows" flex="1" xbl:inherits="hidevscroll"> |
|
41 <children/> |
|
42 </xul:treerows> |
|
43 <xul:textbox anonid="input" class="tree-input" left="0" top="0" hidden="true"/> |
|
44 </xul:stack> |
|
45 <xul:hbox xbl:inherits="collapsed=hidehscroll"> |
|
46 <xul:scrollbar orient="horizontal" flex="1" increment="16" style="position:relative; z-index:2147483647;"/> |
|
47 <xul:scrollcorner xbl:inherits="collapsed=hidevscroll"/> |
|
48 </xul:hbox> |
|
49 </content> |
|
50 |
|
51 <implementation implements="nsIDOMXULTreeElement, nsIDOMXULMultiSelectControlElement"> |
|
52 |
|
53 <!-- ///////////////// nsIDOMXULTreeElement ///////////////// --> |
|
54 |
|
55 <property name="columns" |
|
56 onget="return this.treeBoxObject.columns;"/> |
|
57 |
|
58 <property name="view" |
|
59 onget="return this.treeBoxObject.view;" |
|
60 onset="return this.treeBoxObject.view = val;"/> |
|
61 |
|
62 <property name="body" |
|
63 onget="return this.treeBoxObject.treeBody;"/> |
|
64 |
|
65 <property name="editable" |
|
66 onget="return this.getAttribute('editable') == 'true';" |
|
67 onset="if (val) this.setAttribute('editable', 'true'); |
|
68 else this.removeAttribute('editable'); return val;"/> |
|
69 |
|
70 <!-- ///////////////// nsIDOMXULSelectControlElement ///////////////// --> |
|
71 |
|
72 <!-- ///////////////// nsIDOMXULMultiSelectControlElement ///////////////// --> |
|
73 |
|
74 <property name="selType" |
|
75 onget="return this.getAttribute('seltype')" |
|
76 onset="this.setAttribute('seltype', val); return val;"/> |
|
77 |
|
78 <property name="currentIndex" |
|
79 onget="return this.view ? this.view.selection.currentIndex: - 1;" |
|
80 onset="if (this.view) return this.view.selection.currentIndex = val; return val;"/> |
|
81 |
|
82 <property name="treeBoxObject" |
|
83 onget="return this.boxObject.QueryInterface(Components.interfaces.nsITreeBoxObject);" |
|
84 readonly="true"/> |
|
85 # contentView is obsolete (see bug 202391) |
|
86 <property name="contentView" |
|
87 onget="return this.view; /*.QueryInterface(Components.interfaces.nsITreeContentView)*/" |
|
88 readonly="true"/> |
|
89 # builderView is obsolete (see bug 202393) |
|
90 <property name="builderView" |
|
91 onget="return this.view; /*.QueryInterface(Components.interfaces.nsIXULTreeBuilder)*/" |
|
92 readonly="true"/> |
|
93 <field name="pageUpOrDownMovesSelection"> |
|
94 #ifdef XP_MACOSX |
|
95 false |
|
96 #else |
|
97 true |
|
98 #endif |
|
99 </field> |
|
100 <property name="keepCurrentInView" |
|
101 onget="return (this.getAttribute('keepcurrentinview') == 'true');" |
|
102 onset="if (val) this.setAttribute('keepcurrentinview', 'true'); |
|
103 else this.removeAttribute('keepcurrentinview'); return val;"/> |
|
104 |
|
105 <property name="enableColumnDrag" |
|
106 onget="return this.hasAttribute('enableColumnDrag');" |
|
107 onset="if (val) this.setAttribute('enableColumnDrag', 'true'); |
|
108 else this.removeAttribute('enableColumnDrag'); return val;"/> |
|
109 |
|
110 <field name="_inputField">null</field> |
|
111 |
|
112 <property name="inputField" readonly="true"> |
|
113 <getter><![CDATA[ |
|
114 if (!this._inputField) |
|
115 this._inputField = document.getAnonymousElementByAttribute(this, "anonid", "input"); |
|
116 return this._inputField; |
|
117 ]]></getter> |
|
118 </property> |
|
119 |
|
120 <property name="disableKeyNavigation" |
|
121 onget="return this.hasAttribute('disableKeyNavigation');" |
|
122 onset="if (val) this.setAttribute('disableKeyNavigation', 'true'); |
|
123 else this.removeAttribute('disableKeyNavigation'); return val;"/> |
|
124 |
|
125 <field name="_editingRow">-1</field> |
|
126 <field name="_editingColumn">null</field> |
|
127 |
|
128 <property name="editingRow" readonly="true" |
|
129 onget="return this._editingRow;"/> |
|
130 <property name="editingColumn" readonly="true" |
|
131 onget="return this._editingColumn;"/> |
|
132 |
|
133 <property name="_selectDelay" |
|
134 onset="this.setAttribute('_selectDelay', val);" |
|
135 onget="return this.getAttribute('_selectDelay') || 50;"/> |
|
136 <field name="_columnsDirty">true</field> |
|
137 <field name="_lastKeyTime">0</field> |
|
138 <field name="_incrementalString">""</field> |
|
139 |
|
140 <method name="_ensureColumnOrder"> |
|
141 <body><![CDATA[ |
|
142 if (!this._columnsDirty) |
|
143 return; |
|
144 |
|
145 if (this.columns) { |
|
146 // update the ordinal position of each column to assure that it is |
|
147 // an odd number and 2 positions above its next sibling |
|
148 var cols = []; |
|
149 var i; |
|
150 for (var col = this.columns.getFirstColumn(); col; col = col.getNext()) |
|
151 cols.push(col.element); |
|
152 for (i = 0; i < cols.length; ++i) |
|
153 cols[i].setAttribute("ordinal", (i*2)+1); |
|
154 |
|
155 // update the ordinal positions of splitters to even numbers, so that |
|
156 // they are in between columns |
|
157 var splitters = this.getElementsByTagName("splitter"); |
|
158 for (i = 0; i < splitters.length; ++i) |
|
159 splitters[i].setAttribute("ordinal", (i+1)*2); |
|
160 } |
|
161 this._columnsDirty = false; |
|
162 ]]></body> |
|
163 </method> |
|
164 |
|
165 <method name="_reorderColumn"> |
|
166 <parameter name="aColMove"/> |
|
167 <parameter name="aColBefore"/> |
|
168 <parameter name="aBefore"/> |
|
169 <body><![CDATA[ |
|
170 this._ensureColumnOrder(); |
|
171 |
|
172 var i; |
|
173 var cols = []; |
|
174 var col = this.columns.getColumnFor(aColBefore); |
|
175 if (parseInt(aColBefore.ordinal) < parseInt(aColMove.ordinal)) { |
|
176 if (aBefore) |
|
177 cols.push(aColBefore); |
|
178 for (col = col.getNext(); col.element != aColMove; |
|
179 col = col.getNext()) |
|
180 cols.push(col.element); |
|
181 |
|
182 aColMove.ordinal = cols[0].ordinal; |
|
183 for (i = 0; i < cols.length; ++i) |
|
184 cols[i].ordinal = parseInt(cols[i].ordinal) + 2; |
|
185 } else if (aColBefore.ordinal != aColMove.ordinal) { |
|
186 if (!aBefore) |
|
187 cols.push(aColBefore); |
|
188 for (col = col.getPrevious(); col.element != aColMove; |
|
189 col = col.getPrevious()) |
|
190 cols.push(col.element); |
|
191 |
|
192 aColMove.ordinal = cols[0].ordinal; |
|
193 for (i = 0; i < cols.length; ++i) |
|
194 cols[i].ordinal = parseInt(cols[i].ordinal) - 2; |
|
195 } |
|
196 ]]></body> |
|
197 </method> |
|
198 |
|
199 <method name="_getColumnAtX"> |
|
200 <parameter name="aX"/> |
|
201 <parameter name="aThresh"/> |
|
202 <parameter name="aPos"/> |
|
203 <body><![CDATA[ |
|
204 var isRTL = document.defaultView.getComputedStyle(this, "") |
|
205 .direction == "rtl"; |
|
206 |
|
207 if (aPos) |
|
208 aPos.value = isRTL ? "after" : "before"; |
|
209 |
|
210 var columns = []; |
|
211 var col = this.columns.getFirstColumn(); |
|
212 while (col) { |
|
213 columns.push(col); |
|
214 col = col.getNext(); |
|
215 } |
|
216 if (isRTL) |
|
217 columns.reverse(); |
|
218 var currentX = this.boxObject.x; |
|
219 var adjustedX = aX + this.treeBoxObject.horizontalPosition; |
|
220 for (var i = 0; i < columns.length; ++i) { |
|
221 col = columns[i]; |
|
222 var cw = col.element.boxObject.width; |
|
223 if (cw > 0) { |
|
224 currentX += cw; |
|
225 if (currentX - (cw * aThresh) > adjustedX) |
|
226 return col.element; |
|
227 } |
|
228 } |
|
229 |
|
230 if (aPos) |
|
231 aPos.value = isRTL ? "before" : "after"; |
|
232 return columns.pop().element; |
|
233 ]]></body> |
|
234 </method> |
|
235 |
|
236 <method name="changeOpenState"> |
|
237 <parameter name="row"/> |
|
238 <!-- Optional parameter openState == true or false to set. |
|
239 No openState param == toggle --> |
|
240 <parameter name="openState"/> |
|
241 <body><![CDATA[ |
|
242 if (row < 0 || !this.view.isContainer(row)) { |
|
243 return false; |
|
244 } |
|
245 if (this.view.isContainerOpen(row) != openState) { |
|
246 this.view.toggleOpenState(row); |
|
247 if (row == this.currentIndex) { |
|
248 // Only fire event when current row is expanded or collapsed |
|
249 // because that's all the assistive technology really cares about. |
|
250 var event = document.createEvent('Events'); |
|
251 event.initEvent('OpenStateChange', true, true); |
|
252 this.dispatchEvent(event); |
|
253 } |
|
254 return true; |
|
255 } |
|
256 return false; |
|
257 ]]></body> |
|
258 </method> |
|
259 |
|
260 <property name="_cellSelType"> |
|
261 <getter> |
|
262 <![CDATA[ |
|
263 var seltype = this.selType; |
|
264 if (seltype == "cell" || seltype == "text") |
|
265 return seltype; |
|
266 return null; |
|
267 ]]> |
|
268 </getter> |
|
269 </property> |
|
270 |
|
271 <method name="_getNextColumn"> |
|
272 <parameter name="row"/> |
|
273 <parameter name="left"/> |
|
274 <body><![CDATA[ |
|
275 var col = this.view.selection.currentColumn; |
|
276 if (col) { |
|
277 col = left ? col.getPrevious() : col.getNext(); |
|
278 } |
|
279 else { |
|
280 col = this.columns.getKeyColumn(); |
|
281 } |
|
282 while (col && (col.width == 0 || !col.selectable || |
|
283 !this.view.isSelectable(row, col))) |
|
284 col = left ? col.getPrevious() : col.getNext(); |
|
285 return col; |
|
286 ]]></body> |
|
287 </method> |
|
288 |
|
289 <method name="_keyNavigate"> |
|
290 <parameter name="event"/> |
|
291 <body><![CDATA[ |
|
292 var key = String.fromCharCode(event.charCode).toLowerCase(); |
|
293 if (event.timeStamp - this._lastKeyTime > 1000) |
|
294 this._incrementalString = key; |
|
295 else |
|
296 this._incrementalString += key; |
|
297 this._lastKeyTime = event.timeStamp; |
|
298 |
|
299 var length = this._incrementalString.length; |
|
300 var incrementalString = this._incrementalString; |
|
301 var charIndex = 1; |
|
302 while (charIndex < length && incrementalString[charIndex] == incrementalString[charIndex - 1]) |
|
303 charIndex++; |
|
304 // If all letters in incremental string are same, just try to match the first one |
|
305 if (charIndex == length) { |
|
306 length = 1; |
|
307 incrementalString = incrementalString.substring(0, length); |
|
308 } |
|
309 |
|
310 var keyCol = this.columns.getKeyColumn(); |
|
311 var rowCount = this.view.rowCount; |
|
312 var start = 1; |
|
313 |
|
314 var c = this.currentIndex; |
|
315 if (length > 1) { |
|
316 start = 0; |
|
317 if (c < 0) |
|
318 c = 0; |
|
319 } |
|
320 |
|
321 for (var i = 0; i < rowCount; i++) { |
|
322 var l = (i + start + c) % rowCount; |
|
323 var cellText = this.view.getCellText(l, keyCol); |
|
324 cellText = cellText.substring(0, length).toLowerCase(); |
|
325 if (cellText == incrementalString) |
|
326 return l; |
|
327 } |
|
328 return -1; |
|
329 ]]></body> |
|
330 </method> |
|
331 |
|
332 <method name="startEditing"> |
|
333 <parameter name="row"/> |
|
334 <parameter name="column"/> |
|
335 <body> |
|
336 <![CDATA[ |
|
337 if (!this.editable) |
|
338 return false; |
|
339 if (row < 0 || row >= this.view.rowCount || !column) |
|
340 return false; |
|
341 if (column.type != Components.interfaces.nsITreeColumn.TYPE_TEXT || |
|
342 column.cycler || !this.view.isEditable(row, column)) |
|
343 return false; |
|
344 |
|
345 // Beyond this point, we are going to edit the cell. |
|
346 if (this._editingColumn) |
|
347 this.stopEditing(); |
|
348 |
|
349 var input = this.inputField; |
|
350 |
|
351 var box = this.treeBoxObject; |
|
352 box.ensureCellIsVisible(row, column); |
|
353 |
|
354 // Get the coordinates of the text inside the cell. |
|
355 var textx = {}, texty = {}, textwidth = {}, textheight = {}; |
|
356 var coords = box.getCoordsForCellItem(row, column, "text", |
|
357 textx, texty, textwidth, textheight); |
|
358 |
|
359 // Get the coordinates of the cell itself. |
|
360 var cellx = {}, cellwidth = {}; |
|
361 coords = box.getCoordsForCellItem(row, column, "cell", |
|
362 cellx, {}, cellwidth, {}); |
|
363 |
|
364 // Calculate the top offset of the textbox. |
|
365 var style = window.getComputedStyle(input, ""); |
|
366 var topadj = parseInt(style.borderTopWidth) + parseInt(style.paddingTop); |
|
367 input.top = texty.value - topadj; |
|
368 |
|
369 // The leftside of the textbox is aligned to the left side of the text |
|
370 // in LTR mode, and left side of the cell in RTL mode. |
|
371 var left, widthdiff; |
|
372 if (style.direction == "rtl") { |
|
373 left = cellx.value; |
|
374 widthdiff = cellx.value + cellwidth.value - textx.value - textwidth.value; |
|
375 } else { |
|
376 left = textx.value; |
|
377 widthdiff = textx.value - cellx.value; |
|
378 } |
|
379 |
|
380 input.left = left; |
|
381 input.height = textheight.value + topadj + |
|
382 parseInt(style.borderBottomWidth) + |
|
383 parseInt(style.paddingBottom); |
|
384 input.width = cellwidth.value - widthdiff; |
|
385 input.hidden = false; |
|
386 |
|
387 input.value = this.view.getCellText(row, column); |
|
388 var selectText = function selectText() { |
|
389 input.select(); |
|
390 input.inputField.focus(); |
|
391 } |
|
392 setTimeout(selectText, 0); |
|
393 |
|
394 this._editingRow = row; |
|
395 this._editingColumn = column; |
|
396 |
|
397 this.setAttribute("editing", "true"); |
|
398 return true; |
|
399 ]]> |
|
400 </body> |
|
401 </method> |
|
402 |
|
403 <method name="stopEditing"> |
|
404 <parameter name="accept"/> |
|
405 <body> |
|
406 <![CDATA[ |
|
407 if (!this._editingColumn) |
|
408 return; |
|
409 |
|
410 var input = this.inputField; |
|
411 var editingRow = this._editingRow; |
|
412 var editingColumn = this._editingColumn; |
|
413 this._editingRow = -1; |
|
414 this._editingColumn = null; |
|
415 if (accept) { |
|
416 var value = input.value; |
|
417 this.view.setCellText(editingRow, editingColumn, value); |
|
418 } |
|
419 |
|
420 input.hidden = true; |
|
421 input.value = ""; |
|
422 this.removeAttribute("editing"); |
|
423 ]]> |
|
424 </body> |
|
425 </method> |
|
426 |
|
427 <method name="_moveByOffset"> |
|
428 <parameter name="offset"/> |
|
429 <parameter name="edge"/> |
|
430 <parameter name="event"/> |
|
431 <body> |
|
432 <![CDATA[ |
|
433 if (this._editingColumn || this.view.rowCount == 0) |
|
434 return; |
|
435 |
|
436 if (this._isAccelPressed(event) && this.view.selection.single) { |
|
437 this.treeBoxObject.scrollByLines(offset); |
|
438 return; |
|
439 } |
|
440 |
|
441 var c = this.currentIndex + offset; |
|
442 if (offset > 0 ? c > edge : c < edge) { |
|
443 if (this.view.selection.isSelected(edge) && this.view.selection.count <= 1) |
|
444 return; |
|
445 c = edge; |
|
446 } |
|
447 |
|
448 var cellSelType = this._cellSelType; |
|
449 if (cellSelType) { |
|
450 var column = this.view.selection.currentColumn; |
|
451 if (!column) |
|
452 return; |
|
453 |
|
454 while ((offset > 0 ? c <= edge : c >= edge) && !this.view.isSelectable(c, column)) |
|
455 c += offset; |
|
456 if (offset > 0 ? c > edge : c < edge) |
|
457 return; |
|
458 } |
|
459 |
|
460 if (!this._isAccelPressed(event)) |
|
461 this.view.selection.timedSelect(c, this._selectDelay); |
|
462 else // Ctrl+Up/Down moves the anchor without selecting |
|
463 this.currentIndex = c; |
|
464 this.treeBoxObject.ensureRowIsVisible(c); |
|
465 ]]> |
|
466 </body> |
|
467 </method> |
|
468 |
|
469 <method name="_moveByOffsetShift"> |
|
470 <parameter name="offset"/> |
|
471 <parameter name="edge"/> |
|
472 <parameter name="event"/> |
|
473 <body> |
|
474 <![CDATA[ |
|
475 if (this._editingColumn || this.view.rowCount == 0) |
|
476 return; |
|
477 |
|
478 if (this.view.selection.single) { |
|
479 this.treeBoxObject.scrollByLines(offset); |
|
480 return; |
|
481 } |
|
482 |
|
483 if (this.view.rowCount == 1 && !this.view.selection.isSelected(0)) { |
|
484 this.view.selection.timedSelect(0, this._selectDelay); |
|
485 return; |
|
486 } |
|
487 |
|
488 var c = this.currentIndex; |
|
489 if (c == -1) |
|
490 c = 0; |
|
491 |
|
492 if (c == edge) { |
|
493 if (this.view.selection.isSelected(c)) |
|
494 return; |
|
495 } |
|
496 |
|
497 // Extend the selection from the existing pivot, if any |
|
498 this.view.selection.rangedSelect(-1, c + offset, |
|
499 this._isAccelPressed(event)); |
|
500 this.treeBoxObject.ensureRowIsVisible(c + offset); |
|
501 |
|
502 ]]> |
|
503 </body> |
|
504 </method> |
|
505 |
|
506 <method name="_moveByPage"> |
|
507 <parameter name="offset"/> |
|
508 <parameter name="edge"/> |
|
509 <parameter name="event"/> |
|
510 <body> |
|
511 <![CDATA[ |
|
512 if (this._editingColumn || this.view.rowCount == 0) |
|
513 return; |
|
514 |
|
515 if (this.pageUpOrDownMovesSelection == this._isAccelPressed(event)) { |
|
516 this.treeBoxObject.scrollByPages(offset); |
|
517 return; |
|
518 } |
|
519 |
|
520 if (this.view.rowCount == 1 && !this.view.selection.isSelected(0)) { |
|
521 this.view.selection.timedSelect(0, this._selectDelay); |
|
522 return; |
|
523 } |
|
524 |
|
525 var c = this.currentIndex; |
|
526 if (c == -1) |
|
527 return; |
|
528 |
|
529 if (c == edge && this.view.selection.isSelected(c)) { |
|
530 this.treeBoxObject.ensureRowIsVisible(c); |
|
531 return; |
|
532 } |
|
533 var i = this.treeBoxObject.getFirstVisibleRow(); |
|
534 var p = this.treeBoxObject.getPageLength(); |
|
535 |
|
536 if (offset > 0) { |
|
537 i += p - 1; |
|
538 if (c >= i) { |
|
539 i = c + p; |
|
540 this.treeBoxObject.ensureRowIsVisible(i > edge ? edge : i); |
|
541 } |
|
542 i = i > edge ? edge : i; |
|
543 |
|
544 } else { |
|
545 if (c <= i) { |
|
546 i = c <= p ? 0 : c - p; |
|
547 this.treeBoxObject.ensureRowIsVisible(i); |
|
548 } |
|
549 } |
|
550 this.view.selection.timedSelect(i, this._selectDelay); |
|
551 ]]> |
|
552 </body> |
|
553 </method> |
|
554 |
|
555 <method name="_moveByPageShift"> |
|
556 <parameter name="offset"/> |
|
557 <parameter name="edge"/> |
|
558 <parameter name="event"/> |
|
559 <body> |
|
560 <![CDATA[ |
|
561 if (this._editingColumn || this.view.rowCount == 0) |
|
562 return; |
|
563 |
|
564 if (this.view.rowCount == 1 && !this.view.selection.isSelected(0) && |
|
565 !(this.pageUpOrDownMovesSelection == this._isAccelPressed(event))) { |
|
566 this.view.selection.timedSelect(0, this._selectDelay); |
|
567 return; |
|
568 } |
|
569 |
|
570 if (this.view.selection.single) |
|
571 return; |
|
572 |
|
573 var c = this.currentIndex; |
|
574 if (c == -1) |
|
575 return; |
|
576 if (c == edge && this.view.selection.isSelected(c)) { |
|
577 this.treeBoxObject.ensureRowIsVisible(edge); |
|
578 return; |
|
579 } |
|
580 var i = this.treeBoxObject.getFirstVisibleRow(); |
|
581 var p = this.treeBoxObject.getPageLength(); |
|
582 |
|
583 if (offset > 0) { |
|
584 i += p - 1; |
|
585 if (c >= i) { |
|
586 i = c + p; |
|
587 this.treeBoxObject.ensureRowIsVisible(i > edge ? edge : i); |
|
588 } |
|
589 // Extend the selection from the existing pivot, if any |
|
590 this.view.selection.rangedSelect(-1, i > edge ? edge : i, this._isAccelPressed(event)); |
|
591 |
|
592 } else { |
|
593 |
|
594 if (c <= i) { |
|
595 i = c <= p ? 0 : c - p; |
|
596 this.treeBoxObject.ensureRowIsVisible(i); |
|
597 } |
|
598 // Extend the selection from the existing pivot, if any |
|
599 this.view.selection.rangedSelect(-1, i, this._isAccelPressed(event)); |
|
600 } |
|
601 |
|
602 ]]> |
|
603 </body> |
|
604 </method> |
|
605 |
|
606 <method name="_moveToEdge"> |
|
607 <parameter name="edge"/> |
|
608 <parameter name="event"/> |
|
609 <body> |
|
610 <![CDATA[ |
|
611 if (this._editingColumn || this.view.rowCount == 0) |
|
612 return; |
|
613 |
|
614 if (this.view.selection.isSelected(edge) && this.view.selection.count == 1) { |
|
615 this.currentIndex = edge; |
|
616 return; |
|
617 } |
|
618 |
|
619 // Normal behaviour is to select the first/last row |
|
620 if (!this._isAccelPressed(event)) |
|
621 this.view.selection.timedSelect(edge, this._selectDelay); |
|
622 |
|
623 // In a multiselect tree Ctrl+Home/End moves the anchor |
|
624 else if (!this.view.selection.single) |
|
625 this.currentIndex = edge; |
|
626 |
|
627 this.treeBoxObject.ensureRowIsVisible(edge); |
|
628 ]]> |
|
629 </body> |
|
630 </method> |
|
631 |
|
632 <method name="_moveToEdgeShift"> |
|
633 <parameter name="edge"/> |
|
634 <parameter name="event"/> |
|
635 <body> |
|
636 <![CDATA[ |
|
637 if (this._editingColumn || this.view.rowCount == 0) |
|
638 return; |
|
639 |
|
640 if (this.view.rowCount == 1 && !this.view.selection.isSelected(0)) { |
|
641 this.view.selection.timedSelect(0, this._selectDelay); |
|
642 return; |
|
643 } |
|
644 |
|
645 if (this.view.selection.single || |
|
646 (this.view.selection.isSelected(edge)) && this.view.selection.isSelected(this.currentIndex)) |
|
647 return; |
|
648 |
|
649 // Extend the selection from the existing pivot, if any. |
|
650 // -1 doesn't work here, so using currentIndex instead |
|
651 this.view.selection.rangedSelect(this.currentIndex, edge, this._isAccelPressed(event)); |
|
652 |
|
653 this.treeBoxObject.ensureRowIsVisible(edge); |
|
654 ]]> |
|
655 </body> |
|
656 </method> |
|
657 <method name="_handleEnter"> |
|
658 <parameter name="event"/> |
|
659 <body><![CDATA[ |
|
660 if (this._editingColumn) { |
|
661 this.stopEditing(true); |
|
662 this.focus(); |
|
663 return true; |
|
664 } |
|
665 |
|
666 #ifdef XP_MACOSX |
|
667 // See if we can edit the cell. |
|
668 var row = this.currentIndex; |
|
669 if (this._cellSelType) { |
|
670 var column = this.view.selection.currentColumn; |
|
671 var startedEditing = this.startEditing(row, column); |
|
672 if (startedEditing) |
|
673 return true; |
|
674 } |
|
675 #endif |
|
676 return this.changeOpenState(this.currentIndex); |
|
677 ]]></body> |
|
678 </method> |
|
679 </implementation> |
|
680 |
|
681 <handlers> |
|
682 <handler event="MozMousePixelScroll" preventdefault="true"/> |
|
683 <handler event="DOMMouseScroll" preventdefault="true"> |
|
684 <![CDATA[ |
|
685 if (this._editingColumn) |
|
686 return; |
|
687 if (event.axis == event.HORIZONTAL_AXIS) |
|
688 return; |
|
689 |
|
690 var rows = event.detail; |
|
691 if (rows == UIEvent.SCROLL_PAGE_UP) |
|
692 this.treeBoxObject.scrollByPages(-1); |
|
693 else if (rows == UIEvent.SCROLL_PAGE_DOWN) |
|
694 this.treeBoxObject.scrollByPages(1); |
|
695 else |
|
696 this.treeBoxObject.scrollByLines(rows); |
|
697 ]]> |
|
698 </handler> |
|
699 <handler event="MozSwipeGesture" preventdefault="true"> |
|
700 <![CDATA[ |
|
701 // Figure out which row to show |
|
702 let targetRow = 0; |
|
703 |
|
704 // Only handle swipe gestures up and down |
|
705 switch (event.direction) { |
|
706 case event.DIRECTION_DOWN: |
|
707 targetRow = this.view.rowCount - 1; |
|
708 // Fall through for actual action |
|
709 case event.DIRECTION_UP: |
|
710 this.treeBoxObject.ensureRowIsVisible(targetRow); |
|
711 break; |
|
712 } |
|
713 ]]> |
|
714 </handler> |
|
715 <handler event="select" phase="target" |
|
716 action="if (event.originalTarget == this) this.stopEditing(true);"/> |
|
717 <handler event="focus"> |
|
718 <![CDATA[ |
|
719 this.treeBoxObject.focused = true; |
|
720 if (this.currentIndex == -1 && this.view.rowCount > 0) { |
|
721 this.currentIndex = this.treeBoxObject.getFirstVisibleRow(); |
|
722 } |
|
723 if (this._cellSelType && !this.view.selection.currentColumn) { |
|
724 var col = this._getNextColumn(this.currentIndex, false); |
|
725 this.view.selection.currentColumn = col; |
|
726 } |
|
727 ]]> |
|
728 </handler> |
|
729 <handler event="blur" action="this.treeBoxObject.focused = false;"/> |
|
730 <handler event="blur" phase="capturing" |
|
731 action="if (event.originalTarget == this.inputField.inputField) this.stopEditing(true);"/> |
|
732 <handler event="keydown" keycode="VK_RETURN"> |
|
733 if (this._handleEnter(event)) { |
|
734 event.stopPropagation(); |
|
735 event.preventDefault(); |
|
736 } |
|
737 </handler> |
|
738 #ifndef XP_MACOSX |
|
739 <!-- Use F2 key to enter text editing. --> |
|
740 <handler event="keydown" keycode="VK_F2"> |
|
741 <![CDATA[ |
|
742 if (!this._cellSelType) |
|
743 return; |
|
744 var row = this.currentIndex; |
|
745 var column = this.view.selection.currentColumn; |
|
746 if (this.startEditing(row, column)) |
|
747 event.preventDefault(); |
|
748 ]]> |
|
749 </handler> |
|
750 #endif // XP_MACOSX |
|
751 |
|
752 <handler event="keydown" keycode="VK_ESCAPE"> |
|
753 <![CDATA[ |
|
754 if (this._editingColumn) { |
|
755 this.stopEditing(false); |
|
756 this.focus(); |
|
757 event.stopPropagation(); |
|
758 event.preventDefault(); |
|
759 } |
|
760 ]]> |
|
761 </handler> |
|
762 <handler event="keydown" keycode="VK_LEFT"> |
|
763 <![CDATA[ |
|
764 if (this._editingColumn) |
|
765 return; |
|
766 |
|
767 var row = this.currentIndex; |
|
768 if (row < 0) |
|
769 return; |
|
770 |
|
771 var cellSelType = this._cellSelType; |
|
772 var checkContainers = true; |
|
773 |
|
774 var currentColumn; |
|
775 if (cellSelType) { |
|
776 currentColumn = this.view.selection.currentColumn; |
|
777 if (currentColumn && !currentColumn.primary) |
|
778 checkContainers = false; |
|
779 } |
|
780 |
|
781 if (checkContainers) { |
|
782 if (this.changeOpenState(this.currentIndex, false)) { |
|
783 event.preventDefault(); |
|
784 return; |
|
785 } |
|
786 else { |
|
787 var parentIndex = this.view.getParentIndex(this.currentIndex); |
|
788 if (parentIndex >= 0) { |
|
789 if (cellSelType && !this.view.isSelectable(parentIndex, currentColumn)) { |
|
790 return; |
|
791 } |
|
792 this.view.selection.select(parentIndex); |
|
793 this.treeBoxObject.ensureRowIsVisible(parentIndex); |
|
794 event.preventDefault(); |
|
795 return; |
|
796 } |
|
797 } |
|
798 } |
|
799 |
|
800 if (cellSelType) { |
|
801 var col = this._getNextColumn(row, true); |
|
802 if (col) { |
|
803 this.view.selection.currentColumn = col; |
|
804 this.treeBoxObject.ensureCellIsVisible(row, col); |
|
805 event.preventDefault(); |
|
806 } |
|
807 } |
|
808 ]]> |
|
809 </handler> |
|
810 <handler event="keydown" keycode="VK_RIGHT"> |
|
811 <![CDATA[ |
|
812 if (this._editingColumn) |
|
813 return; |
|
814 |
|
815 var row = this.currentIndex; |
|
816 if (row < 0) |
|
817 return; |
|
818 |
|
819 var cellSelType = this._cellSelType; |
|
820 var checkContainers = true; |
|
821 |
|
822 var currentColumn; |
|
823 if (cellSelType) { |
|
824 currentColumn = this.view.selection.currentColumn; |
|
825 if (currentColumn && !currentColumn.primary) |
|
826 checkContainers = false; |
|
827 } |
|
828 |
|
829 if (checkContainers) { |
|
830 if (this.changeOpenState(row, true)) { |
|
831 event.preventDefault(); |
|
832 return; |
|
833 } |
|
834 else { |
|
835 var c = row + 1; |
|
836 var view = this.view; |
|
837 if (c < view.rowCount && |
|
838 view.getParentIndex(c) == row) { |
|
839 // If already opened, select the first child. |
|
840 // The getParentIndex test above ensures that the children |
|
841 // are already populated and ready. |
|
842 if (cellSelType && !this.view.isSelectable(c , currentColumn)) { |
|
843 var col = this._getNextColumn(c, false); |
|
844 if (col) { |
|
845 this.view.selection.currentColumn = col; |
|
846 } |
|
847 } |
|
848 this.view.selection.timedSelect(c, this._selectDelay); |
|
849 this.treeBoxObject.ensureRowIsVisible(c); |
|
850 event.preventDefault(); |
|
851 return; |
|
852 } |
|
853 } |
|
854 } |
|
855 |
|
856 if (cellSelType) { |
|
857 var col = this._getNextColumn(row, false); |
|
858 if (col) { |
|
859 this.view.selection.currentColumn = col; |
|
860 this.treeBoxObject.ensureCellIsVisible(row, col); |
|
861 event.preventDefault(); |
|
862 } |
|
863 } |
|
864 ]]> |
|
865 </handler> |
|
866 <handler event="keydown" keycode="VK_UP" preventdefault="true" |
|
867 modifiers="accel any" action="_moveByOffset(-1, 0, event);"/> |
|
868 <handler event="keydown" keycode="VK_DOWN" preventdefault="true" |
|
869 modifiers="accel any" action="_moveByOffset(1, this.view.rowCount - 1, event);"/> |
|
870 <handler event="keydown" keycode="VK_UP" preventdefault="true" |
|
871 modifiers="accel any, shift" action="_moveByOffsetShift(-1, 0, event);"/> |
|
872 <handler event="keydown" keycode="VK_DOWN" preventdefault="true" |
|
873 modifiers="accel any, shift" action="_moveByOffsetShift(1, this.view.rowCount - 1, event);"/> |
|
874 <handler event="keydown" keycode="VK_PAGE_UP" preventdefault="true" |
|
875 modifiers="accel any" action="_moveByPage(-1, 0, event);"/> |
|
876 <handler event="keydown" keycode="VK_PAGE_DOWN" preventdefault="true" |
|
877 modifiers="accel any" action="_moveByPage(1, this.view.rowCount - 1, event);"/> |
|
878 <handler event="keydown" keycode="VK_PAGE_UP" preventdefault="true" |
|
879 modifiers="accel any, shift" action="_moveByPageShift(-1, 0, event);"/> |
|
880 <handler event="keydown" keycode="VK_PAGE_DOWN" preventdefault="true" |
|
881 modifiers="accel any, shift" action="_moveByPageShift(1, this.view.rowCount - 1, event);"/> |
|
882 <handler event="keydown" keycode="VK_HOME" preventdefault="true" |
|
883 modifiers="accel any" action="_moveToEdge(0, event);"/> |
|
884 <handler event="keydown" keycode="VK_END" preventdefault="true" |
|
885 modifiers="accel any" action="_moveToEdge(this.view.rowCount - 1, event);"/> |
|
886 <handler event="keydown" keycode="VK_HOME" preventdefault="true" |
|
887 modifiers="accel any, shift" action="_moveToEdgeShift(0, event);"/> |
|
888 <handler event="keydown" keycode="VK_END" preventdefault="true" |
|
889 modifiers="accel any, shift" action="_moveToEdgeShift(this.view.rowCount - 1, event);"/> |
|
890 <handler event="keypress"> |
|
891 <![CDATA[ |
|
892 if (this._editingColumn) |
|
893 return; |
|
894 |
|
895 if (event.charCode == ' '.charCodeAt(0)) { |
|
896 var c = this.currentIndex; |
|
897 if (!this.view.selection.isSelected(c) || |
|
898 (!this.view.selection.single && this._isAccelPressed(event))) { |
|
899 this.view.selection.toggleSelect(c); |
|
900 event.preventDefault(); |
|
901 } |
|
902 } |
|
903 else if (!this.disableKeyNavigation && event.charCode > 0 && |
|
904 !event.altKey && !this._isAccelPressed(event) && |
|
905 !event.metaKey && !event.ctrlKey) { |
|
906 var l = this._keyNavigate(event); |
|
907 if (l >= 0) { |
|
908 this.view.selection.timedSelect(l, this._selectDelay); |
|
909 this.treeBoxObject.ensureRowIsVisible(l); |
|
910 } |
|
911 event.preventDefault(); |
|
912 } |
|
913 ]]> |
|
914 </handler> |
|
915 </handlers> |
|
916 </binding> |
|
917 |
|
918 <binding id="treecols" role="xul:treecolumns"> |
|
919 <resources> |
|
920 <stylesheet src="chrome://global/skin/tree.css"/> |
|
921 </resources> |
|
922 <content orient="horizontal"> |
|
923 <xul:hbox class="tree-scrollable-columns" flex="1"> |
|
924 <children includes="treecol|splitter"/> |
|
925 </xul:hbox> |
|
926 <xul:treecolpicker class="treecol-image" fixed="true" xbl:inherits="tooltiptext=pickertooltiptext"/> |
|
927 </content> |
|
928 <implementation> |
|
929 <constructor><![CDATA[ |
|
930 // Set resizeafter="farthest" on the splitters if nothing else has been |
|
931 // specified. |
|
932 Array.forEach(this.getElementsByTagName("splitter"), function (splitter) { |
|
933 if (!splitter.hasAttribute("resizeafter")) |
|
934 splitter.setAttribute("resizeafter", "farthest"); |
|
935 }); |
|
936 ]]></constructor> |
|
937 </implementation> |
|
938 </binding> |
|
939 |
|
940 <binding id="treerows" extends="chrome://global/content/bindings/tree.xml#tree-base"> |
|
941 <content> |
|
942 <xul:hbox flex="1" class="tree-bodybox"> |
|
943 <children/> |
|
944 </xul:hbox> |
|
945 <xul:scrollbar height="0" minwidth="0" minheight="0" orient="vertical" xbl:inherits="collapsed=hidevscroll" style="position:relative; z-index:2147483647;"/> |
|
946 </content> |
|
947 <handlers> |
|
948 <handler event="underflow"> |
|
949 <![CDATA[ |
|
950 // Scrollport event orientation |
|
951 // 0: vertical |
|
952 // 1: horizontal |
|
953 // 2: both (not used) |
|
954 var tree = document.getBindingParent(this); |
|
955 if (event.detail == 1) |
|
956 tree.setAttribute("hidehscroll", "true"); |
|
957 else if (event.detail == 0) |
|
958 tree.setAttribute("hidevscroll", "true"); |
|
959 event.stopPropagation(); |
|
960 ]]> |
|
961 </handler> |
|
962 <handler event="overflow"> |
|
963 <![CDATA[ |
|
964 var tree = document.getBindingParent(this); |
|
965 if (event.detail == 1) |
|
966 tree.removeAttribute("hidehscroll"); |
|
967 else if (event.detail == 0) |
|
968 tree.removeAttribute("hidevscroll"); |
|
969 event.stopPropagation(); |
|
970 ]]> |
|
971 </handler> |
|
972 </handlers> |
|
973 </binding> |
|
974 |
|
975 <binding id="treebody" extends="chrome://global/content/bindings/tree.xml#tree-base"> |
|
976 <implementation> |
|
977 <constructor> |
|
978 if ("_ensureColumnOrder" in this.parentNode) |
|
979 this.parentNode._ensureColumnOrder(); |
|
980 </constructor> |
|
981 |
|
982 <field name="_lastSelectedRow"> |
|
983 -1 |
|
984 </field> |
|
985 </implementation> |
|
986 <handlers> |
|
987 <!-- If there is no modifier key, we select on mousedown, not |
|
988 click, so that drags work correctly. --> |
|
989 <handler event="mousedown" clickcount="1"> |
|
990 <![CDATA[ |
|
991 if (this.parentNode.disabled) |
|
992 return; |
|
993 if (((!this._isAccelPressed(event) || |
|
994 !this.parentNode.pageUpOrDownMovesSelection) && |
|
995 !event.shiftKey && !event.metaKey) || |
|
996 this.parentNode.view.selection.single) { |
|
997 var row = {}; |
|
998 var col = {}; |
|
999 var obj = {}; |
|
1000 var b = this.parentNode.treeBoxObject; |
|
1001 b.getCellAt(event.clientX, event.clientY, row, col, obj); |
|
1002 |
|
1003 // save off the last selected row |
|
1004 this._lastSelectedRow = row.value; |
|
1005 |
|
1006 if (row.value == -1) |
|
1007 return; |
|
1008 |
|
1009 if (obj.value == "twisty") |
|
1010 return; |
|
1011 |
|
1012 if (col.value) { |
|
1013 if (col.value.cycler) { |
|
1014 b.view.cycleCell(row.value, col.value); |
|
1015 return; |
|
1016 } else if (col.value.type == Components.interfaces.nsITreeColumn.TYPE_CHECKBOX) { |
|
1017 if (this.parentNode.editable && col.value.editable && |
|
1018 b.view.isEditable(row.value, col.value)) { |
|
1019 var value = b.view.getCellValue(row.value, col.value); |
|
1020 value = value == "true" ? "false" : "true"; |
|
1021 b.view.setCellValue(row.value, col.value, value); |
|
1022 return; |
|
1023 } |
|
1024 } |
|
1025 } |
|
1026 |
|
1027 var cellSelType = this.parentNode._cellSelType; |
|
1028 if (cellSelType == "text" && obj.value != "text" && obj.value != "image") |
|
1029 return; |
|
1030 |
|
1031 if (cellSelType) { |
|
1032 if (!col.value.selectable || |
|
1033 !b.view.isSelectable(row.value, col.value)) { |
|
1034 return; |
|
1035 } |
|
1036 } |
|
1037 |
|
1038 if (!b.view.selection.isSelected(row.value)) { |
|
1039 b.view.selection.select(row.value); |
|
1040 b.ensureRowIsVisible(row.value); |
|
1041 } |
|
1042 |
|
1043 if (cellSelType) { |
|
1044 b.view.selection.currentColumn = col.value; |
|
1045 } |
|
1046 } |
|
1047 ]]> |
|
1048 </handler> |
|
1049 |
|
1050 <!-- On a click (up+down on the same item), deselect everything |
|
1051 except this item. --> |
|
1052 <handler event="click" button="0" clickcount="1"> |
|
1053 <![CDATA[ |
|
1054 if (this.parentNode.disabled) |
|
1055 return; |
|
1056 var row = {}; |
|
1057 var col = {}; |
|
1058 var obj = {}; |
|
1059 var b = this.parentNode.treeBoxObject; |
|
1060 b.getCellAt(event.clientX, event.clientY, row, col, obj); |
|
1061 |
|
1062 if (row.value == -1) |
|
1063 return; |
|
1064 |
|
1065 if (obj.value == "twisty") { |
|
1066 if (b.view.selection.currentIndex >= 0 && |
|
1067 b.view.isContainerOpen(row.value)) { |
|
1068 var parentIndex = b.view.getParentIndex(b.view.selection.currentIndex); |
|
1069 while (parentIndex >= 0 && parentIndex != row.value) |
|
1070 parentIndex = b.view.getParentIndex(parentIndex); |
|
1071 if (parentIndex == row.value) { |
|
1072 var parentSelectable = true; |
|
1073 if (this.parentNode._cellSelType) { |
|
1074 var currentColumn = b.view.selection.currentColumn; |
|
1075 if (!b.view.isSelectable(parentIndex, currentColumn)) |
|
1076 parentSelectable = false; |
|
1077 } |
|
1078 if (parentSelectable) |
|
1079 b.view.selection.select(parentIndex); |
|
1080 } |
|
1081 } |
|
1082 this.parentNode.changeOpenState(row.value); |
|
1083 return; |
|
1084 } |
|
1085 |
|
1086 if (! b.view.selection.single) { |
|
1087 var augment = this._isAccelPressed(event); |
|
1088 if (event.shiftKey) { |
|
1089 b.view.selection.rangedSelect(-1, row.value, augment); |
|
1090 b.ensureRowIsVisible(row.value); |
|
1091 return; |
|
1092 } |
|
1093 if (augment) { |
|
1094 b.view.selection.toggleSelect(row.value); |
|
1095 b.ensureRowIsVisible(row.value); |
|
1096 b.view.selection.currentIndex = row.value; |
|
1097 return; |
|
1098 } |
|
1099 } |
|
1100 |
|
1101 /* We want to deselect all the selected items except what was |
|
1102 clicked, UNLESS it was a right-click. We have to do this |
|
1103 in click rather than mousedown so that you can drag a |
|
1104 selected group of items */ |
|
1105 |
|
1106 if (!col.value) return; |
|
1107 |
|
1108 // if the last row has changed in between the time we |
|
1109 // mousedown and the time we click, don't fire the select handler. |
|
1110 // see bug #92366 |
|
1111 if (!col.value.cycler && this._lastSelectedRow == row.value && |
|
1112 col.value.type != Components.interfaces.nsITreeColumn.TYPE_CHECKBOX) { |
|
1113 |
|
1114 var cellSelType = this.parentNode._cellSelType; |
|
1115 if (cellSelType == "text" && obj.value != "text" && obj.value != "image") |
|
1116 return; |
|
1117 |
|
1118 if (cellSelType) { |
|
1119 if (!col.value.selectable || |
|
1120 !b.view.isSelectable(row.value, col.value)) { |
|
1121 return; |
|
1122 } |
|
1123 } |
|
1124 |
|
1125 b.view.selection.select(row.value); |
|
1126 b.ensureRowIsVisible(row.value); |
|
1127 |
|
1128 if (cellSelType) { |
|
1129 b.view.selection.currentColumn = col.value; |
|
1130 } |
|
1131 } |
|
1132 ]]> |
|
1133 </handler> |
|
1134 |
|
1135 <!-- double-click --> |
|
1136 <handler event="click" clickcount="2"> |
|
1137 <![CDATA[ |
|
1138 if (this.parentNode.disabled) |
|
1139 return; |
|
1140 var tbo = this.parentNode.treeBoxObject; |
|
1141 var row = tbo.view.selection.currentIndex; |
|
1142 if (row == -1) |
|
1143 return; |
|
1144 |
|
1145 var col = {}; |
|
1146 var obj = {}; |
|
1147 tbo.getCellAt(event.clientX, event.clientY, {}, col, obj); |
|
1148 |
|
1149 if (obj.value != "twisty") |
|
1150 this.parentNode.startEditing(row, col.value); |
|
1151 |
|
1152 if (this.parentNode._editingColumn || !tbo.view.isContainer(row)) |
|
1153 return; |
|
1154 |
|
1155 // Cyclers and twisties respond to single clicks, not double clicks |
|
1156 if (col.value != -1 && !col.value.cycler && obj.value != "twisty") |
|
1157 this.parentNode.changeOpenState(row); |
|
1158 ]]> |
|
1159 </handler> |
|
1160 |
|
1161 </handlers> |
|
1162 </binding> |
|
1163 |
|
1164 <binding id="treecol-base" role="xul:treecolumnitem" |
|
1165 extends="chrome://global/content/bindings/tree.xml#tree-base"> |
|
1166 <implementation> |
|
1167 <constructor> |
|
1168 this.parentNode.parentNode._columnsDirty = true; |
|
1169 </constructor> |
|
1170 |
|
1171 <property name="ordinal"> |
|
1172 <getter><![CDATA[ |
|
1173 var val = this.getAttribute("ordinal"); |
|
1174 return "" + (val == "" ? 1 : (val == "0" ? 0 : parseInt(val))); |
|
1175 ]]></getter> |
|
1176 <setter><![CDATA[ |
|
1177 this.setAttribute("ordinal", val); |
|
1178 return val; |
|
1179 ]]></setter> |
|
1180 </property> |
|
1181 |
|
1182 <property name="_previousVisibleColumn"> |
|
1183 <getter><![CDATA[ |
|
1184 var sib = this.boxObject.previousSibling; |
|
1185 while (sib) { |
|
1186 if (sib.localName == "treecol" && sib.boxObject.width > 0 && sib.parentNode == this.parentNode) |
|
1187 return sib; |
|
1188 sib = sib.boxObject.previousSibling; |
|
1189 } |
|
1190 return null; |
|
1191 ]]></getter> |
|
1192 </property> |
|
1193 |
|
1194 <method name="_onDragMouseMove"> |
|
1195 <parameter name="aEvent"/> |
|
1196 <body><![CDATA[ |
|
1197 var col = document.treecolDragging; |
|
1198 if (!col) return; |
|
1199 |
|
1200 // determine if we have moved the mouse far enough |
|
1201 // to initiate a drag |
|
1202 if (col.mDragGesturing) { |
|
1203 if (Math.abs(aEvent.clientX - col.mStartDragX) < 5 && |
|
1204 Math.abs(aEvent.clientY - col.mStartDragY) < 5) { |
|
1205 return; |
|
1206 } else { |
|
1207 col.mDragGesturing = false; |
|
1208 col.setAttribute("dragging", "true"); |
|
1209 window.addEventListener("click", col._onDragMouseClick, true); |
|
1210 } |
|
1211 } |
|
1212 |
|
1213 var pos = {}; |
|
1214 var targetCol = col.parentNode.parentNode._getColumnAtX(aEvent.clientX, 0.5, pos); |
|
1215 |
|
1216 // bail if we haven't mousemoved to a different column |
|
1217 if (col.mTargetCol == targetCol && col.mTargetDir == pos.value) |
|
1218 return; |
|
1219 |
|
1220 var tree = col.parentNode.parentNode; |
|
1221 var sib; |
|
1222 var column; |
|
1223 if (col.mTargetCol) { |
|
1224 // remove previous insertbefore/after attributes |
|
1225 col.mTargetCol.removeAttribute("insertbefore"); |
|
1226 col.mTargetCol.removeAttribute("insertafter"); |
|
1227 column = tree.columns.getColumnFor(col.mTargetCol); |
|
1228 tree.treeBoxObject.invalidateColumn(column); |
|
1229 sib = col.mTargetCol._previousVisibleColumn; |
|
1230 if (sib) { |
|
1231 sib.removeAttribute("insertafter"); |
|
1232 column = tree.columns.getColumnFor(sib); |
|
1233 tree.treeBoxObject.invalidateColumn(column); |
|
1234 } |
|
1235 col.mTargetCol = null; |
|
1236 col.mTargetDir = null; |
|
1237 } |
|
1238 |
|
1239 if (targetCol) { |
|
1240 // set insertbefore/after attributes |
|
1241 if (pos.value == "after") { |
|
1242 targetCol.setAttribute("insertafter", "true"); |
|
1243 } else { |
|
1244 targetCol.setAttribute("insertbefore", "true"); |
|
1245 sib = targetCol._previousVisibleColumn; |
|
1246 if (sib) { |
|
1247 sib.setAttribute("insertafter", "true"); |
|
1248 column = tree.columns.getColumnFor(sib); |
|
1249 tree.treeBoxObject.invalidateColumn(column); |
|
1250 } |
|
1251 } |
|
1252 column = tree.columns.getColumnFor(targetCol); |
|
1253 tree.treeBoxObject.invalidateColumn(column); |
|
1254 col.mTargetCol = targetCol; |
|
1255 col.mTargetDir = pos.value; |
|
1256 } |
|
1257 ]]></body> |
|
1258 </method> |
|
1259 |
|
1260 <method name="_onDragMouseUp"> |
|
1261 <parameter name="aEvent"/> |
|
1262 <body><![CDATA[ |
|
1263 var col = document.treecolDragging; |
|
1264 if (!col) return; |
|
1265 |
|
1266 if (!col.mDragGesturing) { |
|
1267 if (col.mTargetCol) { |
|
1268 // remove insertbefore/after attributes |
|
1269 var before = col.mTargetCol.hasAttribute("insertbefore"); |
|
1270 col.mTargetCol.removeAttribute(before ? "insertbefore" : "insertafter"); |
|
1271 |
|
1272 var sib = col.mTargetCol._previousVisibleColumn; |
|
1273 if (before && sib) { |
|
1274 sib.removeAttribute("insertafter"); |
|
1275 } |
|
1276 |
|
1277 // Move the column only if it will result in a different column |
|
1278 // ordering |
|
1279 var move = true; |
|
1280 |
|
1281 // If this is a before move and the previous visible column is |
|
1282 // the same as the column we're moving, don't move |
|
1283 if (before && col == sib) { |
|
1284 move = false; |
|
1285 } |
|
1286 else if (!before && col == col.mTargetCol) { |
|
1287 // If this is an after move and the column we're moving is |
|
1288 // the same as the target column, don't move. |
|
1289 move = false; |
|
1290 } |
|
1291 |
|
1292 if (move) { |
|
1293 col.parentNode.parentNode._reorderColumn(col, col.mTargetCol, before); |
|
1294 } |
|
1295 |
|
1296 // repaint to remove lines |
|
1297 col.parentNode.parentNode.treeBoxObject.invalidate(); |
|
1298 |
|
1299 col.mTargetCol = null; |
|
1300 } |
|
1301 } else |
|
1302 col.mDragGesturing = false; |
|
1303 |
|
1304 document.treecolDragging = null; |
|
1305 col.removeAttribute("dragging"); |
|
1306 |
|
1307 window.removeEventListener("mousemove", col._onDragMouseMove, true); |
|
1308 window.removeEventListener("mouseup", col._onDragMouseUp, true); |
|
1309 // we have to wait for the click event to fire before removing |
|
1310 // cancelling handler |
|
1311 var clickHandler = function(handler) { |
|
1312 window.removeEventListener("click", handler, true); |
|
1313 }; |
|
1314 window.setTimeout(clickHandler, 0, col._onDragMouseClick); |
|
1315 ]]></body> |
|
1316 </method> |
|
1317 |
|
1318 <method name="_onDragMouseClick"> |
|
1319 <parameter name="aEvent"/> |
|
1320 <body><![CDATA[ |
|
1321 // prevent click event from firing after column drag and drop |
|
1322 aEvent.stopPropagation(); |
|
1323 aEvent.preventDefault(); |
|
1324 ]]></body> |
|
1325 </method> |
|
1326 </implementation> |
|
1327 |
|
1328 <handlers> |
|
1329 <handler event="mousedown" button="0"><![CDATA[ |
|
1330 if (this.parentNode.parentNode.enableColumnDrag) { |
|
1331 var xulns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; |
|
1332 var cols = this.parentNode.getElementsByTagNameNS(xulns, "treecol"); |
|
1333 |
|
1334 // only start column drag operation if there are at least 2 visible columns |
|
1335 var visible = 0; |
|
1336 for (var i = 0; i < cols.length; ++i) |
|
1337 if (cols[i].boxObject.width > 0) ++visible; |
|
1338 |
|
1339 if (visible > 1) { |
|
1340 window.addEventListener("mousemove", this._onDragMouseMove, true); |
|
1341 window.addEventListener("mouseup", this._onDragMouseUp, true); |
|
1342 document.treecolDragging = this; |
|
1343 this.mDragGesturing = true; |
|
1344 this.mStartDragX = event.clientX; |
|
1345 this.mStartDragY = event.clientY; |
|
1346 } |
|
1347 } |
|
1348 ]]></handler> |
|
1349 <handler event="click" button="0" phase="target"> |
|
1350 <![CDATA[ |
|
1351 if (event.target != event.originalTarget) |
|
1352 return; |
|
1353 |
|
1354 #ifdef XP_WIN |
|
1355 // On Windows multiple clicking on tree columns only cycles one time |
|
1356 // every 2 clicks. |
|
1357 if (event.detail % 2 == 0) |
|
1358 return; |
|
1359 #endif |
|
1360 |
|
1361 var tree = this.parentNode.parentNode; |
|
1362 var column = tree.columns.getColumnFor(this); |
|
1363 tree.view.cycleHeader(column); |
|
1364 ]]> |
|
1365 </handler> |
|
1366 </handlers> |
|
1367 </binding> |
|
1368 |
|
1369 <binding id="treecol" extends="chrome://global/content/bindings/tree.xml#treecol-base"> |
|
1370 <content> |
|
1371 <xul:label class="treecol-text" xbl:inherits="crop,value=label" flex="1" crop="right"/> |
|
1372 <xul:image class="treecol-sortdirection" xbl:inherits="sortDirection,hidden=hideheader"/> |
|
1373 </content> |
|
1374 </binding> |
|
1375 |
|
1376 <binding id="treecol-image" extends="chrome://global/content/bindings/tree.xml#treecol-base"> |
|
1377 <content> |
|
1378 <xul:image class="treecol-icon" xbl:inherits="src"/> |
|
1379 </content> |
|
1380 </binding> |
|
1381 |
|
1382 <binding id="columnpicker" display="xul:button" role="xul:button" |
|
1383 extends="chrome://global/content/bindings/tree.xml#tree-base"> |
|
1384 <content> |
|
1385 <xul:image class="tree-columnpicker-icon"/> |
|
1386 <xul:menupopup anonid="popup"> |
|
1387 <xul:menuseparator anonid="menuseparator"/> |
|
1388 <xul:menuitem anonid="menuitem" label="&restoreColumnOrder.label;"/> |
|
1389 </xul:menupopup> |
|
1390 </content> |
|
1391 |
|
1392 <implementation> |
|
1393 <method name="buildPopup"> |
|
1394 <parameter name="aPopup"/> |
|
1395 <body> |
|
1396 <![CDATA[ |
|
1397 // We no longer cache the picker content, remove the old content. |
|
1398 while (aPopup.childNodes.length > 2) |
|
1399 aPopup.removeChild(aPopup.firstChild); |
|
1400 |
|
1401 var refChild = aPopup.firstChild; |
|
1402 |
|
1403 var tree = this.parentNode.parentNode; |
|
1404 for (var currCol = tree.columns.getFirstColumn(); currCol; |
|
1405 currCol = currCol.getNext()) { |
|
1406 // Construct an entry for each column in the row, unless |
|
1407 // it is not being shown. |
|
1408 var currElement = currCol.element; |
|
1409 if (!currElement.hasAttribute("ignoreincolumnpicker")) { |
|
1410 var popupChild = document.createElement("menuitem"); |
|
1411 popupChild.setAttribute("type", "checkbox"); |
|
1412 var columnName = currElement.getAttribute("display") || |
|
1413 currElement.getAttribute("label"); |
|
1414 popupChild.setAttribute("label", columnName); |
|
1415 popupChild.setAttribute("colindex", currCol.index); |
|
1416 if (currElement.getAttribute("hidden") != "true") |
|
1417 popupChild.setAttribute("checked", "true"); |
|
1418 if (currCol.primary) |
|
1419 popupChild.setAttribute("disabled", "true"); |
|
1420 aPopup.insertBefore(popupChild, refChild); |
|
1421 } |
|
1422 } |
|
1423 |
|
1424 var hidden = !tree.enableColumnDrag; |
|
1425 const anonids = ["menuseparator", "menuitem"]; |
|
1426 for (var i = 0; i < anonids.length; i++) { |
|
1427 var element = document.getAnonymousElementByAttribute(this, "anonid", anonids[i]); |
|
1428 element.hidden = hidden; |
|
1429 } |
|
1430 ]]> |
|
1431 </body> |
|
1432 </method> |
|
1433 </implementation> |
|
1434 |
|
1435 <handlers> |
|
1436 <handler event="command"> |
|
1437 <![CDATA[ |
|
1438 if (event.originalTarget == this) { |
|
1439 var popup = document.getAnonymousElementByAttribute(this, "anonid", "popup"); |
|
1440 this.buildPopup(popup); |
|
1441 popup.showPopup(this, -1, -1, "popup", "bottomright", "topright"); |
|
1442 } |
|
1443 else { |
|
1444 var tree = this.parentNode.parentNode; |
|
1445 tree.stopEditing(true); |
|
1446 var menuitem = document.getAnonymousElementByAttribute(this, "anonid", "menuitem"); |
|
1447 if (event.originalTarget == menuitem) { |
|
1448 tree.columns.restoreNaturalOrder(); |
|
1449 tree._ensureColumnOrder(); |
|
1450 } |
|
1451 else { |
|
1452 var colindex = event.originalTarget.getAttribute("colindex"); |
|
1453 var column = tree.columns[colindex]; |
|
1454 if (column) { |
|
1455 var element = column.element; |
|
1456 if (element.getAttribute("hidden") == "true") |
|
1457 element.setAttribute("hidden", "false"); |
|
1458 else |
|
1459 element.setAttribute("hidden", "true"); |
|
1460 } |
|
1461 } |
|
1462 } |
|
1463 ]]> |
|
1464 </handler> |
|
1465 </handlers> |
|
1466 </binding> |
|
1467 </bindings> |
|
1468 |