toolkit/content/widgets/tree.xml

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

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

mercurial