View Controller

Draft
This page is not complete.

Controllers are one component of the Places model-view-controller design.  They are responsible for enabling, disabling and performing commands based on the state of a Places view.

Although Places provides its own built-in controller, controllers of different purposes are used throughout an application and are not specific to Places.  See the Commands and Updating Commands pages in the XUL tutorial for general information about them.

The built-in controller

Places provides a built-in controller, PlacesController, a prototype defined in browser/components/places/content/controller.js.  The built-in Places views automatically use PlacesController, so if you use a built-in view, you need not write your own controller, worry about hooking up your view to a controller, or really read the rest of this page.  You do, however, need to include PlacesController's source in your XUL document.  The recommended way of doing so is by overlaying browser/components/places/content/placesOverlay.xul.  This file also contains the built-in Places context menu and XUL commandset.  See Displaying Places information using views for more information.

If you have a custom view that supports all the commands that PlacesController supports and only those commands, you can also use PlacesController.  If your view supports only a subset of those commands, you may still be able to take advantage of PlacesController; see Creating custom controllers below.

Places commands

Listed below are the commands that PlacesController supports along with their descriptions.

Many of the commands operate on the nodes that are currently selected in the controller's view.  Unless noted otherwise, the selected node is found by reading the selectedNode property of the view's nsIPlacesView interface.  Because the selectedNode property assumes that the view is single-selection, commands that rely on selectedNode also also assume that the view is single-selection and will not work if the view's selection contains multiple nodes.

Some commands insert new nodes into the results of the controller's view.  The position in the results at which nodes are inserted is determined by reading the insertionPoint property of the view's nsIPlacesView interface. 

placesCmd_deleteDataHost
Removes all data stored for the selected node's domain by calling nsIPrivateBrowsingService.removeDataFromDomain().
placesCmd_moveBookmarks
Shows the UI used to move the selected nodes to another folder.  The note above about single selection does not apply here; this command calls nsIPlacesView.getSelectionNodes() on the view.
placesCmd_new:bookmark
Shows the UI used to add a new bookmark by calling PlacesUIUtils.showAddBookmarkUI().  The new bookmark is inserted at the view's insertion point.  If no insertion point exists, NS_ERROR_NOT_AVAILABLE is thrown.
placesCmd_new:folder
Shows the UI used to add a new folder by calling PlacesUIUtils.showAddFolderUI().  The new folder is inserted at the view's insertion point.  If no insertion point exists, NS_ERROR_NOT_AVAILABLE is thrown.
placesCmd_new:livemark
Shows the UI used to add a new livemark by calling PlacesUIUtils.showAddLivemarkUI().  The new livemark is inserted at the view's insertion point.  If no insertion point exists, NS_ERROR_NOT_AVAILABLE is thrown.
placesCmd_new:separator
Adds a new separator at the view's current insertion point.  If no insertion point exists, NS_ERROR_NOT_AVAILABLE is thrown.
placesCmd_open
Opens the view's selected node in the current tab by calling PlacesUIUtils.openNodeIn().
placesCmd_open:tab
Opens the view's selected node in a new tab by calling PlacesUIUtils.openNodeIn().
placesCmd_open:window
Opens the view's selected node in a new window by calling PlacesUIUtils.openNodeIn().
placesCmd_reload
Reloads the view's selected node if it is a livemark by calling nsILivemarkService.reloadLivemarkFolder().
placesCmd_reloadMicrosummary
Reloads the view's selected node if it has a microsummary by calling nsIMicrosummaryService.refreshMicrosummary().
placesCmd_show:info
Shows the UI used to edit the properties of the view's selected node by calling PlacesUIUtils.showItemProperties().
placesCmd_sortBy:name
Sorts the contents of the view's selected node if it is a folder by calling nsIPlacesTransactionsService.sortFolderByName().

PlacesController also supports the standard Edit commands:

cmd_copy
Copies the view's selected nodes to the clipboard.  The note above about single selection does not apply here; this command calls nsIPlacesView.getSelectionNodes() on the view.
cmd_cut
Copies the view's selected nodes to the clipboard and removes them.  The implementation of this command is simply a copy followed by a delete.
cmd_delete
Removes the view's selected nodes.  The note above about single selection does not apply here; this command calls nsIPlacesView.getRemovableSelectionNodes() on the view.
cmd_paste
Adds the nodes in the clipboard to the view's results at the current insertion point.  If no insertion point exists, NS_ERROR_NOT_AVAILABLE is thrown.
cmd_redo
Redoes the last Places transaction by calling nsITransactionManager.redoTransaction() on the nsIPlacesTransactionsService instance kept by PlacesUIUtils.
cmd_selectAll
Selects all the nodes in the view by calling nsIPlacesView.selectAll() on the view.
cmd_undo
Undoes the last Places transaction by calling nsITransactionManager.undoTransaction() on the nsIPlacesTransactionsService instance kept by PlacesUIUtils.

Creating custom controllers

You will need to write your own controller if you want to use commands that PlacesController does not support or if you want to handle commands in a way that PlacesController does not.  Otherwise, even if you are writing a custom view, you should save yourself the work and use PlacesController as is.  If you are not familiar with controllers, see the Commands and Updating Commands pages in the XUL tutorial.

If you are writing a controller for a built-in view, you can take advantage of the fact that the view automatically hooks itself up to its own default controller, an instance of PlacesController.  Your custom view need only support the commands that PlacesController either does not support or supports in ways you wish to override.  All other commands will be passed to the view's default controller.  To ensure that your controller overrides the default, it must precede the default in the view's list of controllers.  (If you are not sure why, see the tutorial links given in the previous paragraph.)  The view simply appends its default controller to the end of the list, so you should insert yours by calling the nsIControllers.insertControllerAt() method with index 0.

The following example creates a controller that supports two commands: placesCmd_open, which will override the handling of that command by the view's default controller, and aCommandOfMyOwn, a custom command.  All other commands will be handled by the view's default controller:

var controller = {
  doCommand: function (aCmd) {
    switch (aCmd) {
    case "placesCmd_open":
      alert("No.");
      break;
    case "aCommandOfMyOwn":
      alert("Shrimp and white wine.");
      break;
    }
  },
  isCommandEnabled: function (aCmd) {
    return true;
  },
  onEvent: function (aEventName) {},
  supportsCommand: function (aCmd) {
    return ["placesCmd_open", "aCommandOfMyOwn"].indexOf(aCmd) >= 0;
  }
};
var treeView = document.getElementById("myTreeView");
treeView.controllers.insertControllerAt(0, controller);

If your custom controller is for a custom view, your set of commands includes some of those supported by PlacesController, and you are happy with the way PlacesController handles those commands, then you can still rely on PlacesController to save yourself some work.  There are a couple of strategies.  You can attach two controllers, one a PlacesController instance and one a custom, in the way described in the previous paragraphs.  Or you can attach a single controller, either an instance of PlacesController modified to suit your needs or a completely custom controller that delegates to a PlacesController instance.

The following example creates a PlacesController object, overrides its handling of the placesCmd_open command, and falls back on the default actions for all other commands.  In this case, because the our hypothetical view's only controller is the custom one we're creating here, it is not important that it precede anything; we therefore add the controller by calling nsIControllers.appendController() instead of insertControllerAt():

var treeView = document.getElementById("myCustomTreeView");
var controller = new PlacesController(treeView);
controller._doCommand = controller.doCommand;
controller.doCommand = function (aCmd) {
  if (aCmd === "placesCmd_open")
    alert("No.");
  else
    this._doCommand(aCmd);
};
treeView.controllers.appendController(controller);

Document Tags and Contributors

 Contributors to this page: teoli, Jürgen Jeka, adw, sdwilsh
 Last updated by: teoli,