<?xml version="1.0"?>

<bindings id="treeBindings"
   xmlns="http://www.mozilla.org/xbl"
   xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
   xmlns:xbl="http://www.mozilla.org/xbl">
  
  <binding id="tree-base">
    <resources>
      <stylesheet src="chrome://global/skin/tree.css"/>
    </resources>
  </binding>

  <binding id="tree" extends="chrome://global/content/bindings/tree.xml#tree-base">
    <content>
      <children includes="treecols"/>
      <xul:treerows class="tree-rows" flex="1">
        <children/>
      </xul:treerows>
    </content>
    
    <implementation implements="nsIAccessibleProvider">
      <constructor>
        if (navigator.platform.indexOf("Mac") != -1)
          this.pageUpOrDownMovesSelection = false;
      </constructor>

      <!-- ///////////////// nsIAccessibleProvider ///////////////// -->

      <property name="accessible">
        <getter>
          <![CDATA[
            var accService = Components.classes["@mozilla.org/accessibilityService;1"].getService(Components.interfaces.nsIAccessibilityService);
            return accService.createXULTreeAccessible(this);
          ]]>
        </getter>
      </property>

      <property name="treeBoxObject"
                onget="return this.boxObject.QueryInterface(Components.interfaces.nsITreeBoxObject);"
                readonly="true"/>
      <property name="view"
                onget="return this.treeBoxObject.view;"
                onset="return this.treeBoxObject.view=val;"/>
      <property name="contentView"
                onget="return this.treeBoxObject.view; /*.QueryInterface(Components.interfaces.nsITreeContentView)*/"
                readonly="true"/>
      <property name="builderView"
                onget="return this.treeBoxObject.view; /*.QueryInterface(Components.interfaces.nsIXULTreeBuilder)*/"
                readonly="true"/>
      <property name="currentIndex"
                onget="return this.treeBoxObject.selection.currentIndex;"
                onset="return this.treeBoxObject.selection.currentIndex=val;"/>
      <field name="pageUpOrDownMovesSelection">
        true
      </field>
      <field name="selectionHead">
        -1
      </field>
      <field name="selectionTail">
        -1
      </field>
      <property name="enableColumnDrag"
                onget="return this.hasAttribute('enableColumnDrag');"
                onset="if (val) this.setAttribute('enableColumnDrag', 'true');
                       else this.removeAttribute('enableColumnDrag'); return val;"/>

      <property name="firstOrdinalColumn">
        <getter><![CDATA[
          var cols = this.firstChild;
          while (cols && cols.localName != "treecols")
            cols = cols.nextSibling;

          if (cols)
            return cols.boxObject.firstChild;
          else
            return null;
        ]]></getter>
      </property>

      <property name="disableKeyNavigation"
                onget="return this.hasAttribute('disableKeyNavigation');"
                onset="if (val) this.setAttribute('disableKeyNavigation', 'true');
                       else this.removeAttribute('disableKeyNavigation'); return val;"/>
      
      <property name="_selectDelay" 
                onset="this.setAttribute('_selectDelay', val);"
                onget="return this.getAttribute('_selectDelay') || 50;"/>
      <field name="_columnsDirty">true</field>
      <field name="_lastKeyTime">0</field>
      <field name="_incrementalString">""</field>

      <method name="_ensureColumnOrder">
        <body><![CDATA[
          if (this._columnsDirty) {
            // update the ordinal position of each column to assure that it is
            // an odd number and 2 positions above it's next sibling
            var col = this.firstOrdinalColumn;
            var cols = [];
            while (col) {
              if (col.localName == "treecol" && col.parentNode.parentNode == this)
                cols[cols.length] = col;
              col = col.boxObject.nextSibling;
            }
            var i;
            for (i = 0; i < cols.length; ++i)
              cols[i].setAttribute("ordinal", (i*2)+1);

            // update the ordinal positions of splitters to even numbers, so that 
            // they are in between columns
            var splitters = this.getElementsByTagName("splitter");
            for (i = 0; i < splitters.length; ++i)
              splitters[i].setAttribute("ordinal", (i+1)*2);
            
            this._columnsDirty = false;
          }
        ]]></body>
      </method>

      <method name="_reorderColumn">
        <parameter name="aColMove"/>
        <parameter name="aColBefore"/>
        <parameter name="aBefore"/>
        <body><![CDATA[
          this._ensureColumnOrder();
          
          var cols = [];
          var col;
          if (aColBefore.ordinal < aColMove.ordinal) {
            col = aColBefore;
            while (col) {
              if (col.localName == "treecol")
                cols.push(col);
              col = col.boxObject.nextSibling;
              if (col == aColMove)  
                break;
            }

            aColMove.ordinal = aColBefore.ordinal;
            var i;
            for (i = 0; i < cols.length; ++i)
              cols[i].ordinal += 2;
          } else {
            col = aColMove.boxObject.nextSibling;
            while (col) {
              if (col.localName == "treecol")
                cols.push(col);
              col = col.boxObject.nextSibling;
              if (col == aColBefore && aBefore)
                break;
            }

            aColMove.ordinal = aBefore ? aColBefore.ordinal-2 : aColBefore.ordinal;

            for (i = 0; i < cols.length; ++i)
              cols[i].ordinal -= 2;
          }
        ]]></body>
      </method>
      
      <method name="_getColumnAtX">
        <parameter name="aX"/>
        <parameter name="aThresh"/>
        <parameter name="aPos"/>
        <body><![CDATA[
          if (aPos) aPos.value = "before";

          var col = this.firstOrdinalColumn;
          var lastCol = null;
          var currentX = this.boxObject.x;
          while (col) {
            if (col.localName == "treecol" && col.parentNode.parentNode == this) {
              var cw = col.boxObject.width;
              if (cw > 0) {
                currentX += cw;
                if (currentX - (cw*aThresh) > aX)
                  return col;
              }
              lastCol = col;
            }
            col = col.boxObject.nextSibling;
          }

          if (aPos) aPos.value = "after";
          return lastCol;
        ]]></body>
      </method>

    </implementation>
    
    <handlers>
      <handler event="DOMMouseScroll" phase="capturing">
        <![CDATA[
          var rows = event.detail;
          if (rows == NSUIEvent.SCROLL_PAGE_UP)
            this.treeBoxObject.scrollByPages(-1);
          else if (rows == NSUIEvent.SCROLL_PAGE_DOWN)
            this.treeBoxObject.scrollByPages(1);
          else
            this.treeBoxObject.scrollByLines(rows);
        ]]>
      </handler>
      <handler event="focus" action="this.treeBoxObject.focused = true;"/>
      <handler event="blur" action="this.treeBoxObject.focused = false;"/>
  
      <handler event="dragenter" action="this.treeBoxObject.onDragEnter(event);"/>
      <handler event="dragexit" action="this.treeBoxObject.onDragExit(event);"/>
      <handler event="dragover" action="this.treeBoxObject.onDragOver(event);"/>
      <handler event="dragdrop" action="this.treeBoxObject.onDragDrop(event);"/>

      <handler event="keypress" keycode="vk_enter">
        <![CDATA[
         if (this.currentIndex == -1)
           return;
         if (this.treeBoxObject.view.isContainer(this.currentIndex))
           this.treeBoxObject.view.toggleOpenState(this.currentIndex);
        ]]>
      </handler>
      <handler event="keypress" keycode="vk_return">
        <![CDATA[
         if (this.currentIndex == -1)
           return;
         if (this.treeBoxObject.view.isContainer(this.currentIndex))
           this.treeBoxObject.view.toggleOpenState(this.currentIndex);
        ]]>
      </handler>
      <handler event="keypress" keycode="vk_left">
        <![CDATA[
         if (this.currentIndex == -1)
           return;
         if (this.treeBoxObject.view.isContainer(this.currentIndex) &&
             this.treeBoxObject.view.isContainerOpen(this.currentIndex)) {
           this.treeBoxObject.view.toggleOpenState(this.currentIndex);
         }
         else {
           var parentIndex = this.treeBoxObject.view.getParentIndex(this.currentIndex);
           if (parentIndex >= 0) {
             this.treeBoxObject.selection.select(parentIndex);
             this.treeBoxObject.ensureRowIsVisible(parentIndex);
           }
         }
        ]]>
      </handler>
      <handler event="keypress" keycode="vk_right">
        <![CDATA[
         if (this.currentIndex == -1)
           return;
         if (this.treeBoxObject.view.isContainer(this.currentIndex)) {
           if (!this.treeBoxObject.view.isContainerOpen(this.currentIndex))
             this.treeBoxObject.view.toggleOpenState(this.currentIndex);
         }
        ]]>
      </handler>
      <handler event="keypress" keycode="vk_up">
        <![CDATA[
         var c = this.currentIndex;
         if (c == -1 || c == 0)
           return;
         this.selectionHead = -1;
         this.selectionTail = -1;
         this.treeBoxObject.selection.timedSelect(c-1, this._selectDelay);
         this.treeBoxObject.ensureRowIsVisible(c-1);
        ]]>
      </handler>
      <handler event="keypress" keycode="vk_down">
        <![CDATA[
         var c = this.currentIndex;
         try { if (c+1 == this.treeBoxObject.view.rowCount)
           return;
         } catch (e) {}
         this.selectionHead = -1;
         this.selectionTail = -1;
         this.treeBoxObject.selection.timedSelect(c+1, this._selectDelay);
         this.treeBoxObject.ensureRowIsVisible(c+1);
        ]]>
      </handler>
      <handler event="keypress" keycode="vk_up" modifiers="shift">
        <![CDATA[
         if (this.treeBoxObject.selection.single)
           return;
         var c = this.currentIndex;
         if (c == -1 || c == 0)
           return;
         if (c == this.selectionTail) {
           if (this.selectionHead < this.selectionTail) {
             this.treeBoxObject.selection.toggleSelect(c);
             this.currentIndex = c - 1;
           }
           else {
             this.treeBoxObject.selection.toggleSelect(c - 1);
           }
         }
         else {
           this.treeBoxObject.selection.clearSelection();
           this.selectionHead = c;
           this.treeBoxObject.selection.rangedSelect(c, c - 1, true);
         }
         this.selectionTail = c - 1;
         this.treeBoxObject.ensureRowIsVisible(c - 1);
        ]]>
      </handler>
      <handler event="keypress" keycode="vk_down" modifiers="shift">
        <![CDATA[
         if (this.treeBoxObject.selection.single)
           return;
         var c = this.currentIndex;
         try { if (c+1 == this.treeBoxObject.view.rowCount)
           return;
         } catch (e) {}
         if (c == this.selectionTail) {
           if (this.selectionHead > this.selectionTail) {
             this.treeBoxObject.selection.toggleSelect(c);
             this.currentIndex = c + 1;
           }
           else
             this.treeBoxObject.selection.toggleSelect(c + 1);
         }
         else {
           this.treeBoxObject.selection.clearSelection();
           this.selectionHead = c;
           this.treeBoxObject.selection.rangedSelect(c, c + 1, true);
         }
         this.selectionTail = c + 1;
         this.treeBoxObject.ensureRowIsVisible(c + 1);
        ]]>
      </handler>
      <handler event="keypress" keycode="vk_up" modifiers="control">
        <![CDATA[
         var c = this.currentIndex;
         if (c == -1 || c == 0)
           return;
         this.currentIndex = c-1;
         this.treeBoxObject.ensureRowIsVisible(c-1);
        ]]>
      </handler>
      <handler event="keypress" keycode="vk_down" modifiers="control">
        <![CDATA[
         var c = this.currentIndex;
         try { if (c+1 == this.treeBoxObject.view.rowCount)
           return;
         } catch (e) {}
         this.currentIndex = c+1;
         this.treeBoxObject.ensureRowIsVisible(c+1);
        ]]>
      </handler>
      <handler event="keypress" keycode="vk_page_up">
        <![CDATA[
         if (! this.pageUpOrDownMovesSelection) {
           this.treeBoxObject.scrollByPages(-1);
           return;
         }
         var c = this.currentIndex;
         if (c == 0)
           return;
         this.selectionHead = -1;
         this.selectionTail = -1;
         var f = this.treeBoxObject.getFirstVisibleRow();
         var i = 0;
         if (f > 0) {
           var p = this.treeBoxObject.getPageCount();
           if (f - p >= 0)
             i = c - p;
           else
             i = c - f;
           this.treeBoxObject.scrollByPages(-1);
         }
         this.treeBoxObject.selection.timedSelect(i, this._selectDelay);
        ]]>
      </handler>
      <handler event="keypress" keycode="vk_page_down">
        <![CDATA[
         if (! this.pageUpOrDownMovesSelection) {
           this.treeBoxObject.scrollByPages(1);
           return;
         }
         var c = this.currentIndex;
         var l = this.treeBoxObject.view.rowCount - 1;
         if (c == l)
           return;
         this.selectionHead = -1;
         this.selectionTail = -1;
         var f = this.treeBoxObject.getFirstVisibleRow();
         var p = this.treeBoxObject.getPageCount();
         var i = l;
         var lastTopRowIndex = l - p;
         if (f <= lastTopRowIndex) {
           if (f + p <= lastTopRowIndex)
             i = c + p;
           else
             i = lastTopRowIndex + c - f + 1;
           this.treeBoxObject.scrollByPages(1);
         }
         this.treeBoxObject.selection.timedSelect(i, this._selectDelay);
        ]]>
      </handler>
      <handler event="keypress" keycode="vk_page_up" modifiers="shift">
        <![CDATA[
         if (this.treeBoxObject.selection.single)
           return;
         var c = this.currentIndex;
         if (c == 0)
           return;
         var f = this.treeBoxObject.getFirstVisibleRow();
         var i = 0;
         if (f > 0) {
           var p = this.treeBoxObject.getPageCount();
           if (f - p >= 0)
             i = c - p;
           else
             i = c - f;
           this.treeBoxObject.scrollByPages(-1);
         }
         if (c == this.selectionTail) {
           if (this.selectionHead < this.selectionTail) {
             if (i < this.selectionHead) {
               this.treeBoxObject.selection.clearRange(c, this.selectionHead + 1);
               this.treeBoxObject.selection.rangedSelect(this.selectionHead - 1, i, true);
             }
             else {
               this.treeBoxObject.selection.clearRange(c, i + 1);
               this.currentIndex = i;
             }
           }
           else
             this.treeBoxObject.selection.rangedSelect(c - 1, i, true);
         }
         else {
           this.treeBoxObject.selection.clearSelection();
           this.selectionHead = c;
           this.treeBoxObject.selection.rangedSelect(c, i, true);
         }
         this.selectionTail = i;
        ]]>
      </handler>
      <handler event="keypress" keycode="vk_page_down" modifiers="shift">
        <![CDATA[
         if (this.treeBoxObject.selection.single)
           return;
         var c = this.currentIndex;
         var l = this.treeBoxObject.view.rowCount - 1;
         if (c == l)
           return;
         var f = this.treeBoxObject.getFirstVisibleRow();
         var p = this.treeBoxObject.getPageCount();
         var i = l;
         var lastTopRowIndex = l - p;
         if (f <= lastTopRowIndex) {
           if (f + p <= lastTopRowIndex)
             i = c + p;
           else
             i = lastTopRowIndex + c - f + 1;
           this.treeBoxObject.scrollByPages(1);
         }
         if (c == this.selectionTail) {
           if (this.selectionHead > this.selectionTail) {
             if (i > this.selectionHead) {
               this.treeBoxObject.selection.clearRange(c, this.selectionHead - 1);
               this.treeBoxObject.selection.rangedSelect(this.selectionHead + 1, i, true);
             }
             else {
               this.treeBoxObject.selection.clearRange(c, i - 1);
               this.currentIndex = i;
             }
           }
           else
             this.treeBoxObject.selection.rangedSelect(c + 1, i, true);
         }
         else {
           this.treeBoxObject.selection.clearSelection();
           this.selectionHead = c;
           this.treeBoxObject.selection.rangedSelect(c, i, true);
         }
         this.selectionTail = i;
        ]]>
      </handler>
      <handler event="keypress" keycode="vk_page_up" modifiers="control">
        <![CDATA[
         var c = this.currentIndex;
         if (c == 0)
           return;
         var f = this.treeBoxObject.getFirstVisibleRow();
         var i = 0;
         if (f > 0) {
           var p = this.treeBoxObject.getPageCount();
           if (f - p >= 0)
             i = c - p;
           else
             i = c - f;
           this.treeBoxObject.scrollByPages(-1);
         }
         this.currentIndex = i;
        ]]>
      </handler>
      <handler event="keypress" keycode="vk_page_down" modifiers="control">
        <![CDATA[
         var c = this.currentIndex;
         var l = this.treeBoxObject.view.rowCount - 1;
         if (c == l)
           return;
         var f = this.treeBoxObject.getFirstVisibleRow();
         var p = this.treeBoxObject.getPageCount();
         var i = l;
         var lastTopRowIndex = l - p;
         if (f <= lastTopRowIndex) {
           if (f + p <= lastTopRowIndex)
             i = c + p;
           else
             i = lastTopRowIndex + c - f + 1;
           this.treeBoxObject.scrollByPages(1);
         }
         this.currentIndex = i;
        ]]>
      </handler>
      <handler event="keypress" keycode="vk_home">
        <![CDATA[
         if (this.currentIndex == 0)
           return;
         this.selectionHead = -1;
         this.selectionTail = -1;
         this.treeBoxObject.selection.select(0);
         this.treeBoxObject.ensureRowIsVisible(0);
        ]]>
      </handler>
      <handler event="keypress" keycode="vk_end">
        <![CDATA[
         var l = this.treeBoxObject.view.rowCount - 1;
         if (this.currentIndex == l)
           return;
         this.selectionHead = -1;
         this.selectionTail = -1;
         this.treeBoxObject.selection.select(l);
         this.treeBoxObject.ensureRowIsVisible(l);
        ]]>
      </handler>
      <handler event="keypress" keycode="vk_home" modifiers="shift">
        <![CDATA[
         if (this.treeBoxObject.selection.single)
           return;
         var c = this.currentIndex;
         if (c == 0)
           return;
         if (c != this.selectionTail) {
           this.treeBoxObject.selection.clearSelection();
           this.selectionHead = c;
         }
         this.treeBoxObject.selection.rangedSelect(c, 0, true);
         this.selectionTail = 0;
         this.treeBoxObject.ensureRowIsVisible(0);
        ]]>
      </handler>
      <handler event="keypress" keycode="vk_end" modifiers="shift">
        <![CDATA[
         if (this.treeBoxObject.selection.single)
           return;
         var c = this.currentIndex;
         var l = this.treeBoxObject.view.rowCount - 1;
         if (c == l)
           return;
         if (c != this.selectionTail) {
           this.treeBoxObject.selection.clearSelection();
           this.selectionHead = c;
         }
         this.treeBoxObject.selection.rangedSelect(c, l, true);
         this.selectionTail = l;
         this.treeBoxObject.ensureRowIsVisible(l);
        ]]>
      </handler>
      <handler event="keypress" keycode="vk_home" modifiers="control">
        <![CDATA[
         if (this.currentIndex == 0)
           return;
         this.currentIndex = 0;
         this.treeBoxObject.ensureRowIsVisible(0);
        ]]>
      </handler>
      <handler event="keypress" keycode="vk_end" modifiers="control">
        <![CDATA[
         var l = this.treeBoxObject.view.rowCount - 1;
         if (this.currentIndex == l)
           return;
         this.currentIndex = l;
         this.treeBoxObject.ensureRowIsVisible(l);
        ]]>
      </handler>
      <handler event="keypress">
        <![CDATA[
         var c = this.currentIndex;
         if (event.keyCode == ' '.charCodeAt(0)) {
           if (!this.treeBoxObject.selection.isSelected(c))
             this.treeBoxObject.selection.toggleSelect(c);
         }
         else if (!this.disableKeyNavigation && event.charCode > 0 &&
                  !event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey) {
           var key = String.fromCharCode(event.charCode);
           key = key.toLowerCase();
           if (event.timeStamp - this._lastKeyTime > 1000)
             this._incrementalString = key;
           else
             this._incrementalString += key;
           this._lastKeyTime = event.timeStamp;
           
           var length = this._incrementalString.length;
           var incrementalString = this._incrementalString;
           var charIndex = 1;
           while (charIndex < length && incrementalString[charIndex] == incrementalString[charIndex - 1])
             charIndex++;
           // If all letters in incremental string are same, just try to match the first one
           if (charIndex == length) {
             length = 1;
             incrementalString = incrementalString.substring(0, length);
           }

           var primarycol = this.treeBoxObject.getKeyColumnIndex();
           var colID = this.treeBoxObject.getColumnID(primarycol);
           var rowCount = this.treeBoxObject.view.rowCount;
           var start = 1;
           
           if (length > 1) {
             start = 0;
             if (c < 0)
               c = 0;
           }

           for (var i = 0; i < rowCount; i++) {
             var l = (i + start + c) % rowCount;
             var cellText = this.treeBoxObject.view.getCellText(l, colID);
             cellText = cellText.substring(0, length).toLowerCase();
             if (cellText == incrementalString) {
               this.selectionHead = -1;
               this.selectionTail = -1;
               this.treeBoxObject.selection.timedSelect(l, this._selectDelay);
               this.treeBoxObject.ensureRowIsVisible(l);
               break;
             }
           }
          }
         ]]>
      </handler>
    </handlers>    
  </binding>

  <binding id="treecols" extends="chrome://global/content/bindings/tree.xml#tree-base">
    <content>
      <children includes="treecol|splitter"/>
      <xul:treecolpicker class="treecol-image" fixed="true" ordinal="2147483647" xbl:inherits="tooltiptext=pickertooltiptext"/>
    </content>
    <implementation implements="nsIAccessibleProvider">
      <property name="accessible">
        <getter>
          <![CDATA[
            var accService = Components.classes["@mozilla.org/accessibilityService;1"].getService(Components.interfaces.nsIAccessibilityService);
            return accService.createXULTreeColumnsAccessible(this);
          ]]>
        </getter>
      </property>
    </implementation>
  </binding>

  <binding id="treerows" extends="chrome://global/content/bindings/tree.xml#tree-base">
    <content>
      <xul:hbox flex="1" class="tree-bodybox">
        <children/>
      </xul:hbox>
      <xul:scrollbar height="0" minwidth="0" minheight="0" orient="vertical" class="tree-scrollbar" collapsed="true"/>
    </content>
    <handlers>
      <handler event="underflow">
        <![CDATA[
          document.getAnonymousNodes(this)[1].collapsed = true;
          event.preventBubble();
        ]]>
      </handler>
      <handler event="overflow">
        <![CDATA[
          document.getAnonymousNodes(this)[1].collapsed = false;
          event.preventBubble();
        ]]>
      </handler>
    </handlers>
  </binding>

  <binding id="treebody" extends="chrome://global/content/bindings/tree.xml#tree-base">
    <implementation>
      <constructor>
        if ("_ensureColumnOrder" in this.parentNode)
          this.parentNode._ensureColumnOrder();
      </constructor>

      <field name="_lastSelectedRow">
        -1
      </field>
    </implementation>
    <handlers>
      <!-- If there is no modifier key, we select on mousedown, not
           click, so that drags work correctly. -->
      <handler event="mousedown">
      <![CDATA[
         if (((!event.ctrlKey || !this.parentNode.pageUpOrDownMovesSelection) &&
             !event.shiftKey && !event.metaKey) ||
             this.parentNode.treeBoxObject.selection.single) {
           var row = {};
           var col = {};
           var obj = {};
           var b = this.parentNode.treeBoxObject;
           b.getCellAt(event.clientX, event.clientY, row, col, obj);

           // save off the last selected row
           this._lastSelectedRow = row.value;

           if (row.value == -1)
             return;

           if (col.value && obj.value != "twisty") {
             var column = document.getElementById(col.value);
             var cycler = column.hasAttribute('cycler');

             if (cycler)
               b.view.cycleCell(row.value, col.value);
             else
    	         if (!b.selection.isSelected(row.value)) {
                 b.selection.select(row.value);
                 b.ensureRowIsVisible(row.value);
               }
           }
         }
      ]]>
      </handler>

      <!-- On a click (up+down on the same item), deselect everything
           except this item. -->
      <handler event="click">
      <![CDATA[
        if (event.button != 0) return;
        var row = {};
        var col = {};
        var obj = {};
        var b = this.parentNode.treeBoxObject;
        b.getCellAt(event.clientX, event.clientY, row, col, obj);

        if (row.value == -1)
          return;

        if (obj.value == "twisty") {
          if (b.selection.currentIndex >= 0 &&
              b.view.isContainerOpen(row.value)) {
            var parentIndex = b.view.getParentIndex(b.selection.currentIndex);
            while (parentIndex >= 0 && parentIndex != row.value)
              parentIndex = b.view.getParentIndex(parentIndex);
            if (parentIndex == row.value)
              b.selection.select(parentIndex);
          }
          b.view.toggleOpenState(row.value);
          return;
        }

        if (! this.parentNode.treeBoxObject.selection.single) {
          var augment = event.ctrlKey || event.metaKey;
          if (event.shiftKey) {
            b.selection.rangedSelect(-1, row.value, augment);
            b.ensureRowIsVisible(row.value);
            return;
          }
          if (augment) {
            b.selection.toggleSelect(row.value);
            b.ensureRowIsVisible(row.value);
            b.selection.currentIndex = row.value;
            return;
          }
        }

        /* We want to deselect all the selected items except what was
          clicked, UNLESS it was a right-click.  We have to do this
          in click rather than mousedown so that you can drag a
          selected group of items */

        if (!col.value) return;
        var column = document.getElementById(col.value);
        var cycler = column.hasAttribute('cycler');

        // if the last row has changed in between the time we 
        // mousedown and the time we click, don't fire the select handler.
        // see bug #92366
        if (!cycler && this._lastSelectedRow == row.value) {
          b.selection.select(row.value);  
          b.ensureRowIsVisible(row.value);
        }
      ]]>
      </handler>

      <!-- double-click -->
      <handler event="click" clickcount="2">
      <![CDATA[
           var row = {};
           var col = {};
		   var obj = {};
           var b = this.parentNode.treeBoxObject;
           b.getCellAt(event.clientX, event.clientY, row, col, obj);

           if (row.value == -1)
             return;

		   var column = document.getElementById(col.value);
		   var cycler = column.hasAttribute('cycler');

		   if (!cycler && obj.value != "twisty" && b.view.isContainer(row.value))
             b.view.toggleOpenState(row.value);
      ]]>
      </handler>
      
    </handlers>
  </binding>

  <binding id="treecol-base" extends="chrome://global/content/bindings/tree.xml#tree-base">
    <implementation>
      <constructor>
        this.parentNode.parentNode._columnsDirty = true;
      </constructor>

      <property name="ordinal">
        <getter><![CDATA[
          var val = this.getAttribute("ordinal");
          return val == "" ? 1 : (val == "0" ? 0 : parseInt(val));
        ]]></getter>
        <setter><![CDATA[
          this.setAttribute("ordinal", val);
        ]]></setter>
      </property>
      
      <property name="_previousVisibleColumn">
        <getter><![CDATA[
          var sib = this.boxObject.previousSibling;
          while (sib) {
            if (sib.localName == "treecol" && sib.boxObject.width > 0 && sib.parentNode == this.parentNode)
              return sib;
            sib = sib.boxObject.previousSibling;
          }
          return null;
        ]]></getter>
      </property>

      <method name="onDragMouseMove">
        <parameter name="aEvent"/>
        <body><![CDATA[
          var col = document.treecolDragging;
          if (!col) return;

          // determine if we have moved the mouse far enough
          // to initiate a drag
          if (col.mDragGesturing) {
            if (Math.abs(aEvent.clientX - col.mStartDragX) < 5 &&
                Math.abs(aEvent.clientY - col.mStartDragY) < 5) {
              return;
            } else {
              col.mDragGesturing = false;
              col.setAttribute("dragging", "true");
              window.addEventListener("click", col.onDragMouseClick, true);
            }
          }
                    
          var pos = {};
          var targetCol = col.parentNode.parentNode._getColumnAtX(aEvent.clientX, 0.5, pos);
          
          // bail if we haven't mousemoved to a different column
          if (col.mTargetCol == targetCol && col.mTargetDir == pos.value)
            return;

          var tree = col.parentNode.parentNode;
          var sib;
          if (col.mTargetCol) {
            // remove previous insertbefore/after attributes
            col.mTargetCol.removeAttribute("insertbefore");
            col.mTargetCol.removeAttribute("insertafter");
            tree.treeBoxObject.invalidateColumn(col.mTargetCol.id);
            sib = col.mTargetCol._previousVisibleColumn;
            if (sib) {
              sib.removeAttribute("insertafter");
              tree.treeBoxObject.invalidateColumn(sib.id);
            }
            col.mTargetCol = null;
            col.mTargetDir = null;
          }
          
          if (targetCol) {
            // set insertbefore/after attributes
            if (pos.value == "after") {
              targetCol.setAttribute("insertafter", "true");
            } else {
              targetCol.setAttribute("insertbefore", "true");
              sib = targetCol._previousVisibleColumn;
              if (sib) {
                sib.setAttribute("insertafter", "true");
                tree.treeBoxObject.invalidateColumn(sib.id);
              }
            }
            tree.treeBoxObject.invalidateColumn(targetCol.id);
            col.mTargetCol = targetCol;
            col.mTargetDir = pos.value;
          }
        ]]></body>        
      </method>

      <method name="onDragMouseUp">
        <parameter name="aEvent"/>
        <body><![CDATA[
          var col = document.treecolDragging;
          if (!col) return;
          
          if (!col.mDragGesturing) {
            if (col.mTargetCol) {
              // remove insertbefore/after attributes
              var before = col.mTargetCol.hasAttribute("insertbefore");
              col.mTargetCol.removeAttribute(before ? "insertbefore" : "insertafter");
              if (before) {
                var sib = col.mTargetCol._previousVisibleColumn;
                if (sib)
                  sib.removeAttribute("insertafter");
              }
                          
              // move the column
              if (col != col.mTargetCol)
                col.parentNode.parentNode._reorderColumn(col, col.mTargetCol, before);
            
              // repaint to remove lines
              col.parentNode.parentNode.treeBoxObject.invalidate();
  
              col.mTargetCol = null;
            }
          } else
            col.mDragGesturing = false;
                    
          document.treecolDragging = null;
          col.removeAttribute("dragging");
          
          window.removeEventListener("mousemove", col.onDragMouseMove, true);
          window.removeEventListener("mouseup", col.onDragMouseUp, true);
          // we have to wait for the click event to fire before removing
          // cancelling handler
          var clickHandler = function(handler) { 
            window.removeEventListener("click", handler, true);
          };
          window.setTimeout(clickHandler, 0, col.onDragMouseClick);
        ]]></body>        
      </method>

      <method name="onDragMouseClick">
        <parameter name="aEvent"/>
        <body><![CDATA[
          // prevent click event from firing after column drag and drop
          aEvent.preventBubble();
          aEvent.preventDefault();
        ]]></body>        
      </method>
    </implementation>
    
    <handlers>
      <handler event="mousedown" button="0"><![CDATA[
        if (this.parentNode.parentNode.enableColumnDrag) {
          var xulns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
          var cols = this.parentNode.getElementsByTagNameNS(xulns, "treecol");
          
          // only start column drag operation if there are at least 2 visible columns
          var visible = 0;
          for (var i = 0; i < cols.length; ++i)
            if (cols[i].boxObject.width > 0) ++visible;
            
          if (visible > 1) {
            window.addEventListener("mousemove", this.onDragMouseMove, false);
            window.addEventListener("mouseup", this.onDragMouseUp, false);
            document.treecolDragging = this;
            this.mDragGesturing = true;
            this.mStartDragX = event.clientX;
            this.mStartDragY = event.clientY;
          }
        }
      ]]></handler>
    </handlers>
  </binding>

  <binding id="treecol" extends="chrome://global/content/bindings/tree.xml#treecol-base">
    <content>
      <xul:label class="treecol-text" xbl:inherits="crop,value=label" flex="1" crop="right"/>
      <xul:image class="treecol-sortdirection" xbl:inherits="sortDirection"/>
    </content>
    <implementation implements="nsIAccessibleProvider">
      <property name="accessible">
        <getter>
          <![CDATA[
            var accService = Components.classes["@mozilla.org/accessibilityService;1"].getService(Components.interfaces.nsIAccessibilityService);
            return accService.createXULTreeColumnitemAccessible(this);
          ]]>
        </getter>
      </property>
    </implementation>

    <handlers>
      <handler event="click" button="0" phase="target" action="this.parentNode.parentNode.treeBoxObject.view.cycleHeader(this.id, this);"/>
    </handlers>
  </binding>

  <binding id="treecol-image" extends="chrome://global/content/bindings/tree.xml#treecol-base">
    <content>
      <xul:image class="treecol-icon" xbl:inherits="src"/>
    </content>
    <handlers>
      <handler event="click" button="0" action="this.parentNode.parentNode.treeBoxObject.view.cycleHeader(this.id, this)"/>
    </handlers>
  </binding>

  <binding id="columnpicker" display="xul:button"
           extends="chrome://global/content/bindings/tree.xml#tree-base">
    <content>
      <xul:image class="tree-columnpicker-icon"/>
      <xul:menupopup anonid="popup"/>
    </content>
    
    <implementation>
      <method name="buildPopup">
        <parameter name="aPopup"/>
        <body>
          <![CDATA[
            // we no longer cache the picker content.
            // remove the old content
            while (aPopup.hasChildNodes())
              aPopup.removeChild(aPopup.lastChild);

            for (var currCol = this.parentNode.firstChild; currCol; currCol = currCol.nextSibling) {
              // Construct an entry for each column in the row, unless
              // it is not being shown
              if (currCol.localName == "treecol" && !currCol.hasAttribute("ignoreincolumnpicker")) {
                var popupChild = document.createElement("menuitem");
                popupChild.setAttribute("type", "checkbox");
                var columnName = currCol.getAttribute("display") || currCol.getAttribute("label");
                popupChild.setAttribute("label", columnName);
                popupChild.setAttribute("colid", currCol.id);
                if (currCol.getAttribute("hidden") != "true")
                  popupChild.setAttribute("checked", "true");
                if (currCol.getAttribute("primary") == "true")
                  popupChild.setAttribute("disabled", "true");
                aPopup.appendChild(popupChild);
              }
            }
          ]]>
        </body>
      </method>
    </implementation>

    <handlers>
      <handler event="command">
        <![CDATA[
          if (event.originalTarget == this) {
            var popup = document.getAnonymousElementByAttribute(this, "anonid", "popup");
            this.buildPopup(popup);
            popup.showPopup(this, -1, -1, "popup", "bottomright", "topright");
          } else {
            var colid = event.originalTarget.getAttribute("colid");
            var colNode = document.getElementById(colid);
            if (colNode) {
              if (colNode.getAttribute("hidden") == "true")
                colNode.setAttribute("hidden", "false");
              else
                colNode.setAttribute("hidden", "true");
            }
          }
        ]]>
      </handler>
    </handlers>
  </binding>
</bindings>

