Displaying Places information using views

  • Revision slug: Displaying_Places_information_using_views
  • Revision title: Displaying Places information using views
  • Revision id: 96380
  • Created:
  • Creator: adw
  • Is current revision? No
  • Comment 2 words added, 3 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.  Views implement the interface {{ Interface("nsINavHistoryResultViewer") }}.

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 makes available the following built-in views:

Instantiating

The three built-in views are simply standard XUL elements with a special attribute type whose value is "places".  Every XUL document containing a built-in Places view should import the stylesheet {{ Source("browser/components/places/content/places.css") }}:

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

It's this stylesheet that binds elements with the special attribute to one of the built-in views.

You may also want your view to support the built-in context menu and commands such as opening a bookmark, creating a new bookmark, and deleting a bookmark.  The context menu and commands are contained in overlay {{ Source("browser/components/places/content/placesOverlay.xul") }}, so you will need to include it in your XUL:

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

To hook up a view to its data, use the view's special attribute place.  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 a 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" flex="1" primary="true" />
  </treecols>
  <treechildren flex="1" />
</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;

// Assume tree references some tree element with attribute type="places".
var uri = histServ.queriesToQueryString([query], 1, opts);
tree.place = uri;

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

Script hookup

var view = document.getElementById("your_view");
view.init(null);
view.appendController(PlacesController);

The View is initialized with null (the default View Configuration - for options, read Places:View Configurations for more information on using the ViewConfig object to modify a View's functionality), and the Controller is attached. The View is now ready to use.

Tree view

Create a tree view by setting attribute type 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.

The 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 attributes 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 nsINavHistoryResultNode of each row in that column.  The following table shows the mappings between these magic 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

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

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.

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.

PlacesTreeView

When it comes to trees, "view" is an overloaded word.  This page describes Places views, but recall that all trees display their data using views that implement nsITreeView.  The built-in Places tree view's view is implemented by PlacesTreeView, a prototypical JavaScript object in {{ Source("browser/components/places/content/treeView.js") }}.  Like all tree views, PlacesTreeView implements nsITreeView, and, like all built-in Places views, it implements nsINavHistoryResultViewer.  You can therefore use this object to bridge between a result (see Querying Places) and a tree:

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

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

result.viewer = treeviewer;
tree.view = treeviewer.QueryInterface(Components.interfaces.nsITreeView);

The built-in tree view also allows observers to attach that implement nsINavHistoryResultViewObserver (declared in {{ Source("toolkit/components/places/public/nsINavHistoryService.idl", "nsINavHistoryService.idl") }}). This observer interface allows external components to see what is happening and to take appropriate action. For the Places trees, the controller attaches and, for example, notices when something has been drag-and-dropped on the tree. It then takes the appropriate action.

Menu view

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

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

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 toolbar view by setting attribute type 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 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.

Creating custom views

If you need greater flexibility or to provide a customized appearance for your display of Places information, you can create a custom view to do so.

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.

The nsINavHistoryResultViewer also has an observer interface to allow an nsINavHistoryResultViewObserver to observer changes. However, this observer interface is specifically for trees. The {{ Bug("337638") }} is for moving this to the nsINavHistoryResultTreeViewer object. Other implementors if nsINavHistoryResultViewer should use their own observers.

The PlacesView interface

Different kinds of views implement different interfaces.  But whatever the kind -- the built-in tree, menu, or toolbar view or one of your own -- a view should provide a minimal interface so that controllers have a consistent, general way to interact with it.  For this reason views implement the PlacesView interface.

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.  Views implement the interface {{ Interface("nsINavHistoryResultViewer") }}.</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 makes available 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 attribute <code>type</code> whose value is "places".  Every XUL document containing a built-in Places view should import the stylesheet {{ Source("browser/components/places/content/places.css") }}:</p>
<pre class="brush: xml">&lt;?xml-stylesheet href="chrome://browser/content/places/places.css" ?&gt;
</pre>
<p>It's this stylesheet that binds elements with the special attribute to one of the built-in views.</p>
<p>You may also want your view to support the built-in context menu and commands such as opening a bookmark, creating a new bookmark, and deleting a bookmark.  The context menu and commands are contained in overlay {{ Source("browser/components/places/content/placesOverlay.xul") }}, so you will need to include it in your XUL:</p>
<pre class="brush: xml">&lt;?xul-overlay href="chrome://browser/content/places/placesOverlay.xul" ?&gt;
</pre>
<p>To hook up a view to its data, use the view's special attribute <code>place</code>.  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 a 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 <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" flex="1" primary="true" /&gt;
  &lt;/treecols&gt;
  &lt;treechildren flex="1" /&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;

// Assume tree references some tree element with attribute type="places".
var uri = histServ.queriesToQueryString([query], 1, opts);
tree.place = uri;</pre>
<p>These two examples use tree views, but the point is to demonstrate the use of the <code>place</code> attribute and property.  In this regard the menu and toolbar views are no different.</p>
<h3 name="Script_Hookup">Script hookup</h3>
<pre>var view = document.getElementById("your_view");
view.init(null);
view.appendController(PlacesController);
</pre>
<p>The View is initialized with <code>null</code> (the default View Configuration - for options, read <a href="/en/Places/View_Configurations" title="en/Places/View_Configurations">Places:View Configurations</a> for more information on using the ViewConfig object to modify a View's functionality), and the <a href="/En/Places/View_Controller" title="En/Places/View_Controller">Controller</a> is attached. The View is now ready to use.</p>
<h3>Tree view</h3>
<p>Create a tree view by setting attribute <code>type</code> 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><code><span style="font-family: Verdana,Tahoma,sans-serif;">The 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> attributes 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 <code>nsINavHistoryResultNode</code> of each row in that column.  The following table shows the mappings between these magic <code>id</code> values and their corresponding <code>nsINavHistoryResultNode</code> properties:</p>
<table border="0" cellspacing="3" style="" width="645"> <tbody> <tr> <td><code><strong>treecol</strong></code><strong> <code>id</code> or </strong><code><strong>anonid</strong></code></td> <td><strong>Corresponding <code>nsINavHistoryResultNode</code> property</strong></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> </tbody>
</table>
<p>*<code>keyword</code> and <code>description</code> are looked up in the Places database using the <code>nsINavHistoryResultNode</code> property <code>itemId</code>. </p>
<p>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 XBL.  If both an <code>id</code> attribute and <code>anonid</code> attribute are specified, the <code>anonid</code> is used.</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.</p>
<h4>PlacesTreeView</h4>
<p>When it comes to trees, "view" is an overloaded word.  This page describes Places views, but recall that all trees display their data using views that implement <code><a class="internal" href="/en/nsITreeView" title="En/NsITreeView">nsITreeView</a></code>.  The built-in Places tree view's view is implemented by <code>PlacesTreeView</code>, a prototypical JavaScript object in {{ Source("browser/components/places/content/treeView.js") }}.  Like all tree views, <code>PlacesTreeView</code> implements <code>nsITreeView</code>, and, like all built-in Places views, it implements <a class="internal" href="/en/nsINavHistoryResultViewer" title="En/NsINavHistoryResultViewer"><code>nsINavHistoryResultViewer</code></a>.  You can therefore use this object to bridge between a result (see <a href="/en/Querying_Places" title="en/Querying_Places">Querying Places</a>) and a tree:</p>
<pre>var result = historyService.executeQuery(...); // your Places query result
var tree = document.getElementById("mytree"); // your tree control

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

result.viewer = treeviewer;
tree.view = treeviewer.QueryInterface(Components.interfaces.nsITreeView);
</pre>
<p>The built-in tree view also allows observers to attach that implement <code>nsINavHistoryResultViewObserver</code> (declared in {{ Source("toolkit/components/places/public/nsINavHistoryService.idl", "nsINavHistoryService.idl") }}). This observer interface allows external components to see what is happening and to take appropriate action. For the Places trees, the controller attaches and, for example, notices when something has been drag-and-dropped on the tree. It then takes the appropriate action.</p>
<h3>Menu view</h3>
<p>Create a menu view by setting attribute <code>type</code> 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 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 toolbar view by setting attribute <code>type</code> 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 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 name="Creating_custom_views">Creating custom views</h2>
<p>If you need greater flexibility or to provide a customized appearance for your display of Places information, you can create a custom view to do so.</p>
<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>
<p>The <code>nsINavHistoryResultViewer</code> also has an observer interface to allow an <code>nsINavHistoryResultViewObserver</code> to observer changes. However, this observer interface is specifically for trees. The {{ Bug("337638") }} is for moving this to the <code>nsINavHistoryResultTreeViewer</code> object. Other implementors if <code>nsINavHistoryResultViewer</code> should use their own observers.</p>
<h2>The PlacesView interface</h2>
<p>Different kinds of views implement different interfaces.  But whatever the kind -- the built-in tree, menu, or toolbar view or one of your own -- a view should provide a minimal interface so that controllers have a consistent, general way to interact with it.  For this reason views implement the <a href="/en/The_PlacesView_interface" title="en/The_PlacesView_interface">PlacesView interface</a>.</p>
<h2>See also</h2>
<ul> <li><a href="/en/The_PlacesView_interface" title="en/The_PlacesView_interface">The PlacesView interface</a></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