/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape 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/NPL/
 *
 * 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 Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation. All
 * Rights Reserved.
 *
 * Contributor(s): 
 *   Ben Goodger <ben@netscape.com> (Original Author)
 */

var gBookmarksShell = null;

///////////////////////////////////////////////////////////////////////////////
// Class which defines methods for a bookmarks UI implementation based around
// a toolbar. Subclasses BookmarksBase in bookmarksOverlay.js. Some methods
// are required by the base class, others are for event handling. Window specific
// glue code should go into the BookmarksWindow class in bookmarks.js
function BookmarksToolbar (aID)
{
  this.id = aID;
}

BookmarksToolbar.prototype = {
  __proto__: BookmarksUIElement.prototype,

  /////////////////////////////////////////////////////////////////////////////
  // Personal Toolbar Specific Stuff
  
  openNewWindow: false,


  get db ()
  {
    return this.element.database;
  },

  get element ()
  {
    return document.getElementById(this.id);
  },

  /////////////////////////////////////////////////////////////////////////////
  // This method constructs a menuitem for a context menu for the given command.
  // This is implemented by the client so that it can intercept menuitem naming
  // as appropriate.
  createMenuItem: function (aDisplayName, aCommandName, aItemNode)
  {
    const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
    var xulElement = document.createElementNS(kXULNS, "menuitem");
    xulElement.setAttribute("cmd", aCommandName);
    xulElement.setAttribute("command", "cmd_" + aCommandName.substring(NC_NS_CMD.length));

    switch (aCommandName) {
    case NC_NS_CMD + "open":
      xulElement.setAttribute("label", aDisplayName);
      xulElement.setAttribute("default", "true");
      break;
    case NC_NS_CMD + "openfolder":
      xulElement.setAttribute("default", "true");
      if (aItemNode.localName == "hbox") 
        // Don't show an "Open Folder" item for clicks on the toolbar itself.
        return null;
    default:
      xulElement.setAttribute("label", aDisplayName);
      break;
    }
    return xulElement;
  },

  // Command implementation
  commands: {
    openFolder: function (aSelectedItem)
    {
      var mbo = aSelectedItem.boxObject.QueryInterface(Components.interfaces.nsIMenuBoxObject);
      mbo.openMenu(true);
    },

    editCell: function (aSelectedItem, aXXXLameAssIndex)
    {
      if (aSelectedItem.getAttribute("editable") != "true")
        return;
      var property = "http://home.netscape.com/NC-rdf#Name";
      aSelectedItem.setMode("edit");
      aSelectedItem.addObserver(this.postModifyCallback, "accept", 
                                [gBookmarksShell, aSelectedItem, property]);
    },

    ///////////////////////////////////////////////////////////////////////////
    // Called after an inline-edit cell has left inline-edit mode, and data
    // needs to be modified in the datasource.
    postModifyCallback: function (aParams)
    {
      var aShell = aParams[0];
      var selItemURI = NODE_ID(aParams[1]);
      aShell.propertySet(selItemURI, aParams[2], aParams[3]);
    },

    ///////////////////////////////////////////////////////////////////////////
    // Creates a dummy item that can be placed in edit mode to retrieve data
    // to create new bookmarks/folders.
    createBookmarkItem: function (aMode, aSelectedItem)
    {
      const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
      var dummyButton = document.createElementNS(kXULNS, "menubutton");
      dummyButton = gBookmarksShell.createBookmarkFolderDecorations(dummyButton);
      dummyButton.setAttribute("class", "button-toolbar bookmark-item");

      dummyButton.setAttribute("label", gBookmarksShell.getLocaleString("ile_newfolder") + "  ");
      // By default, create adjacent to the selected button. If there is no button after
      // the selected button, or the target is the toolbar itself, just append. 
      var bIsButton = aSelectedItem.localName == "button" || aSelectedItem.localName == "menubutton";
      if (aSelectedItem.nextSibling && bIsButton)
        aSelectedItem.parentNode.insertBefore(dummyButton, aSelectedItem.nextSibling);
      else
        (bIsButton ? aSelectedItem.parentNode : aSelectedItem).appendChild(dummyButton);

      gBookmarksShell._focusElt = document.commandDispatcher.focusedElement;
      dummyButton.setMode("edit");
      // |aSelectedItem| will be the node we create the new folder relative to. 
      dummyButton.addObserver(this.onEditFolderName, "accept", 
                              [dummyButton, aSelectedItem, dummyButton]);
      dummyButton.addObserver(this.onEditFolderName, "reject", 
                              [dummyButton, aSelectedItem, dummyButton]);
    },

    ///////////////////////////////////////////////////////////////////////////
    // Edit folder name & update the datasource if name is valid
    onEditFolderName: function (aParams, aTopic)
    {
      // Because the toolbar has no concept of selection, this function
      // is much simpler than the one in bookmarksTree.js. However it may
      // become more complex if pink ever lets me put context menus on menus ;) 
      var name = aParams[3];
      var dummyButton = aParams[2];
      var relativeNode = aParams[1];
      var parentNode = gBookmarksShell.findRDFNode(relativeNode, false);

      dummyButton.parentNode.removeChild(dummyButton);

      if (!gBookmarksShell.commands.validateNameAndTopic(name, aTopic, relativeNode, dummyButton))
        return;

      var parentNode = relativeNode.parentNode;
      if (relativeNode.localName == "hbox") {
        parentNode = relativeNode;
        relativeNode = relativeNode.lastChild;
      }

      var args = [{ property: NC_NS + "parent",
                    resource: NODE_ID(parentNode) },
                  { property: NC_NS + "Name",
                    literal:  name }];

      BookmarksUtils.doBookmarksCommand(NODE_ID(relativeNode),
                                        NC_NS_CMD + "newfolder", args);
      // We need to do this because somehow focus shifts and no commands 
      // operate any more. 
      gBookmarksShell._focusElt.focus();
    },

    ///////////////////////////////////////////////////////////////////////////
    // Performs simple validation on what the user has entered:
    //  1) prevents entering an empty string
    //  2) in the case of a canceled operation, remove the dummy item and
    //     restore selection.
    validateNameAndTopic: function (aName, aTopic, aOldSelectedItem, aDummyItem)
    {
      // Don't allow user to enter an empty string "";
      if (!aName) return false;

      // If the user hit escape, go no further.
      return !(aTopic == "reject");
    }
  },

  _focusElt: null,

  /////////////////////////////////////////////////////////////////////////////
  // Evaluates an event to determine whether or not it affords opening a tree
  // item. Typically, this is when the left mouse button is used, and provided
  // the click-rate matches that specified by our owning tree class. For example,
  // some trees open an item when double clicked (bookmarks/history windows) and
  // others on a single click (sidebar panels).
  isValidOpenEvent: function (aEvent)
  {
    return !(aEvent.type == "click" &&
             (aEvent.button != 0 || aEvent.detail != this.openClickCount))
  },

  /////////////////////////////////////////////////////////////////////////////
  // For the given selection, selects the best adjacent element. This method is
  // useful when an action such as a cut or a deletion is performed on a
  // selection, and focus/selection needs to be restored after the operation
  // is performed.
  getNextElement: function (aElement)
  {
    if (aElement.nextSibling)
      return aElement.nextSibling;
    else if (aElement.previousSibling)
      return aElement.previousSibling;
    else
      return aElement.parentNode;
  },

  selectElement: function (aElement)
  {
  },

  //////////////////////////////////////////////////////////////////////////////
  // Add the treeitem element specified by aURI to the tree's current selection.
  addItemToSelection: function (aURI)
  {
  },

  /////////////////////////////////////////////////////////////////////////////
  // Return a set of DOM nodes that represents the current item in the Bookmarks
  // Toolbar. This is always |document.popupNode|.
  getSelection: function ()
  {
    return [document.popupNode];
  },

  getBestItem: function ()
  {
    var seln = this.getSelection ();
    if (seln.length < 1) {
      var kids = ContentUtils.childByLocalName(this.tree, "treechildren");
      if (kids) return kids.lastChild;
    }
    else
      return seln[0];
    return null;
  },

  /////////////////////////////////////////////////////////////////////////////
  // Return a set of DOM nodes that represent the selection in the tree widget.
  // This method is takes a node parameter which is the popupNode for the
  // document. If the popupNode is not contained by the selection, the
  // popupNode is selected and the new selection returned.
  getContextSelection: function (aItemNode)
  {
    return [aItemNode];
  },

  getSelectedFolder: function ()
  {
    return "NC:PersonalToolbarFolder";
  },

  /////////////////////////////////////////////////////////////////////////////
  // For a given start DOM element, find the enclosing DOM element that contains
  // the template builder RDF resource decorations (id, ref, etc). In the 
  // Toolbar case, this is always the popup node (until we're proven wrong ;)
  findRDFNode: function (aStartNode, aIncludeStartNodeFlag)
  {
    var temp = aStartNode;
    while (temp && temp.localName != (aIncludeStartNodeFlag ? "button"     : "hbox") 
                && temp.localName != (aIncludeStartNodeFlag ? "menubutton" : "hbox"))
      temp = temp.parentNode;
    return temp || this.element;
  },

  selectFolderItem: function (aFolderURI, aItemURI, aAdditiveFlag)
  {
    var folder = document.getElementById(aFolderURI);
    var kids = ContentUtils.childByLocalName(folder, "treechildren");
    if (!kids) return;

    var item = kids.firstChild;
    while (item) {
      if (item.id == aItemURI) break;
      item = item.nextSibling;
    }
    if (!item) return;

    this.tree[aAdditiveFlag ? "addItemToSelection" : "selectItem"](item);
  },

  /////////////////////////////////////////////////////////////////////////////
  // Command handling & Updating.
  controller: {
    supportsCommand: function (aCommand)
    {
      switch(aCommand) {
      case "cmd_undo":
      case "cmd_redo":
        return false;
      case "cmd_bm_cut":
      case "cmd_bm_copy":
      case "cmd_bm_paste":
      case "cmd_bm_delete":
      case "cmd_bm_selectAll":
        return true;
      case "cmd_open":
      case "cmd_openfolder":
      case "cmd_openfolderinnewwindow":
      case "cmd_newbookmark":
      case "cmd_newfolder":
      case "cmd_newseparator":
      case "cmd_find":
      case "cmd_properties":
      case "cmd_rename":
      case "cmd_setnewbookmarkfolder":
      case "cmd_setpersonaltoolbarfolder":
      case "cmd_setnewsearchfolder":
      case "cmd_import":
      case "cmd_export":
        return true;
      default:
        return false;
      }
    },

    isCommandEnabled: function (aCommand)
    {
      switch(aCommand) {
      case "cmd_undo":
      case "cmd_redo":
        return false;
      case "cmd_bm_paste":
        var cp = gBookmarksShell.canPaste();
        return cp;
      case "cmd_bm_cut":
      case "cmd_bm_copy":
      case "cmd_bm_delete":
        return document.popupNode != null;
      case "cmd_bm_selectAll":
        return false;
      case "cmd_open":
        var seln = gBookmarksShell.getSelection();
        return document.popupNode != null && seln[0].getAttribute("type") == NC_NS + "Bookmark";
      case "cmd_openfolder":
      case "cmd_openfolderinnewwindow":
        seln = gBookmarksShell.getSelection();
        return document.popupNode != null && seln[0].getAttribute("type") == NC_NS + "Folder";
      case "cmd_find":
      case "cmd_newbookmark":
      case "cmd_newfolder":
      case "cmd_newseparator":
      case "cmd_import":
      case "cmd_export":
        return true;
      case "cmd_properties":
      case "cmd_rename":
        return document.popupNode != null;
      case "cmd_setnewbookmarkfolder":
        seln = gBookmarksShell.getSelection();
        if (!seln.length) return false;
        var folderType = seln[0].getAttribute("type") == (NC_NS + "Folder");
        return document.popupNode != null && !(NODE_ID(seln[0]) == "NC:NewBookmarkFolder") && folderType;
      case "cmd_setpersonaltoolbarfolder":
        seln = gBookmarksShell.getSelection();
        if (!seln.length) return false;
        folderType = seln[0].getAttribute("type") == (NC_NS + "Folder");
        return document.popupNode != null && !(NODE_ID(seln[0]) == "NC:PersonalToolbarFolder") && folderType;
      case "cmd_setnewsearchfolder":
        seln = gBookmarksShell.getSelection();
        if (!seln.length) return false;
        folderType = seln[0].getAttribute("type") == (NC_NS + "Folder");
        return document.popupNode != null && !(NODE_ID(seln[0]) == "NC:NewSearchFolder") && folderType;
      default:
        return false;
      }
    },

    doCommand: function (aCommand)
    {
      switch(aCommand) {
      case "cmd_undo":
      case "cmd_redo":
        break;
      case "cmd_bm_paste":
      case "cmd_bm_copy":
      case "cmd_bm_cut":
      case "cmd_bm_delete":
      case "cmd_newbookmark":
      case "cmd_newfolder":
      case "cmd_newseparator":
      case "cmd_properties":
      case "cmd_rename":
      case "cmd_open":
      case "cmd_openfolder":
      case "cmd_openfolderinnewwindow":
      case "cmd_setnewbookmarkfolder":
      case "cmd_setpersonaltoolbarfolder":
      case "cmd_setnewsearchfolder":
      case "cmd_find":
      case "cmd_import":
      case "cmd_export":
        gBookmarksShell.execCommand(aCommand.substring("cmd_".length));
        break;
      case "cmd_bm_selectAll":
        break;
      }
    },

    onEvent: function (aEvent)
    {
    },

    onCommandUpdate: function ()
    {
    }
  }
};

function BM_navigatorLoad(aEvent)
{
  if (!gBookmarksShell) {
    gBookmarksShell = new BookmarksToolbar("innermostBox");
    controllers.appendController(gBookmarksShell.controller);
  }
}

addEventListener("load", BM_navigatorLoad, false);

