Displaying Places information using views

  • Revision slug: Displaying_Places_information_using_views
  • Revision title: Displaying Places information using views
  • Revision id: 96503
  • Created:
  • Creator: adw
  • Is current revision? No
  • Comment 2 words removed

Revision Content

{{ template{path: "draft"} }}

{{ Firefox3() }}

Views are one component of the Places model-view-controller design.  Use them to display {{ Interface("nsINavHistoryResult") }} objects to the user.  See Querying Places for information about obtaining and using nsINavHistoryResult objects, which this page assumes you are familiar with.

An nsINavHistoryResult instance provides the data for a view.  The view is responsible for expanding the root {{ Interface("nsINavHistoryContainerResultNode") }} of this instance and displaying the {{ Interface("nsINavHistoryResultNode") }} objects contained therein.

The built-in views

If you need to show the contents of Bookmarks or History in your extension or application, you may want to use the built-in Places views, which are generic and will save you a lot of time writing basic functionality so that you can focus on building your extension or application.

Places provides the following built-in views:

Instantiating

The three built-in views are simply standard XUL elements with a special type attribute whose value is "places".

Every XUL document containing a built-in view must import the stylesheet {{ Source("browser/components/places/content/places.css") }} and overlay the file {{ Source("browser/components/places/content/placesOverlay.xul") }}:

<?xml-stylesheet href="chrome://browser/content/places/places.css" ?>
<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul" ?>

It's this stylesheet that binds elements with the special type attribute to one of the views.  The overlay contains JavaScript required by the views.  It also contains the built-in Places context menu and commands, which you may want to take advantage of in your own uses of the views.

Connecting a view to its data

To hook up a built-in view to its data, use the view's special place attribute.  You may specify the attribute directly in the XUL or set its corresponding property via JavaScript.  Its value is the URI of a query, and the data shown in the view are the results of this query.  For simple queries whose URIs do not change over the life of the view, you might specify the place attribute directly in the XUL.  For more complicated queries or queries whose URIs you plan on changing, you will want to set the view's place property dynamically using JavaScript.  Note that in the latter case it is not sufficient to call setAttribute on the element; use the element's place property instead.  See Querying Places and Places query URIs for information on query URIs.

The following example uses the built-in tree view to display bookmarks whose titles or URLs contain "mozilla".  Remember that, because XUL is XML, any ampersands in query URIs must be written as entity reference &amp; and not simply &.

<tree type="places" place="place:terms=mozilla&amp;onlyBookmarked=1&amp;queryType=1">
  <treecols>
    <treecol id="title" label="My Bookmarks" flex="1" primary="true" />
  </treecols>
  <treechildren />
</tree>

The next example does the same as the last but uses JavaScript to set the tree's place attribute:

var histServ =
  Cc["@mozilla.org/browser/nav-history-service;1"].
  getService(Ci.nsINavHistoryService);

var query = histServ.getNewQuery();
query.searchTerms = "mozilla";
query.onlyBookmarked = true;

var opts = histServ.getNewQueryOptions();
opts.queryType = opts.QUERY_TYPE_BOOKMARKS;

var uri = histServ.queriesToQueryString([query], 1, opts);
var tree = document.getElementById("mytree");
tree.place = uri;

These two examples use the built-in tree view, but the point is to demonstrate the use of the place attribute and property.  In this regard the built-in menu and toolbar views are no different.

When a view's underlying data changes, the view will automatically update itself so that it displays the new data.  This update is handled by logic between the view and its results.  All Places views implement (or aggregate) interface {{ Interface("nsINavHistoryResultViewer") }}, which is the point of interaction between a view and its results.  Results themselves observe Places changes, and if on a Places change a result determines that its data specifically has changed, it notifies its view by calling an appropriate method of nsINavHistoryResultViewer.  Once notified, the view is responsible for updating itself.

Tree view

Create a built-in tree view by setting the type attribute to "places" on a tree element.  The treechildren element should be empty:

<tree type="places">
  <treecols>
    <treecol id="title" flex="1" primary="true" />
    <treecol id="url" flex="1" />
  </treecols>
  <treechildren />
</tree>

The tree view is implemented in {{ Source("browser/components/places/content/tree.xml") }}.  See the tree reference and Trees tutorial for general information about trees.

If you would like your tree view to be styled in the same manner that Firefox styles its uses of the view, you should include the following stylesheet.  Note that this stylesheet is different from the one given above, which must be included if you use a built-in Places view.  The following stylesheet is optional and only applies styles and icons to the built-in tree view:

<?xml-stylesheet href="chrome://browser/skin/places/places.css" ?>

Column binding

The built-in tree view makes it easy to hook up specific columns of your tree to specific properties of the result.  It does so by recognizing certain magic values of the id attribute on your treecol elements.  A property in the result is bound to a column via that column's id attribute.  For example, by setting a column's id to "title", you tell the view to display the title of the {{ Interface("nsINavHistoryResultNode") }} of each row in that column.

The following table shows the mappings between these magic column id values and their corresponding nsINavHistoryResultNode properties:

treecol id or anonid Corresponding nsINavHistoryResultNode property
title title
url
uri
date
time
visitCount
accessCount
keyword *
description *
dateAdded
dateAdded
lastModified
lastModified
tags
tags
** icon

*keyword and description are looked up in the Places database using the nsINavHistoryResultNode property itemId.

**The title column (and only the title column) automatically receives the favicon referenced by the nsINavHistoryResultNode property icon.

You may specify as few or as many of these magic columns as you want, and your tree may of course contain other columns as well.  In lieu of setting an id on a treecol, you may specify an anonid attribute.  This is useful when you need the id for another purpose or when a treecol is contained in anonymous content, as in XBL.  If both an id attribute and anonid attribute are specified, the anonid is used.

The built-in tree view is provided as a general-purpose convenience.  If you need to display additional data or otherwise require more control over your view, you may need to write your own.  See Creating custom views below. 

Using the tree view

So you've got a built-in tree view.  How do you ask it about the data it displays?

First, see {{ Interface("nsIPlacesView") }}.  Like all Places views, the built-in tree view implements this interface, which provides broad methods such as getting the view's {{ Interface("nsINavHistoryResult") }} instance and examining the view's selection.

Second, know this: when it comes to trees, "view" is an overloaded word.  This document describes Places views.  Places views are just XUL elements; the built-in Places tree view is just a tree element whose type attribute is equal to "places", as described above.  But recall that all trees—Places view or not—display their data using another kind of view, nsITreeView .  The built-in Places tree view therefore has a view of its own, the built-in Places tree view's view.  This view is an object that implements three interfaces: from most specific to most general, {{ Interface("nsINavHistoryResultTreeViewer") }}, {{ Interface("nsINavHistoryResultViewer") }}, and {{ Interface("nsITreeView") }}.  nsINavHistoryResultTreeViewer maps between row indices and the {{ Interface("nsINavHistoryResultNode") }} objects contained in the rows.  nsINavHistoryResultViewer updates the view as its underlying data changes.  It's not so useful for our purposes here.  nsITreeView provides a high-level interface for trees in general.

Finally, the built-in tree view implements several convenience methods and properties of its own.

You therefore have four points of interaction with the built-in Places tree view:

  1. The convenience methods and properties implemented directly on the view itself,
  2. the {{ Interface("nsIPlacesView") }} interface of the view itself,
  3. the {{ Interface("nsINavHistoryResultTreeViewer") }} interface of the view's view, and
  4. the {{ Interface("nsITreeView") }} interface of the view's view.

The methods and properties implemented directly on the view are very specific, while the interface provided by nsITreeView is very general.  Tasks sometimes require you to use more than one of these points of interaction, going from one layer of generality to another.

To put it in terms of JavaScript, say you have a variable named treeView that references your Places tree view.

var treeView = document.getElementById("myPlacesTreeView");

Points 1 and 2 apply to this variable.

The tree view's view is the object at treeView.view.  Points 3 and 4 apply to this object.

var treeViewView = treeView.view;

Convenience methods

Because the built-in tree view is both widely used and complex, several convenience methods are implemented directly on it to make common tasks easier.

applyFilter()

Loads a new empty query with particular search terms and folders.

void applyFilter(
  string filterString,
  array  folderRestrict
);
Parameters
filterString
The new query's searchTerms property will be set to this string.
folderRestrict
The setFolders function of the new query will be called on this array of folder IDs. Optional.
load()

Sets the queries that the view displays. This method may be used as an alternative to setting the tree's place property as described above.

void load(
  array                     queries,
  nsINavHistoryQueryOptions options
);
Parameters
queries
An array of {{ Interface("nsINavHistoryQuery") }} objects.
options
An {{ Interface("nsINavHistoryQueryOptions") }} object.
selectNode()

Causes a particular node to be selected in the tree, resulting in all containers above the node in the hierarchy to be opened so that the node is visible.

void selectNode(
  nsINavHistoryNode node
);
Parameters
node
The {{ Interface("nsINavHistoryResultNode") }} to select.
selectPlaceURI()

Causes a particular node represented by the specified placeURI to be selected in the tree. All containers above the node in the hierarchy will be opened so that the node is visible.

void selectPlaceURI(
  string placeURI
);
Parameters
placeURI
The URI (as a string) of the {{ Interface("nsINavHistoryResultNode") }} to select.

Convenience properties

In addition to the methods above, some properties of convenience are implemented directly on the built-in tree view.

Property Type Description
flatList boolean If true the view does not recurse into containers. The action to take when a container is toggled can be set via the onOpenFlatContainer property. At least one of flatList and showRoot must be false.
onOpenFlatContainer string The body of function that will be called when a container is toggled. Only applies if property flatList is true. The function will be passed one {{ Interface("nsINavHistoryResultNode") }} argument named aContainer. You can QueryInterface aContainer to an {{ Interface("nsINavHistoryContainerResultNode") }}.
showRoot boolean If true the root {{ Interface("nsINavHistoryContainerResultNode") }} is shown as the first row in the tree. At least one of showRoot and flatList must be false.

Example uses

To get the {{ Interface("nsINavHistoryResultNode") }} at a specific row:

var treeView = document.getElementById("myPlacesTreeView");
var rowIndex = 0;
var historyResultNode = treeView.view.nodeForTreeIndex(rowIndex);

To get the row index of a specific nsINavHistoryResultNode:

var treeView = document.getElementById("myPlacesTreeView"); 
var rowIndex = treeView.view.treeIndexForNode(historyResultNode);

To select a row in the tree whose node has a specific URI:

var treeView = document.getElementById("myPlacesTreeView");
treeView.selectPlaceURI("some place URI");

To select a row in the tree that contains a specific nsINavHistoryResultNode:

var treeView = document.getElementById("myPlacesTreeView");
treeView.selectNode(someHistoryResultNode); 

PlacesTreeView

Note: This section describes the implementation of the Places tree view.

The built-in tree view is backed by an instance of PlacesTreeView, a prototype defined in {{ Source("browser/components/places/content/treeView.js") }}.  PlacesTreeView performs double duty for the built-in tree view: it implements both {{ Interface("nsITreeView") }} and much of the functionality required of a Places view.  The latter functionality is specified specifically by interface {{ Interface("nsINavHistoryResultTreeViewer") }}, which inherits from the more general {{ Interface("nsINavHistoryResultViewer") }}.

Because of PlacesTreeView's double duty, you can use it to bridge a query result and a tree element:

var result = historyService.executeQuery(query, opts); // your Places query result
var tree = document.getElementById("mytree");          // your tree element

var showRootNodeInTree = true;
var view = new PlacesTreeView(showRootNodeInTree);

// Here's the bridge!
result.viewer = view.QueryInterface(Components.interfaces.nsINavHistoryResultViewer);
tree.view = view.QueryInterface(Components.interfaces.nsITreeView);

In fact this is how the built-in tree view works.  It runs code similar to the above when you set its place property or call its load method.

While you are free to set up your tree view in this manner, it's not recommended if you are using the built-in view.  Doing so circumvents updating the view's place attribute, causing it to fall out of sync with the view's query result.  Use the view's load method or place property instead and let it do the work for you.  If, on the other hand, you are writing a custom tree view, you will need to write similar code at some point.

Menu view

Create a built-in menu view by setting the type attribute to "places" on an empty menupopup element (which would be a child of some parent menu element):

<menu>
  <menupopup type="places" />
</menu>

The place attribute or property should be set on the menupopup as well.

The menu view is implemented in {{ Source("browser/components/places/content/menu.xml") }}.  See the menupopup and menu references and Popup Menus tutorial for general information about menus.

Toolbar view

Create a built-in toolbar view by setting the type attribute to "places" on an empty hbox element (which would be a child of some parent toolbaritem element, itself the child of a toolbar element):

<toolbar>
  <toolbaritem>
    <hbox type="places" />
  </toolbaritem>
</toolbar>

The place attribute or property should be set on the hbox as well.

The toolbar view is implemented in {{ Source("browser/components/places/content/toolbar.xml") }}.  See the toolbaritem and toolbar references and Toolbars tutorial for general information about toolbars.

Using a view

So you've got a Places view.  How do you interact with it?

Because it is fairly complex, the built-in tree view provides uniquely tailored interfaces to make it easier to use.  If you write your own complex view, you might do something similar.

But all Places views should provide a minimal interface so that you and controllers have a consistent, general way to interact with them.  For this reason views implement interface {{ Interface("nsIPlacesView") }}.  It allows you to do things like getting the {{ Interface("nsINavHistoryResult") }} instance that a view displays and examining its selected nodes.  In fact the special place property described above is a property of this interface.

Creating custom views

If you need greater flexibility than a built-in view provides, you can create a custom view.  Good reasons for needing a custom view might include (but are in no way limited to):

  • Displaying custom columns alongside those provided by the built-in tree view.
  • Changing the way a built-in view displays dates or other data.
  • Inserting information into the display that is not determined by the underlying data.
  • Displaying Places information in an element not covered by one of the built-in views.

Potentially bad reasons for creating a custom view might include (but are not limited to):

  • Changing only the superficial appearance of a built-in view rather than the content it displays.  Try CSS here.
  • Hiding columns of the built-in tree view.  Just leave out the treecol elements you want to hide.
  • Managing how the view responds to clicks, commands, and other user interaction.  Don't break the separation of concerns here.  Views are for displaying data, controllers for logic.  You need to create a custom controller.  See View Controller for more information.

Registering a view

Register the view by setting the viewer attribute on nsINavHistoryResult. When you do this, the result will in turn set the result attribute on the given view. You should not set the result attribute on the view explicitly. To clear the view, set the viewer attribute to null. This will cause the view's result attribute to be set to null as well.

Be careful about reference cycles. The view and the result both hold owning references to each other. For these objects to be deleted, you must clear this cycle, by setting result.viewer to null. The built-in tree view (see below) does this automatically. When the tree is destroyed or a different nsITreeView is associated with the tree, the tree will call nsITreeView.tree = null; The viewer detects this case and also detaches itself from the result.

Implementing a view

If you require a customized tree view, it may be easiest to wrap the nsINavHistoryResultTreeViewer in your own class. For example, if you wanted a "special" first row, your object would provide nsITreeView responses for the first row, and pass all other messages through to the built-in tree view with indices shifted by one.

The attribute nsINavHistoryResultNode.viewIndex is provided explicitly for the use of the view. These values are initialized to -1 when each node is created. You can use this value to keep track of visible nodes. The built-in tree viewer uses this attribute to store the row index that the node is on.

See also

{{ languages( { "ja": "ja/Displaying_Places_information_using_views" } ) }}

Revision Source

<p>{{ template{path: "draft"} }}</p>
<p>{{ Firefox3() }}</p>
<p>Views are one component of the Places <a class="external" href="http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller" title="http://en.wikipedia.org/wiki/Model–view–controller">model-view-controller</a> design.  Use them to display {{ Interface("nsINavHistoryResult") }} objects to the user.  See <a class="internal" href="/en/Querying_Places" title="En/Querying Places">Querying Places</a> for information about obtaining and using <code>nsINavHistoryResult</code> objects, which this page assumes you are familiar with.</p>
<p>An <code>nsINavHistoryResult</code> instance provides the data for a view.  The view is responsible for expanding the root {{ Interface("nsINavHistoryContainerResultNode") }} of this instance and displaying the {{ Interface("nsINavHistoryResultNode") }} objects contained therein.</p>
<h2 name="The_built-in_views">The built-in views</h2>
<p>If you need to show the contents of Bookmarks or History in your extension or application, you may want to use the built-in Places views, which are generic and will save you a lot of time writing basic functionality so that you can focus on building your extension or application.</p>
<p>Places provides the following built-in views:</p>
<ul> <li><a href="#Tree_view">Tree</a></li> <li><a href="#Menu_view">Menu</a></li> <li><a href="#Toolbar_view">Toolbar</a></li>
</ul>
<h3 name="Instantiating_in_XUL">Instantiating</h3>
<p>The three built-in views are simply standard XUL elements with a special <code>type</code> attribute whose value is "places".</p>
<p>Every XUL document containing a built-in view must import the stylesheet {{ Source("browser/components/places/content/places.css") }} and overlay the file {{ Source("browser/components/places/content/placesOverlay.xul") }}:</p>
<pre class="brush: xml">&lt;?xml-stylesheet href="chrome://browser/content/places/places.css" ?&gt;
&lt;?xul-overlay href="chrome://browser/content/places/placesOverlay.xul" ?&gt;</pre>
<p>It's this stylesheet that binds elements with the special <code>type</code> attribute to one of the views.  The overlay contains JavaScript required by the views.  It also contains the built-in Places context menu and commands, which you may want to take advantage of in your own uses of the views.</p><h3>Connecting a view to its data</h3>
<p>To hook up a built-in view to its data, use the view's special <code>place</code> attribute.  You may specify the attribute directly in the XUL or set its corresponding property via JavaScript.  Its value is the URI of a query, and the data shown in the view are the results of this query.  For simple queries whose URIs do not change over the life of the view, you might specify the <code>place</code> attribute directly in the XUL.  For more complicated queries or queries whose URIs you plan on changing, you will want to set the view's <code>place</code> property dynamically using JavaScript.  Note that in the latter case it is not sufficient to call <code>setAttribute</code> on the element; use the element's <code>place</code> property instead.  See <a class="internal" href="/en/Querying_Places#Serializing_queries" title="En/Querying Places">Querying Places</a> and <a class="internal" href="/en/Places_query_URIs" title="En/Places query URIs">Places query URIs</a> for information on query URIs.</p>
<p>The following example uses the <a href="#Tree_view">built-in tree view</a> to display bookmarks whose titles or URLs contain "mozilla".  Remember that, because XUL is XML, any ampersands in query URIs must be written as entity reference <code>&amp;amp;</code> and not simply <code>&amp;</code>.</p>
<pre class="brush: xml">&lt;tree type="places" place="place:terms=mozilla&amp;amp;onlyBookmarked=1&amp;amp;queryType=1"&gt;
  &lt;treecols&gt;
    &lt;treecol id="title" label="My Bookmarks" flex="1" primary="true" /&gt;
  &lt;/treecols&gt;
  &lt;treechildren /&gt;
&lt;/tree&gt;
</pre>
<p>The next example does the same as the last but uses JavaScript to set the tree's <code>place</code> attribute:</p>
<pre class="brush: js">var histServ =
  Cc["@mozilla.org/browser/nav-history-service;1"].
  getService(Ci.nsINavHistoryService);

var query = histServ.getNewQuery();
query.searchTerms = "mozilla";
query.onlyBookmarked = true;

var opts = histServ.getNewQueryOptions();
opts.queryType = opts.QUERY_TYPE_BOOKMARKS;

var uri = histServ.queriesToQueryString([query], 1, opts);
var tree = document.getElementById("mytree");
tree.place = uri;</pre>
<p>These two examples use the built-in tree view, but the point is to demonstrate the use of the <code>place</code> attribute and property.  In this regard the built-in menu and toolbar views are no different.</p>
<p>When a view's underlying data changes, the view will automatically update itself so that it displays the new data.  This update is handled by logic between the view and its results.  All Places views implement (or aggregate) interface {{ Interface("nsINavHistoryResultViewer") }}, which is the point of interaction between a view and its results.  Results themselves observe Places changes, and if on a Places change a result determines that its data specifically has changed, it notifies its view by calling an appropriate method of <code>nsINavHistoryResultViewer</code>.  Once notified, the view is responsible for updating itself.</p><h3>Tree view</h3>
<p>Create a built-in tree view by setting the <code>type</code> attribute to "places" on a tree element.  The <code>treechildren</code> element should be empty:</p>
<pre class="brush: xml">&lt;tree type="places"&gt;
  &lt;treecols&gt;
    &lt;treecol id="title" flex="1" primary="true" /&gt;
    &lt;treecol id="url" flex="1" /&gt;
  &lt;/treecols&gt;
  &lt;treechildren /&gt;
&lt;/tree&gt;
</pre>
<p>The tree view is implemented in {{ Source("browser/components/places/content/tree.xml") }}.  See the <a class="internal" href="/en/XUL/tree" title="En/XUL/Tree">tree</a> reference and <a class="internal" href="/en/XUL_Tutorial/Trees" title="En/XUL Tutorial/Trees">Trees</a> tutorial for general information about trees.</p>
<p><span class="lang lang-en">If you would like your tree view to be styled in the same manner that Firefox styles its uses of the view, you should include the following stylesheet.  Note that this stylesheet is different from the one given <a href="#Instantiating">above</a>, which <em>must</em> be included if you use a built-in Places view.  The following stylesheet is optional and only applies styles and icons to the built-in tree view:<br>
</span></p>
<pre class="brush: xml"><span class="lang lang-en">&lt;?xml-stylesheet href="chrome://browser/skin/places/places.css" ?&gt;</span>
</pre>
<h4>Column binding</h4>
<p><code><span style="font-family: Verdana,Tahoma,sans-serif;">The built-in tree view</span></code> makes it easy to hook up specific columns of your tree to specific properties of the result.  It does so by recognizing certain magic values of the <code>id</code> attribute on your <code>treecol</code> elements.  A property in the result is bound to a column via that column's <code>id</code> attribute.  For example, by setting a column's <code>id</code> to "title", you tell the view to display the title of the {{ Interface("nsINavHistoryResultNode") }} of each row in that column.</p>
<p>The following table shows the mappings between these magic column <code>id</code> values and their corresponding <code>nsINavHistoryResultNode</code> properties:</p>
<table class="standard-table" style="margin-left: 40px;"> <tbody> <tr> <td class="header"><code>treecol</code> <code>id</code> or <code>anonid</code></td> <td class="header">Corresponding <code>nsINavHistoryResultNode</code> property</td> </tr> <tr> <td>title</td> <td><code>title</code></td> </tr> <tr> <td>url<code><br> </code></td> <td><code>uri</code></td> </tr> <tr> <td>date<code><br> </code></td> <td><code>time</code></td> </tr> <tr> <td>visitCount<code><br> </code></td> <td><code>accessCount</code></td> </tr> <tr> <td>keyword</td> <td>*</td> </tr> <tr> <td>description</td> <td>*</td> </tr> <tr> <td>dateAdded<code><br> </code></td> <td><code>dateAdded</code></td> </tr> <tr> <td>lastModified<code><br> </code></td> <td><code>lastModified</code></td> </tr> <tr> <td>tags<code><br> </code></td> <td><code>tags</code></td> </tr> <tr> <td>**</td> <td><code>icon</code></td> </tr> </tbody>
</table>
<p style="margin-left: 40px;">*keyword and description are looked up in the Places database using the <code>nsINavHistoryResultNode</code> property <code>itemId</code>.</p>
<p style="margin-left: 40px;">**The title column (and only the title column) automatically receives the favicon referenced by the <code>nsINavHistoryResultNode</code> property <code>icon.<br>
</code></p>
<p>You may specify as few or as many of these magic columns as you want, and your tree may of course contain other columns as well.  In lieu of setting an <code>id</code> on a <code>treecol</code>, you may specify an <code>anonid</code> attribute.  This is useful when you need the <code>id</code> for another purpose or when a <code>treecol</code> is contained in anonymous content, as in <a class="internal" href="/en/XBL" title="En/XBL">XBL</a>.  If both an <code>id</code> attribute and <code>anonid</code> attribute are specified, the <code>anonid</code> is used.</p>
<p>The built-in tree view is provided as a general-purpose convenience.  If you need to display additional data or otherwise require more control over your view, you may need to write your own.  See <a href="#Creating_custom_views">Creating custom views</a> below. </p>
<h4>Using the tree view</h4>
<p>So you've got a built-in tree view.  How do you ask it about the data it displays?</p>
<p>First, see {{ Interface("nsIPlacesView") }}.  Like all Places views, the built-in tree view implements this interface, which provides broad methods such as getting the view's {{ Interface("nsINavHistoryResult") }} instance and examining the view's selection.</p>
<p>Second, know this: when it comes to trees, "view" is an overloaded word.  This document describes Places views.  Places views are just XUL elements; the built-in Places tree view is just a tree element whose <code>type</code> attribute is equal to "places", as described <a href="#Tree_view">above</a>.  But recall that all trees—Places view or not—display their data using another kind of view, <span class="lang lang-en"><code><a href="../../../../en/nsITreeView" rel="internal">nsITreeView</a></code> </span>.  The built-in Places tree view therefore has a view of its own, the built-in Places tree view's view.  This view is an object that implements three interfaces: from most specific to most general, {{ Interface("nsINavHistoryResultTreeViewer") }}, {{ Interface("nsINavHistoryResultViewer") }}, and {{ Interface("nsITreeView") }}.  <code>nsINavHistoryResultTreeViewer</code> maps between row indices and the {{ Interface("nsINavHistoryResultNode") }} objects contained in the rows.  <code>nsINavHistoryResultViewer</code> updates the view as its underlying data changes.  It's not so useful for our purposes here.  <code>nsITreeView</code> provides a high-level interface for trees in general.</p>
<p>Finally, the built-in tree view implements several convenience <a href="#Convenience_methods">methods</a> and <a href="#Convenience_properties">properties</a> of its own.</p>
<p>You therefore have four points of interaction with the built-in Places tree view:</p>
<ol> <li>The convenience <a href="#Convenience_methods">methods</a> and <a href="#Convenience_properties">properties</a> implemented directly on the view itself,</li> <li>the {{ Interface("nsIPlacesView") }} interface of the view itself,</li> <li>the {{ Interface("nsINavHistoryResultTreeViewer") }} interface of the view's view, and</li> <li>the {{ Interface("nsITreeView") }} interface of the view's view.</li>
</ol>
<p>The methods and properties implemented directly on the view are very specific, while the interface provided by <code>nsITreeView</code> is very general.  Tasks sometimes require you to use more than one of these points of interaction, going from one layer of generality to another.</p>
<p>To put it in terms of JavaScript, say you have a variable named <code>treeView</code> that references your Places tree view.</p>
<pre class="brush: js">var treeView = document.getElementById("myPlacesTreeView");
</pre>
<p>Points 1 and 2 apply to this variable.</p>
<p>The tree view's view is the object at <code>treeView.view</code>.  Points 3 and 4 apply to this object.<code><br>
</code></p>
<pre class="brush: js">var treeViewView = treeView.view;
</pre>
<h4>Convenience methods</h4>
<p>Because the built-in tree view is both widely used and complex, several convenience methods are implemented directly on it to make common tasks easier.</p>
<h5>applyFilter()</h5>
<p>Loads a new empty query with particular search terms and folders.</p>
<pre>void applyFilter(
  string filterString,
  array  folderRestrict
);
</pre>
<h6>Parameters</h6>
<dl> <dt><code>filterString</code></dt> <dd>The new query's <code>searchTerms</code> property will be set to this string.</dd> <dt><code>folderRestrict</code></dt> <dd>The <code>setFolders</code> function of the new query will be called on this array of folder IDs. Optional.</dd> </dl>
<h5>load()</h5>
<p>Sets the queries that the view displays. This method may be used as an alternative to setting the tree's <code>place</code> property as described above.</p>
<pre>void load(
  array                     queries,
  nsINavHistoryQueryOptions options
);
</pre>
<h6>Parameters</h6>
<dl> <dt><code>queries</code></dt> <dd>An array of {{ Interface("nsINavHistoryQuery") }} objects.</dd> <dt><code>options</code></dt> <dd>An {{ Interface("nsINavHistoryQueryOptions") }} object.</dd> </dl>
<h5>selectNode()</h5>
<p>Causes a particular node to be selected in the tree, resulting in all containers above the node in the hierarchy to be opened so that the node is visible.</p>
<pre>void selectNode(
  nsINavHistoryNode node
);
</pre>
<h6>Parameters</h6>
<dl> <dt><code>node</code></dt> <dd>The {{ Interface("nsINavHistoryResultNode") }} to select.</dd> </dl>
<h5>selectPlaceURI()</h5>
<p>Causes a particular node represented by the specified placeURI to be selected in the tree. All containers above the node in the hierarchy will be opened so that the node is visible.</p>
<pre>void selectPlaceURI(
  string placeURI
);
</pre>
<h6>Parameters</h6>
<dl> <dt><code>placeURI</code></dt> <dd>The URI (as a string) of the {{ Interface("nsINavHistoryResultNode") }} to select.</dd> </dl>
<h4>Convenience properties</h4>
<p>In addition to the methods above, some properties of convenience are implemented directly on the built-in tree view.</p>
<table class="standard-table"> <tbody> <tr> <td class="header">Property</td> <td class="header">Type</td> <td class="header">Description</td> </tr> <tr> <td><code>flatList</code></td> <td><code>boolean</code></td> <td>If true the view does not recurse into containers. The action to take when a container is toggled can be set via the <code>onOpenFlatContainer</code> property. At least one of <code>flatList</code> and <code>showRoot</code> must be false.</td> </tr> <tr> <td><code>onOpenFlatContainer</code></td> <td><code>string</code></td> <td>The body of function that will be called when a container is toggled. Only applies if property <code>flatList</code> is true. The function will be passed one {{ Interface("nsINavHistoryResultNode") }} argument named <code>aContainer</code>. You can QueryInterface <code>aContainer</code> to an {{ Interface("nsINavHistoryContainerResultNode") }}.</td> </tr> <tr> <td><code>showRoot</code></td> <td><code>boolean</code></td> <td>If true the root {{ Interface("nsINavHistoryContainerResultNode") }} is shown as the first row in the tree. At least one of <code>showRoot</code> and <code>flatList</code> must be false.</td> </tr> </tbody>
</table>
<h4>Example uses</h4>
<p>To get the {{ Interface("nsINavHistoryResultNode") }} at a specific row:</p>
<pre class="brush: js">var treeView = document.getElementById("myPlacesTreeView");
var rowIndex = 0;
var historyResultNode = treeView.view.nodeForTreeIndex(rowIndex);</pre>
<p>To get the row index of a specific <code>nsINavHistoryResultNode</code>:</p>
<pre class="brush: js"><span><span class="keyword">var</span><span> treeView = document.getElementById(</span><span class="string">"myPlacesTreeView"</span><span>); </span></span>
var rowIndex = treeView.view.treeIndexForNode(historyResultNode);
</pre>
<p>To select a row in the tree whose node has a specific URI:</p>
<pre class="brush: js"><span><span class="keyword">var</span><span> treeView = document.getElementById(</span><span class="string">"myPlacesTreeView"</span><span>);</span></span>
<span><span>treeView.selectPlaceURI("some place URI");</span></span></pre>
<p>To select a row in the tree that contains a specific <code>nsINavHistoryResultNode</code>:</p>
<pre class="brush: js"><span><span class="keyword">var</span><span> treeView = document.getElementById(</span><span class="string">"myPlacesTreeView"</span><span>);</span></span>
<span><span>treeView.selectNode(someHistoryResultNode);</span></span> 
</pre>
<h4>PlacesTreeView</h4>
<div class="note"><strong>Note:</strong> This section describes the implementation of the Places tree view.</div>
<p>The built-in tree view is backed by an instance of <code>PlacesTreeView</code>, a prototype defined in {{ Source("browser/components/places/content/treeView.js") }}.  <code>PlacesTreeView</code> performs double duty for the built-in tree view: it implements both {{ Interface("nsITreeView") }} and much of the functionality required of a Places view.  The latter functionality is specified specifically by interface {{ Interface("nsINavHistoryResultTreeViewer") }}, which inherits from the more general {{ Interface("nsINavHistoryResultViewer") }}.</p>
<p>Because of <code>PlacesTreeView</code>'s double duty, you can use it to bridge a query result and a tree element:</p>
<pre class="brush: js">var result = historyService.executeQuery(query, opts); // your Places query result
var tree = document.getElementById("mytree");          // your tree element

var showRootNodeInTree = true;
var view = new PlacesTreeView(showRootNodeInTree);

// Here's the bridge!
result.viewer = view.QueryInterface(Components.interfaces.nsINavHistoryResultViewer);
tree.view = view.QueryInterface(Components.interfaces.nsITreeView);
</pre>
<p>In fact this is how the built-in tree view works.  It runs code similar to the above when you set its <code>place</code> property or call its <code>load</code> method.</p>
<p>While you are free to set up your tree view in this manner, it's not recommended if you are using the built-in view.  Doing so circumvents updating the view's <code>place</code> attribute, causing it to fall out of sync with the view's query result.  Use the view's <code>load</code> method or <code>place</code> property instead and let it do the work for you.  If, on the other hand, you are writing a custom tree view, you will need to write similar code at some point.</p><h3>Menu view</h3>
<p>Create a built-in menu view by setting the <code>type</code> attribute to "places" on an empty <code>menupopup</code> element (which would be a child of some parent <code>menu</code> element):</p>
<pre class="brush: xml">&lt;menu&gt;
  &lt;menupopup type="places" /&gt;
&lt;/menu&gt;
</pre>
<p>The <code>place</code> attribute or property should be set on the <code>menupopup</code> as well.</p>
<p>The menu view is implemented in {{ Source("browser/components/places/content/menu.xml") }}.  See the <a class="internal" href="/en/XUL/menupopup" title="En/XUL/Menupopup">menupopup</a> and <a class="internal" href="/en/XUL/menu" title="En/XUL/Menu">menu</a> references and <a class="internal" href="/en/XUL_Tutorial/Popup_Menus" title="En/XUL Tutorial/Popup Menus">Popup Menus</a> tutorial for general information about menus.</p>
<h3>Toolbar view</h3>
<p>Create a built-in toolbar view by setting the <code>type</code> attribute to "places" on an empty <code>hbox</code> element (which would be a child of some parent <code>toolbaritem</code> element, itself the child of a <code>toolbar</code> element):</p>
<pre class="brush: xml">&lt;toolbar&gt;
  &lt;toolbaritem&gt;
    &lt;hbox type="places" /&gt;
  &lt;/toolbaritem&gt;
&lt;/toolbar&gt;
</pre>
<p>The <code>place</code> attribute or property should be set on the <code>hbox</code> as well.</p>
<p>The toolbar view is implemented in {{ Source("browser/components/places/content/toolbar.xml") }}.  See the <a class="internal" href="/en/XUL/toolbaritem" title="En/XUL/Toolbaritem">toolbaritem</a> and <a class="internal" href="/en/XUL/toolbar" title="En/XUL/Toolbar">toolbar</a> references and <a href="/en/XUL_Tutorial/Toolbars" title="en/XUL_Tutorial/Toolbars">Toolbars</a> tutorial for general information about toolbars.</p>
<h2>Using a view</h2>
<p>So you've got a Places view.  How do you interact with it?</p>
<p>Because it is fairly complex, the <a href="#Using_the_tree_view">built-in tree view</a> provides uniquely tailored interfaces to make it easier to use.  If you <a href="#Creating_custom_views">write your own</a> complex view, you might do something similar.</p>
<p>But all Places views should provide a minimal interface so that you and controllers have a consistent, general way to interact with them.  For this reason views implement interface {{ Interface("nsIPlacesView") }}.  It allows you to do things like getting the {{ Interface("nsINavHistoryResult") }} instance that a view displays and examining its selected nodes.  In fact the special <a href="#Connecting_a_view_to_its_data"><code>place</code> property</a> described above is a property of this interface.</p>
<h2 name="Creating_custom_views">Creating custom views</h2>
<p>If you need greater flexibility than a built-in view provides, you can create a custom view.  Good reasons for needing a custom view might include (but are in no way limited to):</p>
<ul> <li>Displaying custom columns alongside those provided by the built-in tree view.</li> <li>Changing the way a built-in view displays dates or other data.</li> <li>Inserting information into the display that is not determined by the underlying data.</li> <li>Displaying Places information in an element not covered by one of the built-in views.</li>
</ul>
<p>Potentially bad reasons for creating a custom view might include (but are not limited to):</p>
<ul> <li>Changing only the superficial appearance of a built-in view rather than the content it displays.  Try CSS here.</li> <li>Hiding columns of the built-in tree view.  Just leave out the <code>treecol</code> elements you want to hide.</li> <li>Managing how the view responds to clicks, commands, and other user interaction.  Don't break the separation of concerns here.  Views are for displaying data, controllers for logic.  You need to create a custom controller.  See <a class="internal" href="/En/Places/View_Controller" title="en/Places/View Controller">View Controller</a> for more information.</li>
</ul>
<h3 name="Registering_a_view">Registering a view</h3>
<p>Register the view by setting the <code>viewer</code> attribute on <code>nsINavHistoryResult</code>. When you do this, the result will in turn set the <code>result</code> attribute on the given view. <em>You should not set the <code>result</code> attribute on the view explicitly.</em> To clear the view, set the <code>viewer</code> attribute to <code>null</code>. This will cause the view's <code>result</code> attribute to be set to <code>null</code> as well.</p>
<p>Be careful about reference cycles. The view and the result both hold owning references to each other. For these objects to be deleted, you must clear this cycle, by setting <code>result.viewer</code> to <code>null</code>. The built-in tree view (see below) does this automatically. When the tree is destroyed or a different <code>nsITreeView</code> is associated with the tree, the tree will call <code>nsITreeView.tree = null;</code> The viewer detects this case and also detaches itself from the result.</p>
<h3 name="Implementing_a_view">Implementing a view</h3>
<p>If you require a customized tree view, it may be easiest to wrap the <code>nsINavHistoryResultTreeViewer</code> in your own class. For example, if you wanted a "special" first row, your object would provide <code>nsITreeView</code> responses for the first row, and pass all other messages through to the built-in tree view with indices shifted by one.</p>
<p>The attribute <code>nsINavHistoryResultNode.viewIndex</code> is provided explicitly for the use of the view. These values are initialized to <code>-1</code> when each node is created. You can use this value to keep track of visible nodes. The built-in tree viewer uses this attribute to store the row index that the node is on.</p><h2>See also</h2>
<ul> <li>{{ Interface("nsIPlacesView") }}</li> <li><a class="internal" href="/en/Querying_Places" title="En/Querying Places">Querying Places</a></li> <li><a class="internal" href="/En/Places/View_Controller" title="En/Places/View Controller">View Controller</a> </li>
</ul>
<p>{{ languages( { "ja": "ja/Displaying_Places_information_using_views" } ) }}</p>
Revert to this revision