toolkit/content/widgets/tree.xml

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/content/widgets/tree.xml	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1468 @@
     1.4 +<?xml version="1.0"?>
     1.5 +<!-- This Source Code Form is subject to the terms of the Mozilla Public
     1.6 +   - License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 +   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
     1.8 +
     1.9 +
    1.10 +<!DOCTYPE bindings [
    1.11 +<!ENTITY % treeDTD SYSTEM "chrome://global/locale/tree.dtd">
    1.12 +%treeDTD;
    1.13 +]>
    1.14 +
    1.15 +<bindings id="treeBindings"
    1.16 +   xmlns="http://www.mozilla.org/xbl"
    1.17 +   xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
    1.18 +   xmlns:xbl="http://www.mozilla.org/xbl">
    1.19 +  
    1.20 +  <binding id="tree-base" extends="chrome://global/content/bindings/general.xml#basecontrol">
    1.21 +    <resources>
    1.22 +      <stylesheet src="chrome://global/skin/tree.css"/>
    1.23 +    </resources>
    1.24 +    <implementation>
    1.25 +      <method name="_isAccelPressed">
    1.26 +        <parameter name="aEvent"/>
    1.27 +        <body><![CDATA[
    1.28 +# Workaround until bug 302174 is fixed
    1.29 +#ifdef XP_MACOSX
    1.30 +          return aEvent.metaKey;
    1.31 +#else
    1.32 +          return aEvent.ctrlKey;
    1.33 +#endif
    1.34 +        ]]></body>
    1.35 +      </method>
    1.36 +    </implementation>
    1.37 +  </binding>
    1.38 +
    1.39 +  <binding id="tree" extends="chrome://global/content/bindings/tree.xml#tree-base" role="xul:tree">
    1.40 +    <content hidevscroll="true" hidehscroll="true" clickthrough="never">
    1.41 +      <children includes="treecols"/>
    1.42 +      <xul:stack class="tree-stack" flex="1">
    1.43 +        <xul:treerows class="tree-rows" flex="1" xbl:inherits="hidevscroll">
    1.44 +          <children/>
    1.45 +        </xul:treerows>
    1.46 +        <xul:textbox anonid="input" class="tree-input" left="0" top="0" hidden="true"/>
    1.47 +      </xul:stack>
    1.48 +      <xul:hbox xbl:inherits="collapsed=hidehscroll">
    1.49 +        <xul:scrollbar orient="horizontal" flex="1" increment="16" style="position:relative; z-index:2147483647;"/>
    1.50 +        <xul:scrollcorner xbl:inherits="collapsed=hidevscroll"/>
    1.51 +      </xul:hbox>
    1.52 +    </content>
    1.53 +    
    1.54 +    <implementation implements="nsIDOMXULTreeElement, nsIDOMXULMultiSelectControlElement">
    1.55 +
    1.56 +      <!-- ///////////////// nsIDOMXULTreeElement ///////////////// -->
    1.57 +
    1.58 +      <property name="columns"
    1.59 +                onget="return this.treeBoxObject.columns;"/>
    1.60 +
    1.61 +      <property name="view"
    1.62 +                onget="return this.treeBoxObject.view;"
    1.63 +                onset="return this.treeBoxObject.view = val;"/>
    1.64 +
    1.65 +      <property name="body"
    1.66 +                onget="return this.treeBoxObject.treeBody;"/>
    1.67 +
    1.68 +      <property name="editable"
    1.69 +                onget="return this.getAttribute('editable') == 'true';"
    1.70 +                onset="if (val) this.setAttribute('editable', 'true');
    1.71 +                       else this.removeAttribute('editable'); return val;"/>
    1.72 +
    1.73 +      <!-- ///////////////// nsIDOMXULSelectControlElement ///////////////// -->
    1.74 +
    1.75 +      <!-- ///////////////// nsIDOMXULMultiSelectControlElement ///////////////// -->
    1.76 +
    1.77 +      <property name="selType"
    1.78 +                onget="return this.getAttribute('seltype')"
    1.79 +                onset="this.setAttribute('seltype', val); return val;"/>
    1.80 +
    1.81 +      <property name="currentIndex"
    1.82 +                onget="return this.view ? this.view.selection.currentIndex: - 1;"
    1.83 +                onset="if (this.view) return this.view.selection.currentIndex = val; return val;"/>
    1.84 +
    1.85 +      <property name="treeBoxObject"
    1.86 +                onget="return this.boxObject.QueryInterface(Components.interfaces.nsITreeBoxObject);"
    1.87 +                readonly="true"/>
    1.88 +# contentView is obsolete (see bug 202391)
    1.89 +      <property name="contentView"
    1.90 +                onget="return this.view; /*.QueryInterface(Components.interfaces.nsITreeContentView)*/"
    1.91 +                readonly="true"/>
    1.92 +# builderView is obsolete (see bug 202393)
    1.93 +      <property name="builderView"
    1.94 +                onget="return this.view; /*.QueryInterface(Components.interfaces.nsIXULTreeBuilder)*/"
    1.95 +                readonly="true"/>
    1.96 +      <field name="pageUpOrDownMovesSelection">
    1.97 +#ifdef XP_MACOSX
    1.98 +        false
    1.99 +#else
   1.100 +        true
   1.101 +#endif
   1.102 +      </field>
   1.103 +      <property name="keepCurrentInView"
   1.104 +                onget="return (this.getAttribute('keepcurrentinview') == 'true');"
   1.105 +                onset="if (val) this.setAttribute('keepcurrentinview', 'true');
   1.106 +                       else this.removeAttribute('keepcurrentinview'); return val;"/>
   1.107 +
   1.108 +      <property name="enableColumnDrag"
   1.109 +                onget="return this.hasAttribute('enableColumnDrag');"
   1.110 +                onset="if (val) this.setAttribute('enableColumnDrag', 'true');
   1.111 +                       else this.removeAttribute('enableColumnDrag'); return val;"/>
   1.112 +
   1.113 +      <field name="_inputField">null</field>
   1.114 +
   1.115 +      <property name="inputField" readonly="true">
   1.116 +        <getter><![CDATA[
   1.117 +          if (!this._inputField)
   1.118 +            this._inputField = document.getAnonymousElementByAttribute(this, "anonid", "input");
   1.119 +          return this._inputField;
   1.120 +        ]]></getter>
   1.121 +      </property>
   1.122 +
   1.123 +      <property name="disableKeyNavigation"
   1.124 +                onget="return this.hasAttribute('disableKeyNavigation');"
   1.125 +                onset="if (val) this.setAttribute('disableKeyNavigation', 'true');
   1.126 +                       else this.removeAttribute('disableKeyNavigation'); return val;"/>
   1.127 +
   1.128 +      <field name="_editingRow">-1</field>
   1.129 +      <field name="_editingColumn">null</field>
   1.130 +
   1.131 +      <property name="editingRow" readonly="true"
   1.132 +                onget="return this._editingRow;"/>
   1.133 +      <property name="editingColumn" readonly="true"
   1.134 +                onget="return this._editingColumn;"/>
   1.135 +
   1.136 +      <property name="_selectDelay" 
   1.137 +                onset="this.setAttribute('_selectDelay', val);"
   1.138 +                onget="return this.getAttribute('_selectDelay') || 50;"/>
   1.139 +      <field name="_columnsDirty">true</field>
   1.140 +      <field name="_lastKeyTime">0</field>
   1.141 +      <field name="_incrementalString">""</field>
   1.142 +
   1.143 +      <method name="_ensureColumnOrder">
   1.144 +        <body><![CDATA[
   1.145 +          if (!this._columnsDirty)
   1.146 +            return;
   1.147 +
   1.148 +          if (this.columns) {
   1.149 +            // update the ordinal position of each column to assure that it is
   1.150 +            // an odd number and 2 positions above its next sibling
   1.151 +            var cols = [];
   1.152 +            var i;
   1.153 +            for (var col = this.columns.getFirstColumn(); col; col = col.getNext())
   1.154 +              cols.push(col.element);
   1.155 +            for (i = 0; i < cols.length; ++i)
   1.156 +              cols[i].setAttribute("ordinal", (i*2)+1);
   1.157 +
   1.158 +            // update the ordinal positions of splitters to even numbers, so that 
   1.159 +            // they are in between columns
   1.160 +            var splitters = this.getElementsByTagName("splitter");
   1.161 +            for (i = 0; i < splitters.length; ++i)
   1.162 +              splitters[i].setAttribute("ordinal", (i+1)*2);
   1.163 +          }
   1.164 +          this._columnsDirty = false;
   1.165 +        ]]></body>
   1.166 +      </method>
   1.167 +
   1.168 +      <method name="_reorderColumn">
   1.169 +        <parameter name="aColMove"/>
   1.170 +        <parameter name="aColBefore"/>
   1.171 +        <parameter name="aBefore"/>
   1.172 +        <body><![CDATA[
   1.173 +          this._ensureColumnOrder();
   1.174 +
   1.175 +          var i;
   1.176 +          var cols = [];
   1.177 +          var col = this.columns.getColumnFor(aColBefore);
   1.178 +          if (parseInt(aColBefore.ordinal) < parseInt(aColMove.ordinal)) {
   1.179 +            if (aBefore)
   1.180 +              cols.push(aColBefore);
   1.181 +            for (col = col.getNext(); col.element != aColMove;
   1.182 +                 col = col.getNext())
   1.183 +              cols.push(col.element);
   1.184 +
   1.185 +            aColMove.ordinal = cols[0].ordinal;
   1.186 +            for (i = 0; i < cols.length; ++i)
   1.187 +              cols[i].ordinal = parseInt(cols[i].ordinal) + 2;
   1.188 +          } else if (aColBefore.ordinal != aColMove.ordinal) {
   1.189 +            if (!aBefore)
   1.190 +              cols.push(aColBefore);
   1.191 +            for (col = col.getPrevious(); col.element != aColMove;
   1.192 +                 col = col.getPrevious())
   1.193 +              cols.push(col.element);
   1.194 +
   1.195 +            aColMove.ordinal = cols[0].ordinal;
   1.196 +            for (i = 0; i < cols.length; ++i)
   1.197 +              cols[i].ordinal = parseInt(cols[i].ordinal) - 2;
   1.198 +          }
   1.199 +        ]]></body>
   1.200 +      </method>
   1.201 +      
   1.202 +      <method name="_getColumnAtX">
   1.203 +        <parameter name="aX"/>
   1.204 +        <parameter name="aThresh"/>
   1.205 +        <parameter name="aPos"/>
   1.206 +        <body><![CDATA[
   1.207 +          var isRTL = document.defaultView.getComputedStyle(this, "")
   1.208 +                              .direction == "rtl";
   1.209 +
   1.210 +          if (aPos)
   1.211 +            aPos.value = isRTL ? "after" : "before";
   1.212 +
   1.213 +          var columns = [];
   1.214 +          var col = this.columns.getFirstColumn();
   1.215 +          while (col) {
   1.216 +            columns.push(col);
   1.217 +            col = col.getNext();
   1.218 +          }
   1.219 +          if (isRTL)
   1.220 +            columns.reverse();
   1.221 +          var currentX = this.boxObject.x;
   1.222 +          var adjustedX = aX + this.treeBoxObject.horizontalPosition;
   1.223 +          for (var i = 0; i < columns.length; ++i) {
   1.224 +            col = columns[i];
   1.225 +            var cw = col.element.boxObject.width;
   1.226 +            if (cw > 0) {
   1.227 +              currentX += cw;
   1.228 +              if (currentX - (cw * aThresh) > adjustedX)
   1.229 +                return col.element;
   1.230 +            }
   1.231 +          }
   1.232 +
   1.233 +          if (aPos)
   1.234 +            aPos.value = isRTL ? "before" : "after";
   1.235 +          return columns.pop().element;
   1.236 +        ]]></body>
   1.237 +      </method>
   1.238 +
   1.239 +      <method name="changeOpenState">
   1.240 +        <parameter name="row"/>
   1.241 +        <!-- Optional parameter openState == true or false to set. 
   1.242 +             No openState param == toggle -->
   1.243 +        <parameter name="openState"/>
   1.244 +        <body><![CDATA[
   1.245 +          if (row < 0 || !this.view.isContainer(row)) {
   1.246 +            return false;
   1.247 +          }
   1.248 +          if (this.view.isContainerOpen(row) != openState) {
   1.249 +            this.view.toggleOpenState(row);
   1.250 +            if (row == this.currentIndex) {
   1.251 +              // Only fire event when current row is expanded or collapsed
   1.252 +              // because that's all the assistive technology really cares about.
   1.253 +              var event = document.createEvent('Events');
   1.254 +              event.initEvent('OpenStateChange', true, true);
   1.255 +              this.dispatchEvent(event);
   1.256 +            }
   1.257 +            return true;
   1.258 +          }
   1.259 +          return false;
   1.260 +        ]]></body>
   1.261 +      </method>
   1.262 +
   1.263 +      <property name="_cellSelType">
   1.264 +        <getter>
   1.265 +          <![CDATA[
   1.266 +            var seltype = this.selType;
   1.267 +            if (seltype == "cell" || seltype == "text")
   1.268 +              return seltype;
   1.269 +            return null;
   1.270 +          ]]>
   1.271 +        </getter>
   1.272 +      </property>
   1.273 +
   1.274 +      <method name="_getNextColumn">
   1.275 +        <parameter name="row"/>
   1.276 +        <parameter name="left"/>
   1.277 +        <body><![CDATA[
   1.278 +          var col = this.view.selection.currentColumn;
   1.279 +          if (col) {
   1.280 +            col = left ? col.getPrevious() : col.getNext();
   1.281 +          }
   1.282 +          else {
   1.283 +            col = this.columns.getKeyColumn();
   1.284 +          }
   1.285 +          while (col && (col.width == 0 || !col.selectable ||
   1.286 +                 !this.view.isSelectable(row, col)))
   1.287 +            col = left ? col.getPrevious() : col.getNext();
   1.288 +          return col;
   1.289 +        ]]></body>
   1.290 +      </method>
   1.291 +
   1.292 +      <method name="_keyNavigate">
   1.293 +        <parameter name="event"/>
   1.294 +        <body><![CDATA[
   1.295 +          var key = String.fromCharCode(event.charCode).toLowerCase();
   1.296 +          if (event.timeStamp - this._lastKeyTime > 1000)
   1.297 +            this._incrementalString = key;
   1.298 +          else
   1.299 +            this._incrementalString += key;
   1.300 +          this._lastKeyTime = event.timeStamp;
   1.301 +
   1.302 +          var length = this._incrementalString.length;
   1.303 +          var incrementalString = this._incrementalString;
   1.304 +          var charIndex = 1;
   1.305 +          while (charIndex < length && incrementalString[charIndex] == incrementalString[charIndex - 1])
   1.306 +            charIndex++;
   1.307 +          // If all letters in incremental string are same, just try to match the first one
   1.308 +          if (charIndex == length) {
   1.309 +            length = 1;
   1.310 +            incrementalString = incrementalString.substring(0, length);
   1.311 +          }
   1.312 +
   1.313 +          var keyCol = this.columns.getKeyColumn();
   1.314 +          var rowCount = this.view.rowCount;
   1.315 +          var start = 1;
   1.316 +
   1.317 +          var c = this.currentIndex;
   1.318 +          if (length > 1) {
   1.319 +            start = 0;
   1.320 +            if (c < 0)
   1.321 +              c = 0;
   1.322 +          }
   1.323 +
   1.324 +          for (var i = 0; i < rowCount; i++) {
   1.325 +            var l = (i + start + c) % rowCount;
   1.326 +            var cellText = this.view.getCellText(l, keyCol);
   1.327 +            cellText = cellText.substring(0, length).toLowerCase();
   1.328 +            if (cellText == incrementalString)
   1.329 +              return l;
   1.330 +          }
   1.331 +          return -1;
   1.332 +        ]]></body>
   1.333 +      </method>
   1.334 +
   1.335 +      <method name="startEditing">
   1.336 +        <parameter name="row"/>
   1.337 +        <parameter name="column"/>
   1.338 +        <body>
   1.339 +          <![CDATA[
   1.340 +            if (!this.editable)
   1.341 +              return false;
   1.342 +            if (row < 0 || row >= this.view.rowCount || !column)
   1.343 +              return false;
   1.344 +            if (column.type != Components.interfaces.nsITreeColumn.TYPE_TEXT ||
   1.345 +                column.cycler || !this.view.isEditable(row, column))
   1.346 +              return false;
   1.347 +
   1.348 +            // Beyond this point, we are going to edit the cell.
   1.349 +            if (this._editingColumn)
   1.350 +              this.stopEditing();
   1.351 +
   1.352 +            var input = this.inputField;
   1.353 +
   1.354 +            var box = this.treeBoxObject;
   1.355 +            box.ensureCellIsVisible(row, column);
   1.356 +
   1.357 +            // Get the coordinates of the text inside the cell.
   1.358 +            var textx = {}, texty = {}, textwidth = {}, textheight = {};
   1.359 +            var coords = box.getCoordsForCellItem(row, column, "text",
   1.360 +                                                  textx, texty, textwidth, textheight);
   1.361 +
   1.362 +            // Get the coordinates of the cell itself.
   1.363 +            var cellx = {}, cellwidth = {};
   1.364 +            coords = box.getCoordsForCellItem(row, column, "cell",
   1.365 +                                              cellx, {}, cellwidth, {});
   1.366 +
   1.367 +            // Calculate the top offset of the textbox.
   1.368 +            var style = window.getComputedStyle(input, "");
   1.369 +            var topadj = parseInt(style.borderTopWidth) + parseInt(style.paddingTop);
   1.370 +            input.top = texty.value - topadj;
   1.371 +
   1.372 +            // The leftside of the textbox is aligned to the left side of the text
   1.373 +            // in LTR mode, and left side of the cell in RTL mode.
   1.374 +            var left, widthdiff;
   1.375 +            if (style.direction == "rtl") {
   1.376 +              left = cellx.value;
   1.377 +              widthdiff = cellx.value + cellwidth.value - textx.value - textwidth.value;
   1.378 +            } else {
   1.379 +              left = textx.value;
   1.380 +              widthdiff = textx.value - cellx.value;
   1.381 +            }
   1.382 +
   1.383 +            input.left = left;
   1.384 +            input.height = textheight.value + topadj +
   1.385 +                           parseInt(style.borderBottomWidth) +
   1.386 +                           parseInt(style.paddingBottom);
   1.387 +            input.width = cellwidth.value - widthdiff;
   1.388 +            input.hidden = false;
   1.389 +
   1.390 +            input.value = this.view.getCellText(row, column);
   1.391 +            var selectText = function selectText() {
   1.392 +              input.select();
   1.393 +              input.inputField.focus();
   1.394 +            }
   1.395 +            setTimeout(selectText, 0);
   1.396 +
   1.397 +            this._editingRow = row;
   1.398 +            this._editingColumn = column;
   1.399 +
   1.400 +            this.setAttribute("editing", "true");
   1.401 +            return true;
   1.402 +          ]]>
   1.403 +        </body>
   1.404 +      </method>
   1.405 +
   1.406 +      <method name="stopEditing">
   1.407 +        <parameter name="accept"/>
   1.408 +        <body>
   1.409 +          <![CDATA[
   1.410 +            if (!this._editingColumn)
   1.411 +              return;
   1.412 +
   1.413 +            var input = this.inputField;
   1.414 +            var editingRow = this._editingRow;
   1.415 +            var editingColumn = this._editingColumn;
   1.416 +            this._editingRow = -1;
   1.417 +            this._editingColumn = null;
   1.418 +            if (accept) {
   1.419 +              var value = input.value;
   1.420 +              this.view.setCellText(editingRow, editingColumn, value);
   1.421 +            }
   1.422 +
   1.423 +            input.hidden = true;
   1.424 +            input.value = "";
   1.425 +            this.removeAttribute("editing");
   1.426 +          ]]>
   1.427 +        </body>
   1.428 +      </method>
   1.429 +
   1.430 +      <method name="_moveByOffset">
   1.431 +        <parameter name="offset"/>
   1.432 +        <parameter name="edge"/>
   1.433 +        <parameter name="event"/>
   1.434 +        <body>
   1.435 +          <![CDATA[
   1.436 +            if (this._editingColumn || this.view.rowCount == 0)
   1.437 +              return;
   1.438 +
   1.439 +            if (this._isAccelPressed(event) && this.view.selection.single) {
   1.440 +              this.treeBoxObject.scrollByLines(offset);
   1.441 +              return;
   1.442 +            }
   1.443 +   
   1.444 +            var c = this.currentIndex + offset;
   1.445 +            if (offset > 0 ? c > edge : c < edge) {
   1.446 +              if (this.view.selection.isSelected(edge) && this.view.selection.count <= 1)
   1.447 +                return;
   1.448 +              c = edge;
   1.449 +            }
   1.450 +   
   1.451 +            var cellSelType = this._cellSelType;
   1.452 +            if (cellSelType) {
   1.453 +              var column = this.view.selection.currentColumn;
   1.454 +              if (!column)
   1.455 +                return;
   1.456 +   
   1.457 +              while ((offset > 0 ? c <= edge : c >= edge) && !this.view.isSelectable(c, column))
   1.458 +                c += offset;
   1.459 +              if (offset > 0 ? c > edge : c < edge)
   1.460 +                return;
   1.461 +            }
   1.462 +   
   1.463 +            if (!this._isAccelPressed(event))
   1.464 +              this.view.selection.timedSelect(c, this._selectDelay);
   1.465 +            else // Ctrl+Up/Down moves the anchor without selecting
   1.466 +              this.currentIndex = c;
   1.467 +            this.treeBoxObject.ensureRowIsVisible(c);
   1.468 +          ]]>
   1.469 +        </body>
   1.470 +      </method>
   1.471 +
   1.472 +      <method name="_moveByOffsetShift">
   1.473 +        <parameter name="offset"/>
   1.474 +        <parameter name="edge"/>
   1.475 +        <parameter name="event"/>
   1.476 +        <body>
   1.477 +          <![CDATA[
   1.478 +            if (this._editingColumn || this.view.rowCount == 0)
   1.479 +              return;
   1.480 +
   1.481 +            if (this.view.selection.single) {
   1.482 +              this.treeBoxObject.scrollByLines(offset);
   1.483 +              return;
   1.484 +            }
   1.485 +
   1.486 +            if (this.view.rowCount == 1 && !this.view.selection.isSelected(0)) {
   1.487 +              this.view.selection.timedSelect(0, this._selectDelay);
   1.488 +              return;
   1.489 +            }
   1.490 +      
   1.491 +            var c = this.currentIndex;
   1.492 +            if (c == -1)
   1.493 +                c = 0;
   1.494 +
   1.495 +            if (c == edge) {
   1.496 +              if (this.view.selection.isSelected(c))
   1.497 +                return;
   1.498 +            }
   1.499 +      
   1.500 +            // Extend the selection from the existing pivot, if any
   1.501 +            this.view.selection.rangedSelect(-1, c + offset,
   1.502 +                                             this._isAccelPressed(event));
   1.503 +            this.treeBoxObject.ensureRowIsVisible(c + offset);
   1.504 +
   1.505 +          ]]>
   1.506 +        </body>
   1.507 +      </method>
   1.508 +
   1.509 +      <method name="_moveByPage">
   1.510 +        <parameter name="offset"/>
   1.511 +        <parameter name="edge"/>
   1.512 +        <parameter name="event"/>
   1.513 +        <body>
   1.514 +          <![CDATA[
   1.515 +            if (this._editingColumn || this.view.rowCount == 0)
   1.516 +              return;
   1.517 +
   1.518 +            if (this.pageUpOrDownMovesSelection == this._isAccelPressed(event)) {
   1.519 +               this.treeBoxObject.scrollByPages(offset);
   1.520 +               return;
   1.521 +            }
   1.522 +
   1.523 +            if (this.view.rowCount == 1 && !this.view.selection.isSelected(0)) {
   1.524 +              this.view.selection.timedSelect(0, this._selectDelay);
   1.525 +              return;
   1.526 +            }
   1.527 +
   1.528 +            var c = this.currentIndex;
   1.529 +            if (c == -1)
   1.530 +              return;
   1.531 +
   1.532 +            if (c == edge && this.view.selection.isSelected(c)) {
   1.533 +              this.treeBoxObject.ensureRowIsVisible(c);
   1.534 +              return;
   1.535 +            }
   1.536 +            var i = this.treeBoxObject.getFirstVisibleRow();
   1.537 +            var p = this.treeBoxObject.getPageLength();
   1.538 +
   1.539 +            if (offset > 0) {
   1.540 +              i += p - 1;
   1.541 +              if (c >= i) {
   1.542 +                 i = c + p;
   1.543 +                 this.treeBoxObject.ensureRowIsVisible(i > edge ? edge : i);
   1.544 +              }
   1.545 +              i = i > edge ? edge : i;
   1.546 +
   1.547 +            } else {
   1.548 +              if (c <= i) {
   1.549 +                 i = c <= p ? 0 : c - p;
   1.550 +                 this.treeBoxObject.ensureRowIsVisible(i);
   1.551 +              }
   1.552 +            }
   1.553 +            this.view.selection.timedSelect(i, this._selectDelay);
   1.554 +          ]]>
   1.555 +        </body>
   1.556 +      </method>
   1.557 +
   1.558 +      <method name="_moveByPageShift">
   1.559 +        <parameter name="offset"/>
   1.560 +        <parameter name="edge"/>
   1.561 +        <parameter name="event"/>
   1.562 +        <body>
   1.563 +          <![CDATA[
   1.564 +            if (this._editingColumn || this.view.rowCount == 0)
   1.565 +              return;
   1.566 +
   1.567 +            if (this.view.rowCount == 1 && !this.view.selection.isSelected(0) &&
   1.568 +                !(this.pageUpOrDownMovesSelection == this._isAccelPressed(event))) {
   1.569 +              this.view.selection.timedSelect(0, this._selectDelay);
   1.570 +              return;
   1.571 +            }
   1.572 +
   1.573 +            if (this.view.selection.single)
   1.574 +              return;
   1.575 +
   1.576 +            var c = this.currentIndex;
   1.577 +            if (c == -1)
   1.578 +              return;
   1.579 +            if (c == edge && this.view.selection.isSelected(c)) {
   1.580 +              this.treeBoxObject.ensureRowIsVisible(edge);
   1.581 +              return;
   1.582 +            }
   1.583 +            var i = this.treeBoxObject.getFirstVisibleRow();
   1.584 +            var p = this.treeBoxObject.getPageLength();
   1.585 +
   1.586 +            if (offset > 0) {
   1.587 +              i += p - 1;
   1.588 +              if (c >= i) {
   1.589 +                 i = c + p;
   1.590 +                 this.treeBoxObject.ensureRowIsVisible(i > edge ? edge : i);
   1.591 +              }
   1.592 +              // Extend the selection from the existing pivot, if any
   1.593 +              this.view.selection.rangedSelect(-1, i > edge ? edge : i, this._isAccelPressed(event));
   1.594 +
   1.595 +            } else {
   1.596 +
   1.597 +              if (c <= i) {
   1.598 +                 i = c <= p ? 0 : c - p;
   1.599 +                 this.treeBoxObject.ensureRowIsVisible(i);
   1.600 +              }
   1.601 +              // Extend the selection from the existing pivot, if any
   1.602 +              this.view.selection.rangedSelect(-1, i, this._isAccelPressed(event));
   1.603 +            }
   1.604 +
   1.605 +          ]]>
   1.606 +        </body>
   1.607 +      </method>
   1.608 +
   1.609 +      <method name="_moveToEdge">
   1.610 +        <parameter name="edge"/>
   1.611 +        <parameter name="event"/>
   1.612 +        <body>
   1.613 +          <![CDATA[
   1.614 +            if (this._editingColumn || this.view.rowCount == 0)
   1.615 +              return;
   1.616 +
   1.617 +            if (this.view.selection.isSelected(edge) && this.view.selection.count == 1) {
   1.618 +              this.currentIndex = edge;
   1.619 +              return;
   1.620 +            }
   1.621 +
   1.622 +            // Normal behaviour is to select the first/last row
   1.623 +            if (!this._isAccelPressed(event))
   1.624 +              this.view.selection.timedSelect(edge, this._selectDelay);
   1.625 +
   1.626 +            // In a multiselect tree Ctrl+Home/End moves the anchor
   1.627 +            else if (!this.view.selection.single)
   1.628 +              this.currentIndex = edge;
   1.629 +
   1.630 +            this.treeBoxObject.ensureRowIsVisible(edge);
   1.631 +          ]]>
   1.632 +        </body>
   1.633 +      </method>
   1.634 +
   1.635 +      <method name="_moveToEdgeShift">
   1.636 +        <parameter name="edge"/>
   1.637 +        <parameter name="event"/>
   1.638 +        <body>
   1.639 +          <![CDATA[
   1.640 +            if (this._editingColumn || this.view.rowCount == 0)
   1.641 +              return;
   1.642 +
   1.643 +            if (this.view.rowCount == 1 && !this.view.selection.isSelected(0)) {
   1.644 +              this.view.selection.timedSelect(0, this._selectDelay);
   1.645 +              return;
   1.646 +            }
   1.647 +
   1.648 +            if (this.view.selection.single ||
   1.649 +                (this.view.selection.isSelected(edge)) && this.view.selection.isSelected(this.currentIndex))
   1.650 +              return;
   1.651 +
   1.652 +            // Extend the selection from the existing pivot, if any.
   1.653 +            // -1 doesn't work here, so using currentIndex instead
   1.654 +            this.view.selection.rangedSelect(this.currentIndex, edge, this._isAccelPressed(event));
   1.655 +
   1.656 +            this.treeBoxObject.ensureRowIsVisible(edge);
   1.657 +          ]]>
   1.658 +        </body>
   1.659 +      </method>
   1.660 +      <method name="_handleEnter">
   1.661 +        <parameter name="event"/>
   1.662 +        <body><![CDATA[
   1.663 +          if (this._editingColumn) {
   1.664 +            this.stopEditing(true);
   1.665 +            this.focus();
   1.666 +            return true;
   1.667 +          }
   1.668 +
   1.669 +#ifdef XP_MACOSX
   1.670 +          // See if we can edit the cell.
   1.671 +          var row = this.currentIndex;
   1.672 +          if (this._cellSelType) {
   1.673 +            var column = this.view.selection.currentColumn;
   1.674 +            var startedEditing = this.startEditing(row, column);
   1.675 +            if (startedEditing)
   1.676 +              return true;
   1.677 +          }
   1.678 +#endif
   1.679 +          return this.changeOpenState(this.currentIndex);
   1.680 +        ]]></body>
   1.681 +      </method>
   1.682 +    </implementation>
   1.683 +    
   1.684 +    <handlers>
   1.685 +      <handler event="MozMousePixelScroll" preventdefault="true"/>
   1.686 +      <handler event="DOMMouseScroll" preventdefault="true">
   1.687 +        <![CDATA[
   1.688 +          if (this._editingColumn)
   1.689 +            return;
   1.690 +          if (event.axis == event.HORIZONTAL_AXIS)
   1.691 +            return;
   1.692 +
   1.693 +          var rows = event.detail;
   1.694 +          if (rows == UIEvent.SCROLL_PAGE_UP)
   1.695 +            this.treeBoxObject.scrollByPages(-1);
   1.696 +          else if (rows == UIEvent.SCROLL_PAGE_DOWN)
   1.697 +            this.treeBoxObject.scrollByPages(1);
   1.698 +          else
   1.699 +            this.treeBoxObject.scrollByLines(rows);
   1.700 +        ]]>
   1.701 +      </handler>
   1.702 +      <handler event="MozSwipeGesture" preventdefault="true">
   1.703 +        <![CDATA[
   1.704 +          // Figure out which row to show
   1.705 +          let targetRow = 0;
   1.706 +
   1.707 +          // Only handle swipe gestures up and down
   1.708 +          switch (event.direction) {
   1.709 +            case event.DIRECTION_DOWN:
   1.710 +              targetRow = this.view.rowCount - 1;
   1.711 +              // Fall through for actual action
   1.712 +            case event.DIRECTION_UP:
   1.713 +              this.treeBoxObject.ensureRowIsVisible(targetRow);
   1.714 +              break;
   1.715 +          }
   1.716 +        ]]>
   1.717 +      </handler>
   1.718 +      <handler event="select" phase="target"
   1.719 +               action="if (event.originalTarget == this) this.stopEditing(true);"/>
   1.720 +      <handler event="focus">
   1.721 +        <![CDATA[
   1.722 +          this.treeBoxObject.focused = true;
   1.723 +          if (this.currentIndex == -1 && this.view.rowCount > 0) {
   1.724 +            this.currentIndex = this.treeBoxObject.getFirstVisibleRow();
   1.725 +          }
   1.726 +          if (this._cellSelType && !this.view.selection.currentColumn) {
   1.727 +            var col = this._getNextColumn(this.currentIndex, false);
   1.728 +            this.view.selection.currentColumn = col;
   1.729 +          }
   1.730 +        ]]>
   1.731 +      </handler>
   1.732 +      <handler event="blur" action="this.treeBoxObject.focused = false;"/>
   1.733 +      <handler event="blur" phase="capturing"
   1.734 +               action="if (event.originalTarget == this.inputField.inputField) this.stopEditing(true);"/>
   1.735 +      <handler event="keydown" keycode="VK_RETURN">
   1.736 +        if (this._handleEnter(event)) {
   1.737 +          event.stopPropagation();
   1.738 +          event.preventDefault();
   1.739 +        }
   1.740 +      </handler>
   1.741 +#ifndef XP_MACOSX
   1.742 +      <!-- Use F2 key to enter text editing. -->
   1.743 +      <handler event="keydown" keycode="VK_F2">
   1.744 +        <![CDATA[
   1.745 +          if (!this._cellSelType)
   1.746 +            return;
   1.747 +          var row = this.currentIndex;
   1.748 +          var column = this.view.selection.currentColumn;
   1.749 +          if (this.startEditing(row, column))
   1.750 +            event.preventDefault();
   1.751 +        ]]>
   1.752 +      </handler>
   1.753 +#endif // XP_MACOSX
   1.754 +
   1.755 +      <handler event="keydown" keycode="VK_ESCAPE">
   1.756 +        <![CDATA[
   1.757 +          if (this._editingColumn) {
   1.758 +            this.stopEditing(false);
   1.759 +            this.focus();
   1.760 +            event.stopPropagation();
   1.761 +            event.preventDefault();
   1.762 +          }
   1.763 +        ]]>
   1.764 +      </handler>
   1.765 +      <handler event="keydown" keycode="VK_LEFT">
   1.766 +        <![CDATA[
   1.767 +         if (this._editingColumn)
   1.768 +           return;
   1.769 +
   1.770 +         var row = this.currentIndex;
   1.771 +         if (row < 0)
   1.772 +           return;
   1.773 +
   1.774 +         var cellSelType = this._cellSelType;
   1.775 +         var checkContainers = true;
   1.776 +
   1.777 +         var currentColumn;
   1.778 +         if (cellSelType) {
   1.779 +           currentColumn = this.view.selection.currentColumn;
   1.780 +           if (currentColumn && !currentColumn.primary)
   1.781 +             checkContainers = false;
   1.782 +         }
   1.783 +
   1.784 +         if (checkContainers) {
   1.785 +           if (this.changeOpenState(this.currentIndex, false)) {
   1.786 +             event.preventDefault();
   1.787 +             return;
   1.788 +           }
   1.789 +           else {
   1.790 +             var parentIndex = this.view.getParentIndex(this.currentIndex);
   1.791 +             if (parentIndex >= 0) {
   1.792 +               if (cellSelType && !this.view.isSelectable(parentIndex, currentColumn)) {
   1.793 +                 return;
   1.794 +               }
   1.795 +               this.view.selection.select(parentIndex);
   1.796 +               this.treeBoxObject.ensureRowIsVisible(parentIndex);
   1.797 +               event.preventDefault();
   1.798 +               return;
   1.799 +             }
   1.800 +           }
   1.801 +         }
   1.802 +
   1.803 +         if (cellSelType) {
   1.804 +           var col = this._getNextColumn(row, true);
   1.805 +           if (col) {
   1.806 +             this.view.selection.currentColumn = col;
   1.807 +             this.treeBoxObject.ensureCellIsVisible(row, col);
   1.808 +             event.preventDefault();
   1.809 +           }
   1.810 +         }
   1.811 +        ]]>
   1.812 +      </handler>
   1.813 +      <handler event="keydown" keycode="VK_RIGHT">
   1.814 +        <![CDATA[
   1.815 +         if (this._editingColumn)
   1.816 +           return;
   1.817 +
   1.818 +          var row = this.currentIndex;
   1.819 +          if (row < 0)
   1.820 +            return;
   1.821 +
   1.822 +          var cellSelType = this._cellSelType;
   1.823 +          var checkContainers = true;
   1.824 +
   1.825 +          var currentColumn;
   1.826 +          if (cellSelType) {
   1.827 +            currentColumn = this.view.selection.currentColumn;
   1.828 +            if (currentColumn && !currentColumn.primary)
   1.829 +              checkContainers = false;
   1.830 +          }
   1.831 +
   1.832 +          if (checkContainers) {
   1.833 +            if (this.changeOpenState(row, true)) {
   1.834 +              event.preventDefault();
   1.835 +              return;
   1.836 +            }
   1.837 +            else {
   1.838 +              var c = row + 1;
   1.839 +              var view = this.view;
   1.840 +              if (c < view.rowCount &&
   1.841 +                  view.getParentIndex(c) == row) {
   1.842 +                // If already opened, select the first child.
   1.843 +                // The getParentIndex test above ensures that the children
   1.844 +                // are already populated and ready.
   1.845 +                if (cellSelType && !this.view.isSelectable(c , currentColumn)) {
   1.846 +                  var col = this._getNextColumn(c, false);
   1.847 +                  if (col) {
   1.848 +                    this.view.selection.currentColumn = col;
   1.849 +                  }
   1.850 +                }
   1.851 +                this.view.selection.timedSelect(c, this._selectDelay);
   1.852 +                this.treeBoxObject.ensureRowIsVisible(c);
   1.853 +                event.preventDefault();
   1.854 +                return;
   1.855 +              }
   1.856 +            }
   1.857 +          }
   1.858 +
   1.859 +          if (cellSelType) {
   1.860 +            var col = this._getNextColumn(row, false);
   1.861 +            if (col) {
   1.862 +              this.view.selection.currentColumn = col;
   1.863 +              this.treeBoxObject.ensureCellIsVisible(row, col);
   1.864 +              event.preventDefault();
   1.865 +            }
   1.866 +          }
   1.867 +        ]]>
   1.868 +      </handler>
   1.869 +      <handler event="keydown" keycode="VK_UP" preventdefault="true"
   1.870 +               modifiers="accel any" action="_moveByOffset(-1, 0, event);"/>
   1.871 +      <handler event="keydown" keycode="VK_DOWN" preventdefault="true"
   1.872 +               modifiers="accel any" action="_moveByOffset(1, this.view.rowCount - 1, event);"/>
   1.873 +      <handler event="keydown" keycode="VK_UP" preventdefault="true"
   1.874 +               modifiers="accel any, shift" action="_moveByOffsetShift(-1, 0, event);"/>
   1.875 +      <handler event="keydown" keycode="VK_DOWN" preventdefault="true"
   1.876 +               modifiers="accel any, shift" action="_moveByOffsetShift(1, this.view.rowCount - 1, event);"/>
   1.877 +      <handler event="keydown" keycode="VK_PAGE_UP" preventdefault="true"
   1.878 +               modifiers="accel any" action="_moveByPage(-1, 0, event);"/>
   1.879 +      <handler event="keydown" keycode="VK_PAGE_DOWN" preventdefault="true"
   1.880 +               modifiers="accel any" action="_moveByPage(1, this.view.rowCount - 1, event);"/>
   1.881 +      <handler event="keydown" keycode="VK_PAGE_UP" preventdefault="true"
   1.882 +               modifiers="accel any, shift" action="_moveByPageShift(-1, 0, event);"/>
   1.883 +      <handler event="keydown" keycode="VK_PAGE_DOWN" preventdefault="true"
   1.884 +               modifiers="accel any, shift" action="_moveByPageShift(1, this.view.rowCount - 1, event);"/>
   1.885 +      <handler event="keydown" keycode="VK_HOME" preventdefault="true"
   1.886 +               modifiers="accel any" action="_moveToEdge(0, event);"/>
   1.887 +      <handler event="keydown" keycode="VK_END" preventdefault="true"
   1.888 +               modifiers="accel any" action="_moveToEdge(this.view.rowCount - 1, event);"/>
   1.889 +      <handler event="keydown" keycode="VK_HOME" preventdefault="true"
   1.890 +               modifiers="accel any, shift" action="_moveToEdgeShift(0, event);"/>
   1.891 +      <handler event="keydown" keycode="VK_END" preventdefault="true"
   1.892 +               modifiers="accel any, shift" action="_moveToEdgeShift(this.view.rowCount - 1, event);"/>
   1.893 +      <handler event="keypress">
   1.894 +        <![CDATA[
   1.895 +         if (this._editingColumn)
   1.896 +           return;
   1.897 +
   1.898 +         if (event.charCode == ' '.charCodeAt(0)) {
   1.899 +           var c = this.currentIndex;
   1.900 +           if (!this.view.selection.isSelected(c) ||
   1.901 +               (!this.view.selection.single && this._isAccelPressed(event))) {
   1.902 +             this.view.selection.toggleSelect(c);
   1.903 +             event.preventDefault();
   1.904 +           }
   1.905 +         }
   1.906 +         else if (!this.disableKeyNavigation && event.charCode > 0 &&
   1.907 +                  !event.altKey && !this._isAccelPressed(event) &&
   1.908 +                  !event.metaKey && !event.ctrlKey) {
   1.909 +           var l = this._keyNavigate(event);
   1.910 +           if (l >= 0) {
   1.911 +             this.view.selection.timedSelect(l, this._selectDelay);
   1.912 +             this.treeBoxObject.ensureRowIsVisible(l);
   1.913 +           }
   1.914 +           event.preventDefault();
   1.915 +         }
   1.916 +         ]]>
   1.917 +      </handler>
   1.918 +    </handlers>    
   1.919 +  </binding>
   1.920 +
   1.921 +  <binding id="treecols" role="xul:treecolumns">
   1.922 +    <resources>
   1.923 +      <stylesheet src="chrome://global/skin/tree.css"/>
   1.924 +    </resources>
   1.925 +    <content orient="horizontal">
   1.926 +      <xul:hbox class="tree-scrollable-columns" flex="1">
   1.927 +        <children includes="treecol|splitter"/>
   1.928 +      </xul:hbox>
   1.929 +      <xul:treecolpicker class="treecol-image" fixed="true" xbl:inherits="tooltiptext=pickertooltiptext"/>
   1.930 +    </content>
   1.931 +    <implementation>
   1.932 +      <constructor><![CDATA[
   1.933 +        // Set resizeafter="farthest" on the splitters if nothing else has been
   1.934 +        // specified.
   1.935 +        Array.forEach(this.getElementsByTagName("splitter"), function (splitter) {
   1.936 +          if (!splitter.hasAttribute("resizeafter"))
   1.937 +            splitter.setAttribute("resizeafter", "farthest");
   1.938 +        });
   1.939 +      ]]></constructor>
   1.940 +    </implementation>
   1.941 +  </binding>
   1.942 +
   1.943 +  <binding id="treerows" extends="chrome://global/content/bindings/tree.xml#tree-base">
   1.944 +    <content>
   1.945 +      <xul:hbox flex="1" class="tree-bodybox">
   1.946 +        <children/>
   1.947 +      </xul:hbox>
   1.948 +      <xul:scrollbar height="0" minwidth="0" minheight="0" orient="vertical" xbl:inherits="collapsed=hidevscroll" style="position:relative; z-index:2147483647;"/>
   1.949 +    </content>
   1.950 +    <handlers>
   1.951 +      <handler event="underflow">
   1.952 +        <![CDATA[
   1.953 +          // Scrollport event orientation
   1.954 +          // 0: vertical
   1.955 +          // 1: horizontal
   1.956 +          // 2: both (not used)
   1.957 +          var tree = document.getBindingParent(this);
   1.958 +          if (event.detail == 1)
   1.959 +            tree.setAttribute("hidehscroll", "true");
   1.960 +          else if (event.detail == 0)
   1.961 +            tree.setAttribute("hidevscroll", "true");
   1.962 +          event.stopPropagation();
   1.963 +        ]]>
   1.964 +      </handler>
   1.965 +      <handler event="overflow">
   1.966 +        <![CDATA[
   1.967 +          var tree = document.getBindingParent(this);
   1.968 +          if (event.detail == 1)
   1.969 +            tree.removeAttribute("hidehscroll");
   1.970 +          else if (event.detail == 0)
   1.971 +            tree.removeAttribute("hidevscroll");
   1.972 +          event.stopPropagation();
   1.973 +        ]]>
   1.974 +      </handler>
   1.975 +    </handlers>
   1.976 +  </binding>
   1.977 +
   1.978 +  <binding id="treebody" extends="chrome://global/content/bindings/tree.xml#tree-base">
   1.979 +    <implementation>
   1.980 +      <constructor>
   1.981 +        if ("_ensureColumnOrder" in this.parentNode)
   1.982 +          this.parentNode._ensureColumnOrder();
   1.983 +      </constructor>
   1.984 +
   1.985 +      <field name="_lastSelectedRow">
   1.986 +        -1
   1.987 +      </field>
   1.988 +    </implementation>
   1.989 +    <handlers>
   1.990 +      <!-- If there is no modifier key, we select on mousedown, not
   1.991 +           click, so that drags work correctly. -->
   1.992 +      <handler event="mousedown" clickcount="1">
   1.993 +      <![CDATA[
   1.994 +         if (this.parentNode.disabled)
   1.995 +           return;
   1.996 +         if (((!this._isAccelPressed(event) ||
   1.997 +             !this.parentNode.pageUpOrDownMovesSelection) &&
   1.998 +             !event.shiftKey && !event.metaKey) ||
   1.999 +             this.parentNode.view.selection.single) {
  1.1000 +           var row = {};
  1.1001 +           var col = {};
  1.1002 +           var obj = {};
  1.1003 +           var b = this.parentNode.treeBoxObject;
  1.1004 +           b.getCellAt(event.clientX, event.clientY, row, col, obj);
  1.1005 +
  1.1006 +           // save off the last selected row
  1.1007 +           this._lastSelectedRow = row.value;
  1.1008 +
  1.1009 +           if (row.value == -1)
  1.1010 +             return;
  1.1011 +
  1.1012 +           if (obj.value == "twisty")
  1.1013 +             return;
  1.1014 +
  1.1015 +           if (col.value) {
  1.1016 +             if (col.value.cycler) {
  1.1017 +               b.view.cycleCell(row.value, col.value);
  1.1018 +               return;
  1.1019 +             } else if (col.value.type == Components.interfaces.nsITreeColumn.TYPE_CHECKBOX) {
  1.1020 +               if (this.parentNode.editable && col.value.editable &&
  1.1021 +                   b.view.isEditable(row.value, col.value)) {
  1.1022 +                 var value = b.view.getCellValue(row.value, col.value);
  1.1023 +                 value = value == "true" ? "false" : "true";
  1.1024 +                 b.view.setCellValue(row.value, col.value, value);
  1.1025 +                 return;
  1.1026 +               }
  1.1027 +             }
  1.1028 +           } 
  1.1029 +
  1.1030 +           var cellSelType = this.parentNode._cellSelType;
  1.1031 +           if (cellSelType == "text" && obj.value != "text" && obj.value != "image")
  1.1032 +             return;
  1.1033 +
  1.1034 +           if (cellSelType) {
  1.1035 +             if (!col.value.selectable ||
  1.1036 +                 !b.view.isSelectable(row.value, col.value)) {
  1.1037 +               return;
  1.1038 +             }
  1.1039 +           }
  1.1040 +
  1.1041 +           if (!b.view.selection.isSelected(row.value)) {
  1.1042 +             b.view.selection.select(row.value);
  1.1043 +             b.ensureRowIsVisible(row.value);
  1.1044 +           }
  1.1045 +
  1.1046 +           if (cellSelType) {
  1.1047 +             b.view.selection.currentColumn = col.value;
  1.1048 +           }
  1.1049 +         }
  1.1050 +      ]]>
  1.1051 +      </handler>
  1.1052 +
  1.1053 +      <!-- On a click (up+down on the same item), deselect everything
  1.1054 +           except this item. -->
  1.1055 +      <handler event="click" button="0" clickcount="1">
  1.1056 +      <![CDATA[
  1.1057 +        if (this.parentNode.disabled)
  1.1058 +          return;
  1.1059 +        var row = {};
  1.1060 +        var col = {};
  1.1061 +        var obj = {};
  1.1062 +        var b = this.parentNode.treeBoxObject;
  1.1063 +        b.getCellAt(event.clientX, event.clientY, row, col, obj);
  1.1064 +
  1.1065 +        if (row.value == -1)
  1.1066 +          return;
  1.1067 +
  1.1068 +        if (obj.value == "twisty") {
  1.1069 +          if (b.view.selection.currentIndex >= 0 &&
  1.1070 +              b.view.isContainerOpen(row.value)) {
  1.1071 +            var parentIndex = b.view.getParentIndex(b.view.selection.currentIndex);
  1.1072 +            while (parentIndex >= 0 && parentIndex != row.value)
  1.1073 +              parentIndex = b.view.getParentIndex(parentIndex);
  1.1074 +            if (parentIndex == row.value) {
  1.1075 +              var parentSelectable = true;
  1.1076 +              if (this.parentNode._cellSelType) {
  1.1077 +                var currentColumn = b.view.selection.currentColumn;
  1.1078 +                if (!b.view.isSelectable(parentIndex, currentColumn))
  1.1079 +                  parentSelectable = false;
  1.1080 +              }
  1.1081 +              if (parentSelectable)
  1.1082 +                b.view.selection.select(parentIndex);
  1.1083 +            }
  1.1084 +          }
  1.1085 +          this.parentNode.changeOpenState(row.value);
  1.1086 +          return;
  1.1087 +        }
  1.1088 +
  1.1089 +        if (! b.view.selection.single) {
  1.1090 +          var augment = this._isAccelPressed(event);
  1.1091 +          if (event.shiftKey) {
  1.1092 +            b.view.selection.rangedSelect(-1, row.value, augment);
  1.1093 +            b.ensureRowIsVisible(row.value);
  1.1094 +            return;
  1.1095 +          }
  1.1096 +          if (augment) {
  1.1097 +            b.view.selection.toggleSelect(row.value);
  1.1098 +            b.ensureRowIsVisible(row.value);
  1.1099 +            b.view.selection.currentIndex = row.value;
  1.1100 +            return;
  1.1101 +          }
  1.1102 +        }
  1.1103 +
  1.1104 +        /* We want to deselect all the selected items except what was
  1.1105 +          clicked, UNLESS it was a right-click.  We have to do this
  1.1106 +          in click rather than mousedown so that you can drag a
  1.1107 +          selected group of items */
  1.1108 +
  1.1109 +        if (!col.value) return;
  1.1110 +
  1.1111 +        // if the last row has changed in between the time we 
  1.1112 +        // mousedown and the time we click, don't fire the select handler.
  1.1113 +        // see bug #92366
  1.1114 +        if (!col.value.cycler && this._lastSelectedRow == row.value &&
  1.1115 +            col.value.type != Components.interfaces.nsITreeColumn.TYPE_CHECKBOX) {
  1.1116 +
  1.1117 +          var cellSelType = this.parentNode._cellSelType;
  1.1118 +          if (cellSelType == "text" && obj.value != "text" && obj.value != "image")
  1.1119 +            return;
  1.1120 +
  1.1121 +          if (cellSelType) {
  1.1122 +            if (!col.value.selectable ||
  1.1123 +                !b.view.isSelectable(row.value, col.value)) {
  1.1124 +              return;
  1.1125 +            }
  1.1126 +          }
  1.1127 +
  1.1128 +          b.view.selection.select(row.value);  
  1.1129 +          b.ensureRowIsVisible(row.value);
  1.1130 +
  1.1131 +          if (cellSelType) {
  1.1132 +            b.view.selection.currentColumn = col.value;
  1.1133 +          }
  1.1134 +        }
  1.1135 +      ]]>
  1.1136 +      </handler>
  1.1137 +
  1.1138 +      <!-- double-click -->
  1.1139 +      <handler event="click" clickcount="2">
  1.1140 +      <![CDATA[
  1.1141 +        if (this.parentNode.disabled)
  1.1142 +          return;
  1.1143 +        var tbo = this.parentNode.treeBoxObject;
  1.1144 +        var row = tbo.view.selection.currentIndex;
  1.1145 +        if (row == -1)
  1.1146 +          return;
  1.1147 +
  1.1148 +        var col = {};
  1.1149 +        var obj = {};
  1.1150 +        tbo.getCellAt(event.clientX, event.clientY, {}, col, obj);
  1.1151 +
  1.1152 +        if (obj.value != "twisty")
  1.1153 +          this.parentNode.startEditing(row, col.value);
  1.1154 +
  1.1155 +        if (this.parentNode._editingColumn || !tbo.view.isContainer(row))
  1.1156 +          return;
  1.1157 +
  1.1158 +        // Cyclers and twisties respond to single clicks, not double clicks
  1.1159 +        if (col.value != -1 && !col.value.cycler && obj.value != "twisty")
  1.1160 +          this.parentNode.changeOpenState(row);
  1.1161 +      ]]>
  1.1162 +      </handler>
  1.1163 +      
  1.1164 +    </handlers>
  1.1165 +  </binding>
  1.1166 +
  1.1167 +  <binding id="treecol-base" role="xul:treecolumnitem"
  1.1168 +           extends="chrome://global/content/bindings/tree.xml#tree-base">
  1.1169 +    <implementation>
  1.1170 +      <constructor>
  1.1171 +        this.parentNode.parentNode._columnsDirty = true;
  1.1172 +      </constructor>
  1.1173 +
  1.1174 +      <property name="ordinal">
  1.1175 +        <getter><![CDATA[
  1.1176 +          var val = this.getAttribute("ordinal");
  1.1177 +          return "" + (val == "" ? 1 : (val == "0" ? 0 : parseInt(val)));
  1.1178 +        ]]></getter>
  1.1179 +        <setter><![CDATA[
  1.1180 +          this.setAttribute("ordinal", val);
  1.1181 +          return val;
  1.1182 +        ]]></setter>
  1.1183 +      </property>
  1.1184 +      
  1.1185 +      <property name="_previousVisibleColumn">
  1.1186 +        <getter><![CDATA[
  1.1187 +          var sib = this.boxObject.previousSibling;
  1.1188 +          while (sib) {
  1.1189 +            if (sib.localName == "treecol" && sib.boxObject.width > 0 && sib.parentNode == this.parentNode)
  1.1190 +              return sib;
  1.1191 +            sib = sib.boxObject.previousSibling;
  1.1192 +          }
  1.1193 +          return null;
  1.1194 +        ]]></getter>
  1.1195 +      </property>
  1.1196 +
  1.1197 +      <method name="_onDragMouseMove">
  1.1198 +        <parameter name="aEvent"/>
  1.1199 +        <body><![CDATA[
  1.1200 +          var col = document.treecolDragging;
  1.1201 +          if (!col) return;
  1.1202 +
  1.1203 +          // determine if we have moved the mouse far enough
  1.1204 +          // to initiate a drag
  1.1205 +          if (col.mDragGesturing) {
  1.1206 +            if (Math.abs(aEvent.clientX - col.mStartDragX) < 5 &&
  1.1207 +                Math.abs(aEvent.clientY - col.mStartDragY) < 5) {
  1.1208 +              return;
  1.1209 +            } else {
  1.1210 +              col.mDragGesturing = false;
  1.1211 +              col.setAttribute("dragging", "true");
  1.1212 +              window.addEventListener("click", col._onDragMouseClick, true);
  1.1213 +            }
  1.1214 +          }
  1.1215 +                    
  1.1216 +          var pos = {};
  1.1217 +          var targetCol = col.parentNode.parentNode._getColumnAtX(aEvent.clientX, 0.5, pos);
  1.1218 +          
  1.1219 +          // bail if we haven't mousemoved to a different column
  1.1220 +          if (col.mTargetCol == targetCol && col.mTargetDir == pos.value)
  1.1221 +            return;
  1.1222 +
  1.1223 +          var tree = col.parentNode.parentNode;
  1.1224 +          var sib;
  1.1225 +          var column;
  1.1226 +          if (col.mTargetCol) {
  1.1227 +            // remove previous insertbefore/after attributes
  1.1228 +            col.mTargetCol.removeAttribute("insertbefore");
  1.1229 +            col.mTargetCol.removeAttribute("insertafter");
  1.1230 +            column = tree.columns.getColumnFor(col.mTargetCol);
  1.1231 +            tree.treeBoxObject.invalidateColumn(column);
  1.1232 +            sib = col.mTargetCol._previousVisibleColumn;
  1.1233 +            if (sib) {
  1.1234 +              sib.removeAttribute("insertafter");
  1.1235 +              column = tree.columns.getColumnFor(sib);
  1.1236 +              tree.treeBoxObject.invalidateColumn(column);
  1.1237 +            }
  1.1238 +            col.mTargetCol = null;
  1.1239 +            col.mTargetDir = null;
  1.1240 +          }
  1.1241 +          
  1.1242 +          if (targetCol) {
  1.1243 +            // set insertbefore/after attributes
  1.1244 +            if (pos.value == "after") {
  1.1245 +              targetCol.setAttribute("insertafter", "true");
  1.1246 +            } else {
  1.1247 +              targetCol.setAttribute("insertbefore", "true");
  1.1248 +              sib = targetCol._previousVisibleColumn;
  1.1249 +              if (sib) {
  1.1250 +                sib.setAttribute("insertafter", "true");
  1.1251 +                column = tree.columns.getColumnFor(sib);
  1.1252 +                tree.treeBoxObject.invalidateColumn(column);
  1.1253 +              }
  1.1254 +            }
  1.1255 +            column = tree.columns.getColumnFor(targetCol);
  1.1256 +            tree.treeBoxObject.invalidateColumn(column);
  1.1257 +            col.mTargetCol = targetCol;
  1.1258 +            col.mTargetDir = pos.value;
  1.1259 +          }
  1.1260 +        ]]></body>        
  1.1261 +      </method>
  1.1262 +
  1.1263 +      <method name="_onDragMouseUp">
  1.1264 +        <parameter name="aEvent"/>
  1.1265 +        <body><![CDATA[
  1.1266 +          var col = document.treecolDragging;
  1.1267 +          if (!col) return;
  1.1268 +          
  1.1269 +          if (!col.mDragGesturing) {
  1.1270 +            if (col.mTargetCol) {
  1.1271 +              // remove insertbefore/after attributes
  1.1272 +              var before = col.mTargetCol.hasAttribute("insertbefore");
  1.1273 +              col.mTargetCol.removeAttribute(before ? "insertbefore" : "insertafter");
  1.1274 +
  1.1275 +              var sib = col.mTargetCol._previousVisibleColumn;
  1.1276 +              if (before && sib) {
  1.1277 +                sib.removeAttribute("insertafter");
  1.1278 +              }
  1.1279 +
  1.1280 +              // Move the column only if it will result in a different column
  1.1281 +              // ordering
  1.1282 +              var move = true;
  1.1283 +
  1.1284 +              // If this is a before move and the previous visible column is
  1.1285 +              // the same as the column we're moving, don't move
  1.1286 +              if (before && col == sib) {
  1.1287 +                move = false;
  1.1288 +              }
  1.1289 +              else if (!before && col == col.mTargetCol) {
  1.1290 +                // If this is an after move and the column we're moving is
  1.1291 +                // the same as the target column, don't move.
  1.1292 +                move = false;
  1.1293 +              }
  1.1294 +
  1.1295 +              if (move) {
  1.1296 +                col.parentNode.parentNode._reorderColumn(col, col.mTargetCol, before);
  1.1297 +              }
  1.1298 +
  1.1299 +              // repaint to remove lines
  1.1300 +              col.parentNode.parentNode.treeBoxObject.invalidate();
  1.1301 +  
  1.1302 +              col.mTargetCol = null;
  1.1303 +            }
  1.1304 +          } else
  1.1305 +            col.mDragGesturing = false;
  1.1306 +                    
  1.1307 +          document.treecolDragging = null;
  1.1308 +          col.removeAttribute("dragging");
  1.1309 +          
  1.1310 +          window.removeEventListener("mousemove", col._onDragMouseMove, true);
  1.1311 +          window.removeEventListener("mouseup", col._onDragMouseUp, true);
  1.1312 +          // we have to wait for the click event to fire before removing
  1.1313 +          // cancelling handler
  1.1314 +          var clickHandler = function(handler) { 
  1.1315 +            window.removeEventListener("click", handler, true);
  1.1316 +          };
  1.1317 +          window.setTimeout(clickHandler, 0, col._onDragMouseClick);
  1.1318 +        ]]></body>        
  1.1319 +      </method>
  1.1320 +
  1.1321 +      <method name="_onDragMouseClick">
  1.1322 +        <parameter name="aEvent"/>
  1.1323 +        <body><![CDATA[
  1.1324 +          // prevent click event from firing after column drag and drop
  1.1325 +          aEvent.stopPropagation();
  1.1326 +          aEvent.preventDefault();
  1.1327 +        ]]></body>        
  1.1328 +      </method>
  1.1329 +    </implementation>
  1.1330 +    
  1.1331 +    <handlers>
  1.1332 +      <handler event="mousedown" button="0"><![CDATA[
  1.1333 +        if (this.parentNode.parentNode.enableColumnDrag) {
  1.1334 +          var xulns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
  1.1335 +          var cols = this.parentNode.getElementsByTagNameNS(xulns, "treecol");
  1.1336 +          
  1.1337 +          // only start column drag operation if there are at least 2 visible columns
  1.1338 +          var visible = 0;
  1.1339 +          for (var i = 0; i < cols.length; ++i)
  1.1340 +            if (cols[i].boxObject.width > 0) ++visible;
  1.1341 +            
  1.1342 +          if (visible > 1) {
  1.1343 +            window.addEventListener("mousemove", this._onDragMouseMove, true);
  1.1344 +            window.addEventListener("mouseup", this._onDragMouseUp, true);
  1.1345 +            document.treecolDragging = this;
  1.1346 +            this.mDragGesturing = true;
  1.1347 +            this.mStartDragX = event.clientX;
  1.1348 +            this.mStartDragY = event.clientY;
  1.1349 +          }
  1.1350 +        }
  1.1351 +      ]]></handler>
  1.1352 +      <handler event="click" button="0" phase="target">
  1.1353 +        <![CDATA[
  1.1354 +          if (event.target != event.originalTarget)
  1.1355 +            return;
  1.1356 +
  1.1357 +#ifdef XP_WIN
  1.1358 +          // On Windows multiple clicking on tree columns only cycles one time
  1.1359 +          // every 2 clicks.
  1.1360 +          if (event.detail % 2 == 0)
  1.1361 +            return;
  1.1362 +#endif
  1.1363 +
  1.1364 +          var tree = this.parentNode.parentNode;
  1.1365 +          var column = tree.columns.getColumnFor(this);
  1.1366 +          tree.view.cycleHeader(column);
  1.1367 +        ]]>
  1.1368 +      </handler>
  1.1369 +    </handlers>
  1.1370 +  </binding>
  1.1371 +
  1.1372 +  <binding id="treecol" extends="chrome://global/content/bindings/tree.xml#treecol-base">
  1.1373 +    <content>
  1.1374 +      <xul:label class="treecol-text" xbl:inherits="crop,value=label" flex="1" crop="right"/>
  1.1375 +      <xul:image class="treecol-sortdirection" xbl:inherits="sortDirection,hidden=hideheader"/>
  1.1376 +    </content>
  1.1377 +  </binding>
  1.1378 +
  1.1379 +  <binding id="treecol-image" extends="chrome://global/content/bindings/tree.xml#treecol-base">
  1.1380 +    <content>
  1.1381 +      <xul:image class="treecol-icon" xbl:inherits="src"/>
  1.1382 +    </content>
  1.1383 +  </binding>
  1.1384 +
  1.1385 +  <binding id="columnpicker" display="xul:button" role="xul:button"
  1.1386 +           extends="chrome://global/content/bindings/tree.xml#tree-base">
  1.1387 +    <content>
  1.1388 +      <xul:image class="tree-columnpicker-icon"/>
  1.1389 +      <xul:menupopup anonid="popup">
  1.1390 +        <xul:menuseparator anonid="menuseparator"/>
  1.1391 +        <xul:menuitem anonid="menuitem" label="&restoreColumnOrder.label;"/>
  1.1392 +      </xul:menupopup>
  1.1393 +    </content>
  1.1394 +    
  1.1395 +    <implementation>
  1.1396 +      <method name="buildPopup">
  1.1397 +        <parameter name="aPopup"/>
  1.1398 +        <body>
  1.1399 +          <![CDATA[
  1.1400 +            // We no longer cache the picker content, remove the old content.
  1.1401 +            while (aPopup.childNodes.length > 2)
  1.1402 +              aPopup.removeChild(aPopup.firstChild);
  1.1403 +
  1.1404 +            var refChild = aPopup.firstChild;
  1.1405 +
  1.1406 +            var tree = this.parentNode.parentNode;
  1.1407 +            for (var currCol = tree.columns.getFirstColumn(); currCol;
  1.1408 +                 currCol = currCol.getNext()) {
  1.1409 +              // Construct an entry for each column in the row, unless
  1.1410 +              // it is not being shown.
  1.1411 +              var currElement = currCol.element;
  1.1412 +              if (!currElement.hasAttribute("ignoreincolumnpicker")) {
  1.1413 +                var popupChild = document.createElement("menuitem");
  1.1414 +                popupChild.setAttribute("type", "checkbox");
  1.1415 +                var columnName = currElement.getAttribute("display") ||
  1.1416 +                                 currElement.getAttribute("label");
  1.1417 +                popupChild.setAttribute("label", columnName);
  1.1418 +                popupChild.setAttribute("colindex", currCol.index);
  1.1419 +                if (currElement.getAttribute("hidden") != "true")
  1.1420 +                  popupChild.setAttribute("checked", "true");
  1.1421 +                if (currCol.primary)
  1.1422 +                  popupChild.setAttribute("disabled", "true");
  1.1423 +                aPopup.insertBefore(popupChild, refChild);
  1.1424 +              }
  1.1425 +            }
  1.1426 +
  1.1427 +            var hidden = !tree.enableColumnDrag;
  1.1428 +            const anonids = ["menuseparator", "menuitem"];
  1.1429 +            for (var i = 0; i < anonids.length; i++) {
  1.1430 +              var element = document.getAnonymousElementByAttribute(this, "anonid", anonids[i]);
  1.1431 +              element.hidden = hidden;
  1.1432 +            }
  1.1433 +          ]]>
  1.1434 +        </body>
  1.1435 +      </method>
  1.1436 +    </implementation>
  1.1437 +
  1.1438 +    <handlers>
  1.1439 +      <handler event="command">
  1.1440 +        <![CDATA[
  1.1441 +          if (event.originalTarget == this) {
  1.1442 +            var popup = document.getAnonymousElementByAttribute(this, "anonid", "popup");
  1.1443 +            this.buildPopup(popup);
  1.1444 +            popup.showPopup(this, -1, -1, "popup", "bottomright", "topright");
  1.1445 +          }
  1.1446 +          else {
  1.1447 +            var tree = this.parentNode.parentNode;
  1.1448 +            tree.stopEditing(true);
  1.1449 +            var menuitem = document.getAnonymousElementByAttribute(this, "anonid", "menuitem");
  1.1450 +            if (event.originalTarget == menuitem) {
  1.1451 +              tree.columns.restoreNaturalOrder();
  1.1452 +              tree._ensureColumnOrder();
  1.1453 +            }
  1.1454 +            else {
  1.1455 +              var colindex = event.originalTarget.getAttribute("colindex");
  1.1456 +              var column = tree.columns[colindex];
  1.1457 +              if (column) {
  1.1458 +                var element = column.element;
  1.1459 +                if (element.getAttribute("hidden") == "true")
  1.1460 +                  element.setAttribute("hidden", "false");
  1.1461 +                else
  1.1462 +                  element.setAttribute("hidden", "true");
  1.1463 +              }
  1.1464 +            }
  1.1465 +          }
  1.1466 +        ]]>
  1.1467 +      </handler>
  1.1468 +    </handlers>
  1.1469 +  </binding>
  1.1470 +</bindings>
  1.1471 +

mercurial