Displaying Places information using views

  • リビジョンの URL スラッグ: Displaying_Places_information_using_views
  • リビジョンのタイトル: Displaying Places information using views
  • リビジョンの ID: 96079
  • 作成日:
  • 作成者: saneyuki
  • 現行リビジョン いいえ
  • コメント no wording changes

このリビジョンの内容

{{ 翻訳中() }} {{ Firefox3() }}

ビューは、Placesの構造を視覚的に操作するための要素のひとつです。ユーザーに向けてnsINavHistoryResultオブジェクトを表示するために使用します。  Placesのクエリでは、nsINavHistoryResult オブジェクトについての情報を保持し、使用しています。nsINavHistoryResultオブジェクトの使い方と保持の仕方については、Querying Placesを参照してください。このページでは、既にこれらについて知っているものとして話を進めます。

nsINavHistoryResult インスタンスはビューのデータを提供します。ビューは、このインスタンスの{{ Interface("nsINavHistoryContainerResultNode") }}を展開し、その時点で含んでいる{{ Interface("nsINavHistoryResultNode") }}オブジェクトを表示する機能を持ちます。

組み込みのビュー

もし、あなたの拡張機能やアプリケーションでブックマークまたは履歴の内容を表示する必要がある場合、あなたは組み込みのPlacesビューを使用することができます。それらを使用することで、基礎的な箇所を記述する作業時間を抑え、あなたの拡張機能もしくはアプリケーションの構築に注力することができるでしょう。

Placesでは以下の組み込みのビューが使用できます。

インスタンス化

3つの組み込みビューは、単純かつ標準的なXUL要素に特殊なtype属性の値として"places"を設定します。

いかなるXULドキュメントも、組み込みビューを含む場合、スタイルシート({{ Source("browser/components/places/content/places.css") }})を読み込み、ファイル({{ 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" ?>

このスタイルシートが、特殊なtype属性を持った要素に、ビューの一つをバインディングします。オーバーレイはビューによって要求されるJavaScriptを含んでいます。また、組み込みのコンテクストメニュー及びコマンドも含んでおり、ビューを利用する際に、有利な点が得られるかもしれません。

ビューとデータの接続

組み込みビューとデータを接続するために、ビューの特殊なplace属性を使用します。あなたは直接XUL中の属性に値を指定するか、JavaScrptを使用して対応するプロパティに値を設定してください。その値はビューに表示するデータがクエリの結果となるようなクエリのURIにしてください。ビューが終了するまで変更されないような単純なクエリの場合は、XUL中のplace属性に直接指定したほうが良いかもしれません。より複雑なクエリの場合、またはクエリのURIが変わる予定のある場合は、JavaScriptを用いてビューのplaceプロパティに、動的に値を設定したほうが良いでしょう。後者の場合では、要素の属性にsetAttributeを用いて値を設定する手法では十分ではないということを記して起きます。代わりに要素のplaceプロパティを使用してください。クエリURIの情報については、Querying PlacesおよびPlaces query URIsを参照してください。

下記の例では、タイトルまたはURLに"mozilla"を含むブックマークを表示するために、組み込みのツリービューを使用しています。XULはXMLであり、クエリURI中のいかなるアンパサンドも単純な&ではなく、実体参照の&amp;で記述されなければならないということを思い出してください。

<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>

次の例では、最終的な表示は同じですが、JavaScriptを用いてツリーのplace属性の値を設定しています。

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;

これら2つの例では組み込みのツリービューを使用していますが、重要なのはplace属性とplaceプロパティの使用についての実演という点です。組み込みのメニューやツールバーにおいても違いはないと考えてください。

基本的なデータの変更のとき、ビューは自動で更新され、新たなデータを表示します。この更新は、論理的なビューと結果の違いによって処理されます。すべてのPlacesビューの部品、または{{ Interface("nsINavHistoryResultViewer") }}インターフェイスのインスタンスにおいて、ビューと結果の間には相互作用する点が存在します。検索結果はPlacesの変更を受け取り、もしPlacesの変更がデータの明確な変更がなされたと確定された検索結果は、nsINavHistoryResultViewerの適切なメソッドによってビューに通知されます。通知を受けると、ビューは自身を更新します。

ツリービュー

 ツリー要素のtype属性に"places"が設定されることによって組み込みのツリービューは生成されます。treechildrenは空要素にしてください。

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

ツリービューは{{ Source("browser/components/places/content/tree.xml") }}で実装されています。ツリーについての一般的な情報は、treeリファレンスと、Treesチュートリアルを参照してください。

もし、あなたのツリービューに、Firefoxにおいて通常使われているスタイルを適用したい場合は、以下のスタイルシートを読み込ませてください。注意点として、このスタイルシートは上述の、組み込みのPlacesビューに読み込まれたものとは違います。下記のスタイルシートはオプションであり、スタイルとアイコンは、組み込みのツリービューにしか適用されません。

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

XULというよりも、JavaScriptによるツリービューのインスタンス化の例は、Programmatic generation of the built-in tree viewを参照してください。

カラムのバインディング

組み込みのツリービューでは、treecol要素のid属性に特定の値を設定することによって、検索結果の特定のプロパティと、ツリービューの特定の列の接続を行えます。検索結果のプロパティは、列のid属性によって、列と結び付けられています。例えば、列のidを"title"に設定すると、列中のどの行もnsINavHistoryResultNode  のtitleプロパティを表示します。

下記の表は、列のidと値が、{{ Interface("nsINavHistoryResultNode") }}のプロパティのどれに関連付けられているかを示しています。

treecol id または anonid 対応する nsINavHistoryResultNode プロパティ
title title
url
uri
date
time
visitCount
accessCount
keyword *
description *
dateAdded
dateAdded
lastModified
lastModified
tags
tags
** icon

*keyword とdescriptionは、nsINavHistoryResultNode のitemIdプロパティを用いてPlacesデータベースから検索されます。

**title列は自動的に nsINavHistoryResultNode iconプロパティによって参照されているfaviconを受け取ります。(ただしtitle列に限ります)

あなたのツリーが、十分な数の列を持っているならば、複数の列に上記の特殊な値を設定することも可能です。また、treecolid属性を設定する代わりに、anonid 属性を設定することも可能です。anonid属性の設定は、id属性を別の目的に使用するとき、またはtreecolが、XBLなどによる匿名コンテンツを含む場合に役立ちます。id属性とanonid属性の両方を指定した場合、anonid属性が使用されます。

組み込みのツリービューは、一般的な使用の範囲においては便利な機能を提供しています。もし追加のデータを表示したい、またはビュー上において別の操作方法を必要とする場合は、あなた自身の手で実装を行う必要があります。下記のカスタムビューの作成を参照してください 。

ツリービューの使い方

組み込みのツリービューの使用の準備ができました。あなたはどのように表示されるデータを扱いますか?

はじめに、{{ Interface("nsIPlacesView") }}を参照してください。全てのPlacesビューと同様に、組み込みのツリービューもこのインターフェイスを実装しており、ビューの{{ Interface("nsINavHistoryResult") }}インスタンスを取得し、ビューの選択部を調べるための幅広い手段を提供しています。

第2に、ツリーについて話す場合において、"view"とは多くの意味を持つ言葉であるということを知っておいてください。この文書では、Placesビューについて述べています。 上で述べたように、組み込みのPlacesツリービューは、type属性に"places"が設定されたtree要素なのです。しかし、Placesビューであるかどうかに関わらず、全てのツリーは、nsITreeViewを使用して、それぞれのデータを表示しているということを思い出してください。そのため組み込みのPlacesツリービューは、viewの値に自分自身を持ちます。このビューは、{{ Interface("nsINavHistoryResultTreeViewer") }}、{{ Interface("nsINavHistoryResultViewer") }}、そして {{ Interface("nsITreeView") }}といった3つのインターフェイスを実装したオブジェクトです。nsINavHistoryResultTreeViewerはそれぞれの行のindexと、それぞれの列内のnsINavHistoryResultNodeを配置します。nsINavHistoryResultViewer は根本的なデータの変更があった際に、ビューを更新します。ですが、ここでの私たちの目的としてはあまり便利ではありません。一般的に、ツリーにおいての高レベルのインターフェイスは、nsITreeViewが提供しています。

そして、組み込みのツリービューは、便利なメソッドプロパティを実装しています。

そのため、組み込みのPlacesビューでは、相互に作用する4通りの手段が存在します。

  1. ビュー自身に直接実装された便利なメソッドプロパティ
  2. ビュー自身の{{ Interface("nsIPlacesView") }}インターフェイス。
  3. ビューがviewに持つ、{{ Interface("nsINavHistoryResultTreeViewer") }}インターフェイス。
  4. ビューがviewに持つ、{{ Interface("nsITreeView") }} インターフェイス。

nsITreeView によって提供されているインターフェイスは非常に一般的なものであるのですが、ビューに直接実装されたメソッドとプロパティは非常に特殊なものです。時々、相互に作用する4つの手段のうち、主に使用している手段とは別の手段を使用することが必要となることがあります。

JavaScriptでは以下のように、あなたのPlacesツリービューを参照するためにtreeViewという名前の変数を宣言してください。

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

手段1と手段2が、この変数に対して適用できます。

ツリービューのviewは、treeView.viewが持っているオブジェクトです。手段3と手段4をこのオブジェクトに適用できます。

var treeViewView = treeView.view;

便利なメソッド

組み込みのツリービューは幅広く使用されるものの複雑であるので、よく使われる処理を簡単に記述するために、いくつかの便利なメソッドがツリービューに直接実装されています。

警告: {{ Bug("476952") }} が修正されるまでは、 place属性やplaceプロパティと併用して、これらのメソッドを使用することによって問題が発生するかもしれません。
applyFilter()

特定の検索条件とフォルダに合致した新規クエリを読み込みます。

void applyFilter(
  string filterString,
  array folderRestrict
);
パラメータ
filterString
新規クエリのsearchTermsプロパティに設定される文字列。
folderRestrict
フォルダIDの配列に基づき、新規クエリのsetFolders関数が呼び出されます。省略可。
load()

ビューの表示のクエリを設定します。このメソッドは上で述べられているツリーのplaceプロパティを設定するための代わりの手段としても使われます。

void load(
  array queries,
  nsINavHistoryQueryOptions options
);
パラメータ
queries
{{ Interface("nsINavHistoryQuery") }} オブジェクトの配列。
options
{{ Interface("nsINavHistoryQueryOptions") }} オブジェクト。
selectItems()

与えられたアイテムIDのそれぞれに合致した、ツリー中の最初のノードを選択します。選択されたアイテムを表示する必要があるため、選択されたアイテムの親ノードを展開します。

void selectItems(
  array aIDs,
  array aOpenContainers
);
パラメータ
aIDs
アイテムIDの配列。
aOpenContainers
真または未定義の場合、閉じられたフォルダも同様に検索されます。そうでない場合、閉じられたフォルダは検索されません。省略可。
selectNode()

ツリー中で選択された特定のノードの祖先である全てのコンテナが展開され、選択されたノードが見えるようになります。

void selectNode(
  nsINavHistoryNode node
);
パラメータ
node
選択された{{ Interface("nsINavHistoryResultNode") }}。
selectPlaceURI()

ツリー中で、特定のplaceURIに一致するノードが選択され、そのノードの祖先である全てのコンテナが展開されることにより、選択されたノードが見えるようになります。

void selectPlaceURI(
  string placeURI
);
パラメータ
placeURI
選択する{{ Interface("nsINavHistoryResultNode") }}のURI(文字列型)。

 

便利なプロパティ

上述のメソッドのように、いくつかの便利なプロパティが組み込みのツリービューに直接実装されています。

警告: {{ Bug("476952") }} が修正されるまでは、 place属 性やplaceプロパティと併用して、これらのメソッドを使用することによって問題が発生するかもしれません。
プロパティ 詳細
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.

使用例

特定の行の{{ Interface("nsINavHistoryResultNode") }}を取得する。

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

特定の nsINavHistoryResultNodeの行のindexを取得する。

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

ツリー中で特定のURIを持つ行を選択する。

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

ツリー中で特定のnsINavHistoryResultNodeを含む列を選択する。

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

Placesツリービュー

註: この節では、Placesツリービューの実装の方法について述べています。

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要素の子孫である)空のmenupopup要素のtype属性に"places"という値を設定することで、組み込みのメニュービューは生成されます。

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

place属性またはplaceプロパティは、menupopup要素に設定してください。

メニュービューは{{ Source("browser/components/places/content/menu.xml") }}で実装されています。メニューについての一般的な情報はmenupopupmenuのリファレンス及びPopup Menusチュートリアルを参照してください。

ツールバービュー

toolbar要素の子孫であるいくつかのtoolbaritem要素の子孫である)空のhbox要素のtype属性に"places"という値を設定することで、組み込みのツールバービューは生成されます。

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

place属性またはplaceプロパティは、hbox要素に設定してください。

ツールバービューは{{ Source("browser/components/places/content/toolbar.xml") }}で実装されています。ツールバーについての一般的な情報はtoolbaritemtoolbarのリファレンス及びToolbarsチュートリアルを参照してください。

ビューの使い方

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.

カスタムビューの作成

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 can hook up a built-in view to the standard Places commands and context menu, or you can create a custom controller to handle custom commands.  See View Controller for more information.

Regardless of the kind of view you are writing, it should implement interface {{ Interface("nsIPlacesView") }}.  (nsIPlacesView is currently not a true interface; the built-in views simply implement its methods and attributes directly.)  As described above in Using a view, this interface provides controllers and other callers a consistent, general way to interact with views.

Your view should also either implement or own an instance of {{ Interface("nsINavHistoryResultViewer") }}.  To hook up your view to a {{ Interface("nsINavHistoryResult") }} object, you set the viewer property of that object to an instance of nsINavHistoryResultViewer.  If your view implements nsINavHistoryResultViewer, then you can set the viewer property to the view itself.  If your view contains an internal instance of nsINavHistoryResultViewer, you would set viewer to it instead.  (The three built-in views take this approach.)  The results object then communicates to your view via this instance, notifying it about changes in the underlying data so it can update its display.

var bmServ =
    Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"].
    getService(Components.interfaces.nsINavBookmarksService);
var histServ =
  Components.classes["@mozilla.org/browser/nav-history-service;1"].
  getService(Components.interfaces.nsINavHistoryService);

var opts = histServ.getNewQueryOptions();
var query = histServ.getNewQuery();
query.setFolders([bmServ.placesRoot], 1);
var result = histServ.executeQuery(query, opts);

// Here myView is your view, and myView.viewer is some object implementing nsINavHistoryResultViewer.  
result.viewer = myView.viewer;

More practically, a variation of the code above might be internal to a method on your view.  A caller would pass in a result or a query, and you would execute the query and set the result's viewer to your viewer.  (That's how the load method of the built-in tree view works.)  Similarly, your view's place property setter must generate a result from a query URI and then set the result's viewer.

When you set the viewer property of an nsINavHistoryResult, the result will in turn set the result property on the given nsINavHistoryResultViewerYou should not set the result property explicitly.  To disconnect a viewer from its result, set the nsINavHistoryResult's viewer property to null.  This will cause the nsINavHistoryResultViewer's result property to be set to null as well.

Be careful about reference cycles.  The viewer and the result both hold owning references to each other.  For these objects to be freed during JavaScript garbage collection, you must clear this cycle by setting result.viewer to null.  (For example, the built-in tree view does this automatically.  When the tree is destroyed or a different {{ Interface("nsITreeView") }} is associated with the tree, the tree will set the tree property of the old nsITreeView to null.  Because the object implementing nsITreeView also implements nsINavHistoryResultViewer, the viewer then disconnects itself from the result by setting the result's viewer property to null.)

カスタムツリービューの作成

Custom Places tree views can be a lot of work.  Take a look at Using the tree view above to get an idea of what's required.  Custom nsITreeViews in general are a lot of work, and if you are not familiar with creating custom nsITreeViews, see the Custom Tree Views and Tree View Details pages in the XUL tutorial.  All of that information is relevant here.

Fortunately you can commandeer parts of the built-in tree view to suit your purposes.  The JavaScript prototype backing the built-in view, PlacesTreeView, is especially useful since it implements all three of {{ Interface("nsINavHistoryResultTreeViewer") }}, {{ Interface("nsINavHistoryResultViewer") }}, and {{ Interface("nsITreeView") }}.  You can therefore implement your own custom functionality on top of PlacesTreeView while relying on it for non-custom functionality and much of the tedious work.  One strategy is to create your custom object(s) implementing each of these interfaces, support your custom behavior with them, but pass off everything else to a PlacesTreeView instance that you would own.  Perhaps an easier strategy is to modify an existing PlacesTreeView instance, which is what the following example does.

The following JavaScript creates a new PlacesTreeView instance but overrides two of its nsITreeView methods to display columns not present in the built-in tree view:

var view = new PlacesTreeView();

view._getCellText = view.getCellText;
view.getCellText = function (aRowIndex, aCol) {
  // Handle our special columns.  As with PlacesTreeView, we'll recognize
  // them by their id's or anonid's.
  switch (aCol.id || aCol.element.getAttribute("anonid"))
  {
  // URI for all nodes (like folders), not just URI nodes (like bookmarks)
  case "fullURI":
    return this.nodeForTreeIndex(aRowIndex).uri;
    break;
  // Index of node in parent container
  case "indexInParent":
    return this.nodeForTreeIndex(aRowIndex).bookmarkIndex;
    break;
  // Is the row even or odd?
  case "parity":
    return (aRowIndex % 2 === 0 ? "even" : "odd");
    break;
  }
  // Otherwise, pass off to the original getCellText method.
  return this._getCellText(aRowIndex, aCol);
};

view._cycleHeader = view.cycleHeader;
view.cycleHeader = function (aCol) {
  switch (aCol.id || aCol.element.getAttribute("anonid"))
  {
  case "fullURI":
  case "indexInParent":
  case "parity":
    // You might resort by column here.
    break;
  default:
    this._cycleHeader(aCol);
    break;
  }
};

// クエリの実行と検索結果の取得
var bmServ =
    Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"].
    getService(Components.interfaces.nsINavBookmarksService);
var histServ =
  Components.classes["@mozilla.org/browser/nav-history-service;1"].
  getService(Components.interfaces.nsINavHistoryService);
var opts = histServ.getNewQueryOptions();
var query = histServ.getNewQuery();
query.setFolders([bmServ.placesRoot], 1);
var result = histServ.executeQuery(query, opts);

// Hook up the result's viewer and the tree's nsITreeView to our custom view.
var treeView = document.getElementById("myTreeView");
result.viewer = view;
treeView.view = view;

下記のXULでは、JavaScript中から参照されmyTreeView要素を定義しています。

<tree id="myTreeView" flex="1">
  <treecols>
    <treecol id="title" label="title" flex="1" primary="true" />
    <splitter class="tree-splitter" />
    <treecol anonid="fullURI" label="fullURI" flex="1" />
    <splitter class="tree-splitter" />
    <treecol id="indexInParent" label="indexInParent" />
    <splitter class="tree-splitter" />
    <treecol id="parity" label="parity" />
  </treecols>
  <treechildren />
</tree>

このページ下部のファイルの項目に、コードの全文が挙げられています。

Note that the tree element above does not have the special type attribute common to the built-in views.  It's just a regular tree element that you've hooked up to a Places query, with none of the convenience properties or methods of the built-in Places tree view.  If your custom view is used only once in your application, code similar to the above will likely be sufficient; it's enough to create a single PlacesTreeView object, modify it, and attach it to a regular tree.  If your view is widely used, however, you may want to take a cue from the built-in tree view and create your own JavaScript prototype similar to PlacesTreeView and your own XBL tree binding so that much of the work of creating views and viewers and hooking them up to results is abstracted away.

The attribute nsINavHistoryResultNode.viewIndex is provided explicitly for the use of the view.  This value is initialized to -1 when each node is created. You can use this value to keep track of visible nodes.  PlacesTreeView uses this attribute to store the row index that the node is on.

関連記事

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

このリビジョンのソースコード

<p>{{ 翻訳中() }} {{ Firefox3() }}</p>
<p>ビューは、Placesの構造を視覚的に操作するための要素のひとつです。ユーザーに向けてnsINavHistoryResultオブジェクトを表示するために使用します。  Placesのクエリでは、nsINavHistoryResult オブジェクトについての情報を保持し、使用しています。<code>nsINavHistoryResultオブジェクトの使い方と保持の仕方については、</code><a class="internal" href="/ja/Querying_Places" title="ja/Querying
Places">Querying Places</a>を参照してください。このページでは<span style="font-family: monospace;">、既にこれら</span><code>について知っているものとして話を進めます。</code></p>
<p><code>nsINavHistoryResult </code>インスタンスはビューのデータを提供します。ビューは、このインスタンスの{{ Interface("nsINavHistoryContainerResultNode") }}を展開し、その時点で含んでいる{{ Interface("nsINavHistoryResultNode") }}オブジェクトを表示する機能を持ちます。</p>
<h2 name="The_built-in_views">組み込みのビュー</h2>
<p>もし、あなたの拡張機能やアプリケーションでブックマークまたは履歴の内容を表示する必要がある場合、あなたは組み込みのPlacesビューを使用することができます。それらを使用することで、基礎的な箇所を記述する作業時間を抑え、あなたの拡張機能もしくはアプリケーションの構築に注力することができるでしょう。</p>
<p>Placesでは以下の組み込みのビューが使用できます。</p>
<ul> <li><a href="#Tree_view">ツリー</a></li> <li><a href="#Menu_view">メニュー</a></li> <li><a href="#Toolbar_view">ツールバー</a></li>
</ul>
<h3 name="Instantiating_in_XUL">インスタンス化</h3>
<p>3つの組み込みビューは、単純かつ標準的なXUL要素に特殊な<code>type</code>属性の値として"places"を設定します。</p>
<p>いかなるXULドキュメントも、組み込みビューを含む場合、スタイルシート({{ Source("browser/components/places/content/places.css") }})を読み込み、ファイル({{ 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>このスタイルシートが、特殊な<code>type</code>属性を持った要素に、ビューの一つをバインディングします。オーバーレイはビューによって要求されるJavaScriptを含んでいます。また、組み込みのコンテクストメニュー及びコマンドも含んでおり、ビューを利用する際に、有利な点が得られるかもしれません。</p>
<h3>ビューとデータの接続</h3>
<p>組み込みビューとデータを接続するために、ビューの特殊な<code>place</code>属性を使用します。あなたは直接XUL中の属性に値を指定するか、JavaScrptを使用して対応するプロパティに値を設定してください。その値はビューに表示するデータがクエリの結果となるようなクエリのURIにしてください。ビューが終了するまで変更されないような単純なクエリの場合は、XUL中の<code>place</code>属性に直接指定したほうが良いかもしれません。より複雑なクエリの場合、またはクエリのURIが変わる予定のある場合は、JavaScriptを用いてビューの<code>place</code>プロパティに、動的に値を設定したほうが良いでしょう。後者の場合では、要素<span style="font-family: monospace;">の属性に</span><code>setAttribute</code>を用いて値を設定する手法では十分ではないということを記して起きます。代わりに要素の<code>place</code>プロパティを使用してください。クエリURIの情報については、<a class="internal" href="/ja/Querying_Places#Serializing_queries" title="ja/Querying Places">Querying Places</a>および<a class="internal" href="/ja/Places_query_URIs" title="ja/Places query URIs">Places query URIs</a>を参照してください。</p>
<p>下記の例では、タイトルまたはURLに"mozilla"を含むブックマークを表示するために、<a href="#Tree_view">組み込みのツリービュー</a>を使用しています。XULはXMLであり、クエリURI中のいかなるアンパサンドも単純な<code>&amp;</code>ではなく、実体参照の<code>&amp;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>次の例では、最終的な表示は同じですが、JavaScriptを用いてツリーの<code>place</code>属性の値を設定しています。</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>これら2つの例では組み込みのツリービューを使用していますが、重要なのは<code>place</code>属性と<code>place</code>プロパティの使用についての実演という点です。組み込みのメニューやツールバーにおいても違いはないと考えてください。</p>
<p>基本的なデータの変更のとき、ビューは自動で更新され、新たなデータを表示します。この更新は、論理的なビューと結果の違いによって処理されます。すべてのPlacesビューの部品、または{{ Interface("nsINavHistoryResultViewer") }}インターフェイスのインスタンスにおいて、ビューと結果の間には相互作用する点が存在します。検索結果はPlacesの変更を受け取り、もしPlacesの変更がデータの明確な変更がなされたと確定された検索結果は、<code>nsINavHistoryResultViewer</code>の適切なメソッドによってビューに通知されます。通知を受けると、ビューは自身を更新します。</p>
<h3 id="Tree_view">ツリービュー</h3>
<p> ツリー要素の<code>type</code>属性に"places"が設定されることによって組み込みのツリービューは生成されます。<code>treechildren</code>は空要素にしてください。</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>ツリービューは{{ Source("browser/components/places/content/tree.xml") }}で実装されています。ツリーについての一般的な情報は、<a class="internal" href="/ja/XUL/tree" title="ja/XUL/Tree">tree</a>リファレンスと、<a class="internal" href="/ja/XUL_Tutorial/Trees" title="ja/XUL
Tutorial/Trees">Trees</a>チュートリアルを参照してください。</p>
<p><span class="lang lang-en">もし、あなたのツリービューに、Firefoxにおいて通常使われているスタイルを適用したい場合は、以下のスタイルシートを読み込ませてください。注意点として、このスタイルシートは</span><span class="lang lang-en"><a href="#Instantiating">上述</a></span><span class="lang lang-en">の、組み込みのPlacesビューに読み込まれたものとは違います。下記のスタイルシートはオプションであり、スタイルとアイコンは、組み込みのツリービューにしか適用されません。</span></p>
<pre class="brush: xml"><span class="lang lang-en">&lt;?xml-stylesheet href="chrome://browser/skin/places/places.css" ?&gt;</span>
</pre>
<p>XULというよりも、JavaScriptによるツリービューのインスタンス化の例は、<a class="internal" href="/ja/Places/Programmatic_generation_of_the_built-in_tree_view" title="ja/Places/Programmatic generation of the built-in tree view">Programmatic generation of the built-in tree view</a>を参照してください。</p>
<h4>カラムのバインディング</h4>
<p>組み込みのツリービューでは、<code>treecol</code>要素の<code>id</code>属性に特定の値を設定することによって、検索結果の特定のプロパティと、ツリービューの特定の列の接続を行えます。検索結果のプロパティは、列の<code>id</code>属性によって、列と結び付けられています。例えば、列の<code>id</code>を"title"に設定すると、列中のどの行もnsINavHistoryResultNode  のtitleプロパティを表示します。</p>
<p>下記の表は、列の<code>id</code>と値が、{{ Interface("nsINavHistoryResultNode") }}のプロパティのどれに関連付けられているかを示しています。</p>
<table class="standard-table" style="margin-left: 40px;"> <tbody> <tr> <td class="header"><code>treecol</code> <code>id</code> または <code>anonid</code></td> <td class="header">対応する <code>nsINavHistoryResultNode</code> プロパティ</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 とdescriptionは、<code>nsINavHistoryResultNode</code> の<code>itemId</code>プロパティを用いてPlacesデータベースから検索されます。</p>
<p style="margin-left: 40px;">**title列は自動的に <code>nsINavHistoryResultNode </code>の <code>icon</code>プロパティによって参照されているfaviconを受け取ります。(ただしtitle列に限ります)</p>
<p>あなたのツリーが、十分な数の列を持っているならば、複数の列に上記の特殊な値を設定することも可能です。また、<code>treecol</code>に<code>id</code>属性を設定する代わりに、<code>anonid </code>属性を設定することも可能です。<code>anonid</code>属性の設定は、<code>id</code>属性を別の目的に使用するとき、または<code>treecol</code>が、<a class="internal" href="/ja/XBL" title="ja/XBL">XBL</a>などによる匿名コンテンツを含む場合に役立ちます。<code>id</code>属性と<code>anonid</code>属性の両方を指定した場合、<code>anonid</code>属性が使用されます。</p>
<p>組み込みのツリービューは、一般的な使用の範囲においては便利な機能を提供しています。もし追加のデータを表示したい、またはビュー上において別の操作方法を必要とする場合は、あなた自身の手で実装を行う必要があります。下記の<a href="#Creating_custom_views">カスタムビューの作成</a>を参照してください 。</p>
<h4>ツリービューの使い方</h4>
<p>組み込みのツリービューの使用の準備ができました。あなたはどのように表示されるデータを扱いますか?</p>
<p>はじめに、{{ Interface("nsIPlacesView") }}を参照してください。全てのPlacesビューと同様に、組み込みのツリービューもこのインターフェイスを実装しており、ビューの{{ Interface("nsINavHistoryResult") }}インスタンスを取得し、ビューの選択部を調べるための幅広い手段を提供しています。</p>
<p>第2に、ツリーについて話す場合において、"view"とは多くの意味を持つ言葉であるということを知っておいてください。この文書では、Placesビューについて述べています。 <a href="#Tree_view">上で述べたように</a>、組み込みのPlacesツリービューは、<code>type</code>属性に"places"が設定されたtree要素なのです。しかし、Placesビューであるかどうかに関わらず、全てのツリーは、<span class="lang lang-en"><code><a href="../../../../ja/nsITreeView" rel="internal">nsITreeView</a></code></span>を使用して、それぞれのデータを表示しているということを思い出してください。そのため組み込みのPlacesツリービューは、viewの値に自分自身を持ちます。このビューは、{{ Interface("nsINavHistoryResultTreeViewer") }}、{{ Interface("nsINavHistoryResultViewer") }}、そして {{ Interface("nsITreeView") }}といった3つのインターフェイスを実装したオブジェクトです。<code>nsINavHistoryResultTreeViewer</code>はそれぞれの行のindexと、それぞれの列内の<code>nsINavHistoryResultNode</code>を配置します。<code>nsINavHistoryResultViewer </code>は根本的なデータの変更があった際に、ビューを更新します。ですが、ここでの私たちの目的としてはあまり便利ではありません。一般的に、ツリーにおいての高レベルのインターフェイスは、<code>nsITreeView</code>が提供しています。</p>
<p>そして、組み込みのツリービューは、便利な<a href="#Convenience_methods">メソッド</a>と<a href="#Convenience_properties">プロパティ</a>を実装しています。</p>
<p>そのため、組み込みのPlacesビューでは、相互に作用する4通りの手段が存在します。</p>
<ol> <li>ビュー自身に直接実装された便利な<a href="#Convenience_methods">メソッド</a>と<a href="#Convenience_properties">プロパティ</a>。</li> <li>ビュー自身の{{ Interface("nsIPlacesView") }}インターフェイス。</li> <li>ビューがviewに持つ、{{ Interface("nsINavHistoryResultTreeViewer") }}インターフェイス。</li> <li>ビューがviewに持つ、{{ Interface("nsITreeView") }} インターフェイス。</li>
</ol>
<p><code>nsITreeView</code> <code><span style="font-family: Verdana,Tahoma,sans-serif;">によって提供されているインターフェイスは非常に一般的なものであるのですが、ビューに直接実装されたメソッドとプロパティは非常に特殊なものです。時々、相互に作用する4つの手段のうち、主に使用している手段とは別の手段を使用することが必要となることがあります。</span></code></p>
<p>JavaScriptでは以下のように、あなたのPlacesツリービューを参照するために<code>treeView</code>という名前の変数を宣言してください。</p>
<pre class="brush: js">var treeView = document.getElementById("myPlacesTreeView");
</pre>
<p>手段1と手段2が、この変数に対して適用できます。</p>
<p>ツリービューのviewは、<code>treeView.view</code>が持っているオブジェクトです。手段3と手段4をこのオブジェクトに適用できます。</p>
<pre class="brush: js">var treeViewView = treeView.view;
</pre>
<h4>便利なメソッド</h4>
<p>組み込みのツリービューは幅広く使用されるものの複雑であるので、よく使われる処理を簡単に記述するために、いくつかの便利なメソッドがツリービューに直接実装されています。</p>
<div class="warning"><strong>警告:</strong> {{ Bug("476952") }} が修正されるまでは、 <code>place</code>属性や<code>place</code>プロパティと併用して、これらのメソッドを使用することによって問題が発生するかもしれません。</div>
<h5>applyFilter()</h5>
<p>特定の検索条件とフォルダに合致した新規クエリを読み込みます。</p>
<pre>void applyFilter(
  string filterString,
  array folderRestrict
);
</pre>
<h6>パラメータ</h6>
<dl> <dt><code>filterString</code></dt> <dd>新規クエリの<code>searchTerms</code>プロパティに設定される文字列。</dd> <dt><code>folderRestrict</code></dt> <dd>フォルダIDの配列に基づき、新規クエリの<code>setFolders</code>関数が呼び出されます。省略可。</dd>
</dl>
<h5>load()</h5>
<p>ビューの表示のクエリを設定します。このメソッドは上で述べられているツリーの<code>place</code>プロパティを設定するための代わりの手段としても使われます。</p>
<pre>void load(
  array queries,
  nsINavHistoryQueryOptions options
);
</pre>
<h6>パラメータ</h6>
<dl> <dt><code>queries</code></dt> <dd>{{ Interface("nsINavHistoryQuery") }} オブジェクトの配列。</dd> <dt><code>options</code></dt> <dd>{{ Interface("nsINavHistoryQueryOptions") }} オブジェクト。</dd>
</dl>
<h5>selectItems()</h5>
<p>与えられたアイテムIDのそれぞれに合致した、ツリー中の最初のノードを選択します。選択されたアイテムを表示する必要があるため、選択されたアイテムの親ノードを展開します。</p>
<pre>void selectItems(
  array aIDs,
  array aOpenContainers
);
</pre>
<h6>パラメータ</h6>
<dl> <dt><code>aIDs</code></dt> <dd>アイテムIDの配列。</dd> <dt><code>aOpenContainers</code></dt> <dd>真または未定義の場合、閉じられたフォルダも同様に検索されます。そうでない場合、閉じられたフォルダは検索されません。省略可。</dd>
</dl>
<h5>selectNode()</h5>
<p>ツリー中で選択された特定のノードの祖先である全てのコンテナが展開され、選択されたノードが見えるようになります。</p>
<pre>void selectNode(
  nsINavHistoryNode node
);
</pre>
<h6>パラメータ</h6>
<dl> <dt><code>node</code></dt> <dd>選択された{{ Interface("nsINavHistoryResultNode") }}。</dd>
</dl>
<h5>selectPlaceURI()</h5>
<p>ツリー中で、特定のplaceURIに一致するノードが選択され、そのノードの祖先である全てのコンテナが展開されることにより、選択されたノードが見えるようになります。</p>
<pre>void selectPlaceURI(
  string placeURI
);
</pre>
<h6>パラメータ</h6>
<dl> <dt><code>placeURI</code></dt> <dd>選択する{{ Interface("nsINavHistoryResultNode") }}のURI(文字列型)。</dd>
</dl>
<p> </p>
<h4>便利なプロパティ</h4>
<p>上述のメソッドのように、いくつかの便利なプロパティが組み込みのツリービューに直接実装されています。</p>
<div class="warning"><strong>警告:</strong> {{ Bug("476952") }} が修正されるまでは、 <code>place</code>属 性や<code>place</code>プロパティと併用して、これらのメソッドを使用することによって問題が発生するかもしれません。</div>
<table class="standard-table"> <tbody> <tr> <td class="header">プロパティ</td> <td class="header">型</td> <td class="header">詳細</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>使用例</h4>
<p>特定の行の{{ Interface("nsINavHistoryResultNode") }}を取得する。</p>
<pre class="brush: js">var treeView = document.getElementById("myPlacesTreeView");
var rowIndex = 0;
var historyResultNode = treeView.view.nodeForTreeIndex(rowIndex);</pre>
<p>特定の <code>nsINavHistoryResultNode</code>の行のindexを取得する。</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>ツリー中で特定の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>ツリー中で特定の<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>Placesツリービュー</h4>
<div class="note"><strong>註:</strong> この節では、Placesツリービューの実装の方法について述べています。</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 <a href="#Creating_custom_views">custom tree view</a>, you will need to write similar code at some point.</p>
<h3 id="Menu_view">メニュービュー</h3>
<p>(いくつかの<code>menu</code>要素の子孫である)空の<code>menupopup</code>要素の<code>type</code>属性に"places"という値を設定することで、組み込みのメニュービューは生成されます。</p>
<pre class="brush: xml">&lt;menu&gt;
  &lt;menupopup type="places" /&gt;
&lt;/menu&gt;
</pre>
<p><code>place</code>属性または<code>place</code>プロパティは、<code>menupopup</code>要素に設定してください。</p>
<p>メニュービューは{{ Source("browser/components/places/content/menu.xml") }}で実装されています。メニューについての一般的な情報は<a class="internal" href="/ja/XUL/menupopup" title="ja/XUL/Menupopup">menupopup</a>と<a class="internal" href="/ja/XUL/menu" title="ja/XUL/Menu">menu</a>のリファレンス及び<a class="internal" href="/ja/XUL_Tutorial/Popup_Menus" title="ja/XUL Tutorial/Popup Menus">Popup Menus</a>チュートリアルを参照してください。</p>
<h3 id="Toolbar_view">ツールバービュー</h3>
<p>(<code>toolbar</code>要素の子孫であるいくつかの<code>toolbaritem</code>要素の子孫である)空の<code>hbox</code>要素の<code>type</code>属性に"places"という値を設定することで、組み込みのツールバービューは生成されます。</p>
<pre class="brush: xml">&lt;toolbar&gt;
  &lt;toolbaritem&gt;
    &lt;hbox type="places" /&gt;
  &lt;/toolbaritem&gt;
&lt;/toolbar&gt;
</pre>
<p><code>place</code>属性または<code>place</code>プロパティは、<code>hbox</code>要素に設定してください。</p>
<p>ツールバービューは{{ Source("browser/components/places/content/toolbar.xml") }}で実装されています。ツールバーについての一般的な情報は<a class="internal" href="/ja/XUL/toolbaritem" title="ja/XUL/Toolbaritem">toolbaritem</a>と<a class="internal" href="/ja/XUL/toolbar" title="ja/XUL/Toolbar">toolbar</a>のリファレンス及び<a href="/ja/XUL_Tutorial/Toolbars" title="ja/XUL_Tutorial/Toolbars">Toolbars</a>チュートリアルを参照してください。</p>
<h2>ビューの使い方</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">カスタムビューの作成</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 can hook up a built-in view to the standard Places commands and context menu, or you can create a custom controller to handle custom commands.  See <a class="internal" href="/ja/Places/View_Controller" title="ja/Places/View Controller">View Controller</a> for more information.</li>
</ul>
<p>Regardless of the kind of view you are writing, it should implement interface {{ Interface("nsIPlacesView") }}.  (<code>nsIPlacesView</code> is currently not a true interface; the built-in views simply implement its methods and attributes directly.)  As described above in <a href="#Using_a_view">Using a view</a>, this interface provides controllers and other callers a consistent, general way to interact with views.</p>
<p>Your view should also either implement or own an instance of {{ Interface("nsINavHistoryResultViewer") }}.  To hook up your view to a {{ Interface("nsINavHistoryResult") }} object, you set the <code>viewer</code> property of that object to an instance of <code>nsINavHistoryResultViewer</code>.  If your view implements <code>nsINavHistoryResultViewer</code>, then you can set the <code>viewer</code> property to the view itself.  If your view contains an internal instance of <code>nsINavHistoryResultViewer</code>, you would set <code>viewer</code> to it instead.  (The three built-in views take this approach.)  The results object then communicates to your view via this instance, notifying it about changes in the underlying data so it can update its display.</p>
<pre class="brush: js">var bmServ =
    Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"].
    getService(Components.interfaces.nsINavBookmarksService);
var histServ =
  Components.classes["@mozilla.org/browser/nav-history-service;1"].
  getService(Components.interfaces.nsINavHistoryService);

var opts = histServ.getNewQueryOptions();
var query = histServ.getNewQuery();
query.setFolders([bmServ.placesRoot], 1);
var result = histServ.executeQuery(query, opts);

// Here myView is your view, and myView.viewer is some object implementing nsINavHistoryResultViewer.  
result.viewer = myView.viewer;
</pre>
<p>More practically, a variation of the code above might be internal to a method on your view.  A caller would pass in a result or a query, and you would execute the query and set the result's <code>viewer</code> to your viewer.  (That's how the <a href="#load()">load method</a> of the built-in tree view works.)  Similarly, your view's <code>place</code> property setter must generate a result from a query URI and then set the result's <code>viewer</code>.</p>
<p>When you set the <code>viewer</code> property of an <code>nsINavHistoryResult</code>, the result will in turn set the <code>result</code> property on the given <code>nsINavHistoryResultViewer</code>.  <em>You should not set the <code>result</code> property explicitly.</em>  To disconnect a viewer from its result, set the <code>nsINavHistoryResult</code>'s <code>viewer</code> property to null.  This will cause the <code>nsINavHistoryResultViewer</code>'s <code>result</code> property to be set to null as well.</p>
<p>Be careful about reference cycles.  The viewer and the result both hold owning references to each other.  For these objects to be freed during JavaScript garbage collection, you must clear this cycle by setting <code>result.viewer</code> to <code>null</code>.  (For example, the built-in tree view does this automatically.  When the tree is destroyed or a different {{ Interface("nsITreeView") }} is associated with the tree, the tree will set the <code>tree</code> property of the old <code>nsITreeView</code> to null.  Because the object implementing <code>nsITreeView</code> also implements <code>nsINavHistoryResultViewer</code>, the viewer then disconnects itself from the result by setting the result's <code>viewer</code> property to null.)</p>
<h3>カスタムツリービューの作成</h3>
<p>Custom Places tree views can be a lot of work.  Take a look at <a href="#Using_the_tree_view">Using the tree view</a> above to get an idea of what's required.  Custom <code>nsITreeView</code>s in general are a lot of work, and if you are not familiar with creating custom <code>nsITreeView</code>s, see the <a class="internal" href="/ja/XUL_Tutorial/Custom_Tree_Views" title="ja/XUL Tutorial/Custom Tree Views">Custom Tree Views</a> and <a class="internal" href="/ja/XUL_Tutorial/Tree_View_Details" title="ja/XUL Tutorial/Tree View Details">Tree View Details</a> pages in the XUL tutorial.  All of that information is relevant here.</p>
<p>Fortunately you can commandeer parts of the built-in tree view to suit your purposes.  The JavaScript prototype backing the built-in view, <a href="#PlacesTreeView"><code>PlacesTreeView</code></a>, is especially useful since it implements all three of {{ Interface("nsINavHistoryResultTreeViewer") }}, {{ Interface("nsINavHistoryResultViewer") }}, and {{ Interface("nsITreeView") }}.  You can therefore implement your own custom functionality on top of <code>PlacesTreeView</code> while relying on it for non-custom functionality and much of the tedious work.  One strategy is to create your custom object(s) implementing each of these interfaces, support your custom behavior with them, but pass off everything else to a <code>PlacesTreeView</code> instance that you would own.  Perhaps an easier strategy is to modify an existing <code>PlacesTreeView</code> instance, which is what the following example does.</p>
<p>The following JavaScript creates a new <code>PlacesTreeView</code> instance but overrides two of its <code>nsITreeView</code> methods to display columns not present in the built-in tree view:</p>
<pre class="brush: js">var view = new PlacesTreeView();

view._getCellText = view.getCellText;
view.getCellText = function (aRowIndex, aCol) {
  // Handle our special columns.  As with PlacesTreeView, we'll recognize
  // them by their id's or anonid's.
  switch (aCol.id || aCol.element.getAttribute("anonid"))
  {
  // URI for all nodes (like folders), not just URI nodes (like bookmarks)
  case "fullURI":
    return this.nodeForTreeIndex(aRowIndex).uri;
    break;
  // Index of node in parent container
  case "indexInParent":
    return this.nodeForTreeIndex(aRowIndex).bookmarkIndex;
    break;
  // Is the row even or odd?
  case "parity":
    return (aRowIndex % 2 === 0 ? "even" : "odd");
    break;
  }
  // Otherwise, pass off to the original getCellText method.
  return this._getCellText(aRowIndex, aCol);
};

view._cycleHeader = view.cycleHeader;
view.cycleHeader = function (aCol) {
  switch (aCol.id || aCol.element.getAttribute("anonid"))
  {
  case "fullURI":
  case "indexInParent":
  case "parity":
    // You might resort by column here.
    break;
  default:
    this._cycleHeader(aCol);
    break;
  }
};

// クエリの実行と検索結果の取得
var bmServ =
    Components.classes["@mozilla.org/browser/nav-bookmarks-service;1"].
    getService(Components.interfaces.nsINavBookmarksService);
var histServ =
  Components.classes["@mozilla.org/browser/nav-history-service;1"].
  getService(Components.interfaces.nsINavHistoryService);
var opts = histServ.getNewQueryOptions();
var query = histServ.getNewQuery();
query.setFolders([bmServ.placesRoot], 1);
var result = histServ.executeQuery(query, opts);

// Hook up the result's viewer and the tree's nsITreeView to our custom view.
var treeView = document.getElementById("myTreeView");
result.viewer = view;
treeView.view = view;
</pre>
<p>下記のXULでは、JavaScript中から参照され<span style="font-family: monospace;">る</span><code>myTreeView</code><span style="font-family: monospace;">要素を定義しています。</span></p>
<pre class="brush: xml">&lt;tree id="myTreeView" flex="1"&gt;
  &lt;treecols&gt;
    &lt;treecol id="title" label="title" flex="1" primary="true" /&gt;
    &lt;splitter class="tree-splitter" /&gt;
    &lt;treecol anonid="fullURI" label="fullURI" flex="1" /&gt;
    &lt;splitter class="tree-splitter" /&gt;
    &lt;treecol id="indexInParent" label="indexInParent" /&gt;
    &lt;splitter class="tree-splitter" /&gt;
    &lt;treecol id="parity" label="parity" /&gt;
  &lt;/treecols&gt;
  &lt;treechildren /&gt;
&lt;/tree&gt;
</pre>
<p>このページ下部のファイルの項目に、コードの全文が挙げられています。</p>
<p>Note that the tree element above does not have the special <code>type</code> attribute common to the built-in views.  It's just a regular tree element that you've hooked up to a Places query, with none of the convenience <a href="#Convenience_properties">properties</a> or <a href="#Convenience_methods">methods</a> of the built-in Places tree view.  If your custom view is used only once in your application, code similar to the above will likely be sufficient; it's enough to create a single <code>PlacesTreeView</code> object, modify it, and attach it to a regular tree.  If your view is widely used, however, you may want to take a cue from the built-in tree view and create your own JavaScript prototype similar to <code>PlacesTreeView</code> and your own <a href="/ja/XBL" title="ja/XBL">XBL</a> tree binding so that much of the work of creating views and viewers and hooking them up to results is abstracted away.</p>
<p>The attribute <code>nsINavHistoryResultNode.viewIndex</code> is provided explicitly for the use of the view.  This value is initialized to <code>-1</code> when each node is created. You can use this value to keep track of visible nodes.  <code>PlacesTreeView</code> uses this attribute to store the row index that the node is on.</p>
<h2>関連記事</h2>
<ul> <li>{{ Interface("nsIPlacesView") }}</li> <li><a class="internal" href="/ja/Querying_Places" title="ja/Querying Places">Querying Places</a></li> <li><a class="internal" href="/ja/Places/View_Controller" title="ja/Places/View Controller">View Controller</a> </li>
</ul>
<p>{{ languages( { "en": "en/Displaying_Places_information_using_views" } ) }}</p>
Revert to this revision