<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
   - License, v. 2.0. If a copy of the MPL was not distributed with this
   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->

<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.root.containerOpen = false;
        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.nsITreeView)
                .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[
          var result = this.getResult();
          if (result)
            // If we have a result then simply return its sorting mode.
            return result.sortingMode;
          else {
            // 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();

          // Observer removal is done within the view itself. When the tree
          // goes away, treeboxobject calls view.setTree(null), which then
          // calls removeObserver.
          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>

      <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="dragstart"><![CDATA[
        if (event.target.localName != "treechildren")
          return;

        var nodes = this.getSelectionNodes();
        for (var i = 0; i < nodes.length; ++i) {
          // Disallow dragging the root node of a tree.
          if (!nodes[i].parent) {
            event.preventDefault();
            event.stopPropagation();
            return;
          }

          // History nodes can always be dragged off.
        }

        // Stuff the encoded selection into the transferable data object.
        this._controller.setDataTransfer(event);
        event.stopPropagation();
      ]]></handler>
    </handlers>
  </binding>

</bindings>
