<?xml version="1.0"?>
<!-- ***** BEGIN LICENSE BLOCK *****
   - Version: MPL 1.1/GPL 2.0/LGPL 2.1
   -
   - The contents of this file are subject to the Mozilla Public License Version
   - 1.1 (the "License"); you may not use this file except in compliance with
   - the License. You may obtain a copy of the License at
   - http://www.mozilla.org/MPL/
   -
   - Software distributed under the License is distributed on an "AS IS" basis,
   - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
   - for the specific language governing rights and limitations under the
   - License.
   -
   - The Original Code is mozilla.org code.
   -
   - The Initial Developer of the Original Code is
   - Netscape Communications Corporation.
   - Portions created by the Initial Developer are Copyright (C) 1998
   - the Initial Developer. All Rights Reserved.
   -
   - Contributor(s):
   -   Ben Goodger <beng@google.com>
   -   Annie Sullivan <annie.sullivan@gmail.com>
   -   Marco Bonardo <mak77@bonardo.net>
   -
   - Alternatively, the contents of this file may be used under the terms of
   - either the GNU General Public License Version 2 or later (the "GPL"), or
   - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
   - in which case the provisions of the GPL or the LGPL are applicable instead
   - of those above. If you wish to allow use of your version of this file only
   - under the terms of either the GPL or the LGPL, and not to allow others to
   - use your version of this file under the terms of the MPL, indicate your
   - decision by deleting the provisions above and replace them with the notice
   - and other provisions required by the LGPL or the GPL. If you do not delete
   - the provisions above, a recipient may use your version of this file under
   - the terms of any one of the MPL, the GPL or the LGPL.
   -
   - ***** END LICENSE BLOCK ***** -->

<bindings id="placesTreeBindings"
          xmlns="http://www.mozilla.org/xbl"
          xmlns:xbl="http://www.mozilla.org/xbl"
          xmlns:html="http://www.w3.org/1999/xhtml"
          xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">

  <binding id="places-tree" extends="chrome://global/content/bindings/tree.xml#tree">
    <implementation>
      <constructor><![CDATA[
        // Force an initial build.
        if (this.place)
          this.place = this.place;
      ]]></constructor>

      <destructor><![CDATA[
        // Break the treeviewer->result->treeviewer cycle.
        // Note: unsetting the result's viewer also unsets
        // the viewer's reference to our treeBoxObject.
        var result = this.getResult();
        if (result)
          result.removeObserver(this.view);
        this.view = null;
      ]]></destructor>

      <property name="controller"
                readonly="true"
                onget="return this._controller;"/>

      <!-- overriding -->
      <property name="view">
        <getter><![CDATA[
          try {
            return this.treeBoxObject.view
                .QueryInterface(Components.interfaces.nsINavHistoryResultTreeViewer);
          }
          catch(e) {
            return null;
          }
        ]]></getter>
        <setter><![CDATA[
          return this.treeBoxObject.view = val;
        ]]></setter>
      </property>

      <property name="sortingMode" readonly="true">
        <getter><![CDATA[
          try {
            // If we have a result then simply return its sorting mode.
            return this.view.QueryInterface(Components.interfaces.nsINavHistoryResultViewer).result.sortingMode;
          }
          catch (e) {
            // Find the sorted column from the persisted attributes.
            const NHQO = Components.interfaces.nsINavHistoryQueryOptions;
            var sortedColumn = this.columns.getSortedColumn();
            if (!sortedColumn)
              return NHQO.SORT_BY_NONE;

            var sortDescending =
              sortedColumn.element.getAttribute("sortDirection") != "ascending";
            switch (sortedColumn.id) {
              case "Name":
                return NHQO.SORT_BY_TITLE_ASCENDING + sortDescending;
              case "URL":
                return NHQO.SORT_BY_URI_ASCENDING + sortDescending;
              case "Date":
                return NHQO.SORT_BY_DATE_ASCENDING + sortDescending;
              case "VisitCount":
                return NHQO.SORT_BY_VISITCOUNT_ASCENDING + sortDescending;
            }
            return NHQO.SORT_BY_NONE;
          }
        ]]></getter>
      </property>

      <method name="applyFilter">
        <parameter name="filterString"/>
        <body><![CDATA[
          // preserve grouping
          var queryNode = asQuery(this.getResultNode());
          var options = queryNode.queryOptions.clone();

          var query = PlacesUtils.history.getNewQuery();
          query.searchTerms = filterString;

          this.load([query], options);
        ]]></body>
      </method>

      <method name="load">
        <parameter name="queries"/>
        <parameter name="options"/>
        <body><![CDATA[
          var result = PlacesUtils.history.executeQueries(queries, queries.length,
                                                          options);

          var treeView = new PlacesTreeView();
          result.addObserver(treeView, false);
          this.view = treeView;
          if (!this._controller) {
            this._controller = new PlacesController(this);
            this.controllers.appendController(this._controller);
          }
        ]]></body>
      </method>

      <!-- nsIPlacesView -->
      <method name="getResult">
        <body><![CDATA[
          try {
            return this.view.QueryInterface(Components.interfaces.nsINavHistoryResultObserver).result;
          }
          catch (e) {
            return null;
          }
        ]]></body>
      </method>

      <!-- nsIPlacesView -->
      <method name="getResultNode">
        <body><![CDATA[
          return this.getResult().root;
        ]]></body>
      </method>

      <!-- nsIPlacesView -->
      <property name="place">
        <getter><![CDATA[
          return this.getAttribute("place");
        ]]></getter>
        <setter><![CDATA[
          this.setAttribute("place", val);

          var queriesRef = { };
          var queryCountRef = { };
          var optionsRef = { };
          PlacesUtils.history.queryStringToQueries(val, queriesRef, queryCountRef, optionsRef);
          if (queryCountRef.value == 0)
            queriesRef.value = [PlacesUtils.history.getNewQuery()];
          if (!optionsRef.value)
            optionsRef.value = PlacesUtils.history.getNewQueryOptions();

          this.load(queriesRef.value, optionsRef.value);

          return val;
        ]]></setter>
      </property>

      <!-- nsIPlacesView -->
      <property name="hasSelection">
        <getter><![CDATA[
          return this.view && this.view.selection.count >= 1;
        ]]></getter>
      </property>

      <!-- nsIPlacesView -->
      <method name="getSelectionNodes">
        <body><![CDATA[
          var nodes = [];
          if (!this.hasSelection)
            return nodes;

          var selection = this.view.selection;
          var rc = selection.getRangeCount();
          var resultview = this.view;
          for (var i = 0; i < rc; ++i) {
            var min = { }, max = { };
            selection.getRangeAt(i, min, max);

            for (var j = min.value; j <= max.value; ++j)
              nodes.push(resultview.nodeForTreeIndex(j));
          }
          return nodes;
        ]]></body>
      </method>

      <!-- nsIPlacesView -->
      <method name="getDragableSelection">
        <body><![CDATA[
          return this.getSelectionNodes();
        ]]></body>
      </method>

      <!-- nsIPlacesView -->
      <property name="selectedNode">
        <getter><![CDATA[
          var view = this.view;
          if (!view || view.selection.count != 1)
            return null;

          var selection = view.selection;
          var min = { }, max = { };
          selection.getRangeAt(0, min, max);

          return this.view.nodeForTreeIndex(min.value);
        ]]></getter>
      </property>

      <!-- nsIPlacesView -->
      <property name="insertionPoint">
        <getter><![CDATA[
          // there is no insertion point for history queries
          // so just bail out
          return null;
        ]]></getter>
      </property>

      <!-- nsIPlacesView -->
      <method name="selectAll">
        <body><![CDATA[
          this.view.selection.selectAll();
        ]]></body>
      </method>

      <!-- nsDragAndDrop -->
      <method name="onDragStart">
        <parameter name="aEvent"/>
        <parameter name="aXferData"/>
        <parameter name="aDragAction"/>
        <body><![CDATA[
          var nodes = this.getSelectionNodes();
          for (var i = 0; i < nodes.length; ++i) {
            var node = nodes[i];

            // Disallow dragging the root node of a tree
            var parent = node.parent;
            if (!parent)
              return;

            // History nodes can always be dragged off
          }

          // Stuff the encoded selection into the transferable data object
          this._controller.setDataTransfer(aEvent);
        ]]></body>
      </method>

      <!-- nsDragAndDrop -->
      <method name="canDrop">
        <parameter name="aEvent"/>
        <parameter name="aDragSession"/>
        <body><![CDATA[
          // compat to general places tree but you never can drop on history
          return false
        ]]></body>
      </method>

      <!-- nsDragAndDrop -->
      <method name="onDragOver">
        <parameter name="aEvent"/>
        <parameter name="aFlavour"/>
        <parameter name="aDragSession"/>
        <body><![CDATA[
          if (!this.canDrop(aEvent, aDragSession))
            aEvent.dataTransfer.effectAllowed = "none";
        ]]></body>
      </method>

      <!-- nsDragAndDrop -->
      <method name="getSupportedFlavours">
        <body><![CDATA[
          // nothing can be dropped on history, actually.
          return [];
        ]]></body>
      </method>

      <method name="buildContextMenu">
        <parameter name="aPopup"/>
        <body><![CDATA[
          return this.controller.buildContextMenu(aPopup);
        ]]></body>
      </method>

      <method name="destroyContextMenu">
        <parameter name="aPopup"/>
        <body/>
      </method>
    </implementation>
    <handlers>
      <handler event="focus"><![CDATA[
        // See select handler. We need the sidebar's places commandset to be
        // updated as well
        document.commandDispatcher.updateCommands("focus");
      ]]></handler>
      <handler event="select"><![CDATA[
        // This additional complexity is here for the sidebars
        var win = window;
        while (true) {
          win.document.commandDispatcher.updateCommands("focus");
          if (win == window.top)
            break;

          win = win.parent;
        }
      ]]></handler>
      <handler event="draggesture"><![CDATA[
        if (event.target.localName == "treechildren")
          nsDragAndDrop.startDrag(event, this);
      ]]></handler>
      <handler event="dragover"><![CDATA[
        if (event.target.localName == "treechildren")
          nsDragAndDrop.dragOver(event, this);
      ]]></handler>
    </handlers>
  </binding>

</bindings>
