Displaying Places information using views

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

このリビジョンの内容

{{Fx_minversion_header(3)}}

ビューは、Places model-view-controller デザインにおけるコンポーネントのひとつです。ユーザーに向けて{{ Interface("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 属性を使用します。

{{ gecko_callout_heading("2.0") }}

Gecko 2.0 {{ geckoRelease("2.0") }} 以降では、メニュービューにおいて places 属性を使用することができません。 詳しくはメニュービューを参照してください。

あなたは直接 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 の適切なメソッドによってビューに通知されます。通知を受けると、ビューは自身を更新します。

{{ gecko_callout_heading("2.0") }}

Gecko 2.0 {{ geckoRelease("2.0") }}以降では、nsINavHistoryResultViewer はさらに強力な {{ interface("nsINavHistoryResultObserver") }} によって置き換えられています。

ツリービュー

ツリー要素の 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" に設定すると、列中のどの行も {{ Interface("nsINavHistoryResultNode") }} の title プロパティを表示します。

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

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

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

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

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

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

ツリービューの使い方

ビルトインツリービューの使用の準備ができました。どうやって表示されるデータを扱うのでしょうか?

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

次に、ツリーについて話す場合において、"view" とは多くの意味を持つ言葉であるということを知っておいてください。この文書では Places ビューについて述べています。Places ビューはまさしく XUL 要素であり、上で述べたように、ビルトイン Places ツリービューは、type 属性に "places" が設定されたtree要素なのです。ですが、 Places ビューであるかどうかに関わらず、全てのツリーは nsITreeView を使用して、それぞれのデータを表示しているということを思い出してください。そのためビルトイン Places ツリービューは、 view の値に自分自身を持ちます。このビューは、最も一般的かつ最も具体的なものから順に、 {{ Interface("nsINavHistoryResultTreeViewer") }}、 {{ Interface("nsINavHistoryResultObserver") }} {{ gecko_minversion_inline("2.0") }}、そして {{ Interface("nsITreeView") }} といった3つのインターフェイスを実装したオブジェクトです。nsINavHistoryResultTreeViewer は、それぞれの行の索引と、それぞれの列中に含まれている {{ Interface("nsINavHistoryResultNode") }} を配置します。nsINavHistoryResultObserver は、基底データの変更があった際、その更新について観察しているクライアントに通知します。ですが、ここでの私たちの目的としてはあまり便利ではありません。通常、ツリーにおいての高レベルのインターフェイスは 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 true であれば、コンテナの内部を表示しません。 onOpenFlatContainer プロパティによってコンテナの開閉が設定されている際に機能します。 少なくとも flatList 及び showRoot の一方は false である必要があります。
onOpenFlatContainer string コンテナが開閉された際に呼び出されます。 flatList プロパティがtrueである場合のみ適用されます。 aContainerという名前の引数として {{ Interface("nsINavHistoryResultNode") }} を渡します。 aContainer に {{ Interface("nsINavHistoryContainerResultNode") }} を QueryInterfaceします。
showRoot boolean true であれば、ルートの {{ Interface("nsINavHistoryContainerResultNode") }} をツリー最初の列に表示します。 少なくとも showRoot 及び flatList の一方は 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);

PlacesTreeView

註: この節では、PlacesTreeView の実装の方法について述べています。

ビルトインツリービューの実態は PlacesTreeView のインスタンスであり、プロトタイプは {{ Source("browser/components/places/content/treeView.js") }} で定義されています。 PlacesTreeView の挙動はビルトインツリービューにおいて、 {{ Interface("nsITreeView") }}、及び Places ビューに必要な機能の多くを実装するという2つの機能をなします。後者においては具体的には {{ Interface("nsINavHistoryResultViewer") }} を継承した {{ Interface("nsINavHistoryResultObserver") }} {{ gecko_minversion_inline("2.0") }} が実装されています。

以下のように PlacesTreeView が2つの機能をなすために、検索結果とツリー要素の表示のブリッジを可能としています

var result = historyService.executeQuery(query, opts); // あなたのPlaces検索クエリ
var tree = document.getElementById("mytree");          // あなたのツリー要素

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

// ブリッジを行う
result.addObserver(view.QueryInterface(Components.interfaces.nsINavHistoryResultObserver));
tree.view = view.QueryInterface(Components.interfaces.nsITreeView);

実際にビルトインツリービューはこのように機能しています。上で述べたように、place プロパティを設定したり、load メソッドを呼び出した際も同様に機能しています。

ツリービューをあなた自身によって実装する場合は自由であるのですが、ビルトインツリービューを使用する場合において、place 属性の使用はあまり推奨されません。place 属性はビューの更新を回避してしまい、ビューの検索結果との同期に失敗する原因となってします。そのかわりにビューの load メソッドもしくは place プロパティを使用するようにしてください。もし、カスタムツリービューをあなた自身の手で実装した場合、いくつかの点で似たようなコードを記述する必要があります。

ポップアップメニューにおける Places ビューについての情報が記述されています。

{{ h3_gecko_minversion("Firefox 3.6 以前", "1.9.2", "In_Firefox_3.6_and_earlier") }}

(いくつかの menu 要素の子孫である)空の menupopup 要素の type 属性に "places" という値を設定することで、ビルトインメニュービューは生成されます。

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

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

{{ h3_gecko_minversion("Firefox 4", "2.0") }}

以下のようにすることで、ポップアップのように Places の情報を追加することができます。

<menu id="bookmarksMenu">
  <menupopup placespopup="true">
    onpopupshowing="if (!document.getElementById('bookmarksMenu')._placesView)
                        new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
  </menupopup>
</menu> 

メニュービューは {{ 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チュートリアルを参照してください。

ビューの使い方

Places ビューの使用の準備ができました。どうやって使用するのでしょうか?

複雑であるため、ビルトインツリービューは簡単に捜査できるように特別なインターフェイスを提供しています。もし、あなた自身の手で複雑なビューを実装した場合、似たようなインターフェイスも実装しているかもしれません。

ですが、矛盾無く一般的な操作を行えるように全ての Places ビューが最低限のインターフェイスを提供すべきです。この理由から、{{ Interface("nsIPlacesView") }} インターフェイスは実装されています。ビューを表示し、選択したノードを実行する {{ Interface("nsINavHistoryResult") }} インスタンスを取得すると言ったことが可能です。事実、上で述べらていた特殊な placeプロパティ は、このインターフェイスのプロパティです。

カスタムビューの作成

提供されているビルトインビュー以上の柔軟性を必要をする場合は、カスタムビューの作成が可能です。カスタムビューを必要とする場合は以下のようなものです。(もちろん、これだけに限定されるわけではありません)

  • ビルトインビューによって提供されるカラムのとなりに独自のカラムを表示したい。
  • ビルトインビューによる日付や他のデータのデータの表示方法を変更したい。
  • 基底部データに定義されていない情報の表示をしたい。
  • ビルトインビューで提供されていない要素によって Places の情報を表示したい。

潜在的に悪い理由があるとして、カスタムビューが推奨されないのは以下の場合です。(これもまた、これだけに限定されるわけではありませんが)

  • 新たなコンテンツを表示するというよりも、ビルトインビューの表面的な外見を変更したい場合。CSS を適用してください。
  • ビルトインツリービューの列を隠したい場合。treecol 要素を除外してください。
  • クリックやコマンド操作、他のユーザーの操作を受け取ったビューの挙動の管理をしたい場合。この不満を別の問題として捉えないでください。表示されているデータにとってのビューであり、ロジックにとっての操作方法です。あなたは標準的なPlacesのコマンドやコンテクストメニューをビルトインビューに接続したり、独自のコマンドを操作するためのコントローラーの作成が可能です。詳しくは、 View Controller を参照してください。

あなたが書いたビューの種類に関わらず、 {{ Interface("nsIPlacesView") }} インターフェイスを実装してください。(現在、 nsIPlacesView は実際にはインターフェイスではありません。組み込みビューは単純に、メソッド及び属性を直接実装しています) 『ビューの使い方』の章での前述のとおり、このインターフェイスは、ビューを扱う一般的な方法に矛盾しないコントローラや呼び出しを提供します。

あなたのビューにおいては、同様の実装を行うか、ビューを {{ Interface("nsINavHistoryResultObserver") }} のインスタンスとして保持してください。あなたの view と {{ Interface("nsINavHistoryResult") }} オブジェクトを接続するため、結果の addObserver() メソッドを呼び出し、 nsINavHistoryResultObserver のインスタンスである viewer オブジェクトを渡してください。もし、あなたの view が nsINavHistoryResultObserver を実装しているのであれば、 viewer オブジェクトを渡す事ができます。もし、あなたの view が nsINavHistoryResultObserver のインスタンスを内部に含んでいるならば、代わりにそれを渡すことができます。(3種類の組み込みビューもこのアプローチを採用しています) result オブジェクトはこのインスタンスを通じてあなたのviewと連絡を取り、基底データの変更の通知を受けた場合に表示を更新するのです。

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

// この myView とはあなたの view です。そして myView.viewer は nsINavHistoryResultObserver を実装したオブジェクトです。
result.addObserver(myView.viewer);

より本質的な意味において、上記コードは、あなたの view のメソッドへと送信されます。呼び出し元は検索結果または検索クエリを渡し、あなたは検索クエリを実行し、 addObserver() を使用して観察対象のクライアントとしてあなたの view を追加します。(ビルトインツリービューの load メソッド での処理手法です)

あなたの view が結果を観察した時、 result は与えられた nsINavHistoryResultObserverresult プロパティに設定されます。あなたは result プロパティを明確に設定するべきではありません。 result とビューの接続を解除するには、 あなたの結果から removeObserver(view) を呼び出してください。

循環参照に注意してください。ビューと result は、両方とも互いへの参照を持ちます。 JavaScript のガベージコレクションによってこれらのオブジェクトが解放されるためには、 result.removeObserver(view) を呼び出すことによって、この循環を消去する必要があります。(例えば、ビルトインツリービューでは自動的にこの作業を行っています。ツリーが破棄された、または別の {{ Interface("nsITreeView") }} とツリーが関連付けられた場合、ツリーは古い方の nsITreeViewtree プロパティを null に設定します。 nsITreeView を実装したオブジェクトは nsINavHistoryResultObserver も実装しているため、ビューは removeObserver() を呼び出し、 result から自分自身への接続を解除します。)

nsINavHistoryResultObserver が使用されている場合でも正確な内容でしょうか?

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

カスタムPlacesツリービューは多くのことができます。要求するアイディアを得るには、前述した『ツリービューの使い方』の章を参照してください。一般的に、カスタム nsITreeView は多くの作業を要します。カスタム nsITreeView に慣れ親しんでいない場合は、 XUL チュートリアルの Custom Tree ViewsTree View Details のページを参照してください。ここには関連情報について述べます。

幸運にも、あなたの目的に合致したビルトインツリービューの一部を拝借することが可能です。ビルトインビューである PlacesTreeView の実態である JavaScript プロトタイプは、特に役立つ {{ Interface("nsINavHistoryResultTreeViewer") }}、 {{ Interface("nsINavHistoryResultObserver") }}、 {{ Interface("nsITreeView") }} の 3 種を実装しています。そのため、非カスタム機能と退屈な作業を肩代わりさせるために、PlacesTreeView 上にあなたのカスタム機能を実装することができます。ひとつの方策としては、カスタム機能を提供するオブジェクトにこれらのインターフェイスを全て実装して、これらカスタムされた振る舞いを全てサポートすることで、カスタムオブジェクトを PlacesTreeView のインスタンスに偽装するというものがあります。ですが、おそらくより簡単な方策としては、下記のサンプルのように、既に存在する PlacesTreeView インスタンスを修正するものがあります。

下記の JavaScript では、新たに PlacesTreeView インスタンスを作成していますが、ビルトインツリービューに表示されていない列を表示するために、nsITreeView のメソッドを二つ上書きしています。

var view = new PlacesTreeView();

view._getCellText = view.getCellText;
view.getCellText = function (aRowIndex, aCol) {
  // 特殊な列を操作する。PlacesTreeViewとして、列のidまたはanonidを認識する。
  switch (aCol.id || aCol.element.getAttribute("anonid"))
  {
  // (ブックマークのような)URIノードではなく、(フォルダのような)すべてのノードのURI
  case "fullURI":
    return this.nodeForTreeIndex(aRowIndex).uri;
    break;
  // 親コンテナ内でのノードのindex
  case "indexInParent":
    return this.nodeForTreeIndex(aRowIndex).bookmarkIndex;
    break;
  // 偶数列か奇数列か
  case "parity":
    return (aRowIndex % 2 === 0 ? "even" : "odd");
    break;
  }
   // そうでない場合、オリジナルのgetCellTextメソッドに偽装する
  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":
    // ここで列の再ソートが可能
    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);

// result's viewerとカスタムビューのツリーのnsITreeViewの接続
var treeView = document.getElementById("myTreeView");
result.addObserver(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>

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

上記のツリー要素は、ビルトインビューの際の特殊な type 属性を持たないことを注記しておきます。上記のツリーはあなたが Places クエリと接続した通常のツリーであって、ビルトインツリービューのような便利なプロパティメソッドは存在しません。もし、カスタムビューがあなたのアプリケーションにおいてのみ使用されるのであれば、コードは上述したようなもので十分でしょう。上記のコードでは、PlacesTreeView オブジェクトを 1 つ作成し、修正を加え、通常のツリーに組み込むのに充分です。しかしながらもし、あなたのビューが幅広く使われるのであればビルトインビューのように PlacesTreeView に似通った JavaScript プロトタイプと XBL ツリーバインディングを作成し、ビューと表示と接続作成に多くの作業を費やすべきでしょう。

nsINavHistoryResultNode.viewIndex 属性は、ビューを使用することによって明示的に提供されます。この値はそれぞれのノードが生成されたときは -1 に初期化されています。あなたは、可視ノードの追跡を保存するためにこの値を使用します。 PlacesTreeView は、列中で有効になっているノードの index を保存するためにこの値を使用します。

関連記事

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

<div>
  {{Fx_minversion_header(3)}}</div>
<p>ビューは、Places <a class="external" href="http://ja.wikipedia.org/wiki/Model_View_Controller" title="http://ja.wikipedia.org/wiki/Model_View_Controller">model-view-controller</a> デザインにおけるコンポーネントのひとつです。ユーザーに向けて{{ Interface("nsINavHistoryResult") }}オブジェクトを表示するために使用します。Placesのクエリでは、<code>nsINavHistoryResult</code> オブジェクトについての情報を保持し、使用しています。<code>nsINavHistoryResult</code> オブジェクトの保持および扱い方については、<a href="/ja/docs/Querying_Places" title="Querying
Places">Querying Places</a> を参照してください。このページでは、既にこれらについて知っているものとして話を進めます。</p>
<p><code>nsINavHistoryResult</code> のインスタンスはビューのデータを提供します。ビューは、このインスタンスのルートである {{ Interface("nsINavHistoryContainerResultNode") }} を展開し、その時点で含んでいる{{ Interface("nsINavHistoryResultNode") }}オブジェクトを表示する機能を持ちます。</p>
<h2 id="The_built-in_views" 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 id="Instantiating_in_XUL" 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 id="Connecting_a_view_to_its_data" name="Connecting_a_view_to_its_data">ビューとデータの接続</h3>
<p>ビルトインビューとデータを接続するためには、ビューの特殊な<code>place</code> 属性を使用します。</p>
<div class="geckoVersionNote">
  <p>{{ gecko_callout_heading("2.0") }}</p>
  <p>Gecko 2.0 {{ geckoRelease("2.0") }} 以降では、メニュービューにおいて places 属性を使用することができません。 詳しくは<a href="/ja/docs/Displaying_Places_information_using_views#Menu_view" title="Displaying Places information using views#Menu view">メニュービュー</a>を参照してください。</p>
</div>
<p>あなたは直接 XUL 中の属性に値を指定するか、JavaScrpt を使用して対応するプロパティに値を設定しなければなりません。その値は、ビューに表示するデータがクエリの結果となるようなクエリ形式の URI にしてください。ビューが終了するまで変更されないような単純なクエリの場合は、XUL 中の<code>place</code> 属性に直接指定したほうが良いかもしれません。より複雑なクエリの場合、またはクエリの URI が変わる予定のある場合は、JavaScript を用いてビューの <code>place</code> プロパティに動的に値を設定したほうが良いでしょう。後者の場合では、要素の属性に <code>setAttribute</code> を用いて値を設定する手法では充分ではないということを記しておきます。代わりに要素の <code>place</code> プロパティを使用してください。クエリ URI の情報については、<a href="/ja/docs/Querying_Places#Serializing_queries" title="Querying Places">Querying Places</a> および <a href="/ja/docs/Places_query_URIs" title="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>
<div class="geckoVersionNote">
  <p>{{ gecko_callout_heading("2.0") }}</p>
  <p>Gecko 2.0 {{ geckoRelease("2.0") }}以降では、<code>nsINavHistoryResultViewer</code> はさらに強力な <code>{{ interface("nsINavHistoryResultObserver") }}</code> によって置き換えられています。</p>
</div>
<h3 id="Tree_view" name="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 href="/ja/docs/XUL/tree" title="XUL/Tree">tree</a> リファレンスと、<a href="/ja/docs/XUL_Tutorial/Trees" title="XUL
Tutorial/Trees">Trees</a> チュートリアルを参照してください。</p>
<p>もし、あなたのツリービューに Firefox において通常使われているスタイルを適用したい場合は以下のスタイルシートを読み込ませてください。注意すべきこととしては、このスタイルシートは<a href="#Instantiating">上述</a>の、ビルトイン Places ビューを使用する際に<em>読み込まなければならない</em>ものとは違います。下記のスタイルシートはオプションであり、スタイルとアイコンはビルトインツリービューにしか適用されません。</p>
<pre class="brush: xml">
&lt;?xml-stylesheet href="chrome://browser/skin/places/places.css" ?&gt;
</pre>
<p>XUL というよりも、JavaScript によるツリービューのインスタンス化の例は、<a href="/ja/docs/Places/Programmatic_generation_of_the_built-in_tree_view" title="Places/Programmatic generation of the built-in tree view">Programmatic generation of the built-in tree view</a>を参照してください。</p>
<h4 id="Column_binding" name="Column_binding">列のバインディング</h4>
<p><code>ビルトインツリービュー</code>は、あなたのツリーの特定の列と結果の特定のプロパティの接続を簡単に行えます。それは、あなたの <code>treecol</code> 要素の<code>id</code> 属性に与えられた魔法の値によって認識されています。検索結果のプロパティは、列の<code>id</code> 属性によって列と結び付けられています。例えば、列の <code>id</code> を "title" に設定すると、列中のどの行も {{ Interface("nsINavHistoryResultNode") }} の <code>title</code> プロパティを表示します。</p>
<p>下記の表は、列の <code>id</code> と値が、<code>nsINavHistoryResultNode</code> のプロパティのどれに関連付けられているかを示しています。</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</td>
      <td><code>uri</code></td>
    </tr>
    <tr>
      <td>date</td>
      <td><code>time</code></td>
    </tr>
    <tr>
      <td>visitCount</td>
      <td><code>accessCount</code></td>
    </tr>
    <tr>
      <td>keyword</td>
      <td>*</td>
    </tr>
    <tr>
      <td>description</td>
      <td>*</td>
    </tr>
    <tr>
      <td>dateAdded</td>
      <td><code>dateAdded</code></td>
    </tr>
    <tr>
      <td>lastModified</td>
      <td><code>lastModified</code></td>
    </tr>
    <tr>
      <td>tags</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 href="/ja/docs/XBL" title="XBL">XBL</a> などによる匿名コンテンツを含む場合に役立ちます。仮に <code>id</code> 属性と <code>anonid</code> 属性の両方を指定した場合、<code>anonid</code> 属性が使用されます。</p>
<p>ビルトインツリービューは一般的な使用の範囲においては便利な機能を提供しています。追加のデータを表示したい、またはビュー上において別の操作方法を必要とする場合は、あなた自身で実装を行う必要があります。下記の<a href="#Creating_custom_views">カスタムビューの作成</a>を参照してください。</p>
<h4 id="Using_the_tree_view" name="Using_the_tree_view">ツリービューの使い方</h4>
<p>ビルトインツリービューの使用の準備ができました。どうやって表示されるデータを扱うのでしょうか?</p>
<p>はじめに {{ Interface("nsIPlacesView") }} を参照してください。全ての Places ビューと同様に、ビルトインツリービューもこのインターフェイスを実装しており、ビューの {{Interface("nsINavHistoryResult")}} インスタンスを取得し、ビューの選択部を調べるための幅広い手段を提供しています。</p>
<p>次に、ツリーについて話す場合において、"view" とは多くの意味を持つ言葉であるということを知っておいてください。この文書では Places ビューについて述べています。Places ビューはまさしく XUL 要素であり、<a href="#Tree_view">上で述べたように</a>、ビルトイン Places ツリービューは、<code>type</code> 属性に "places" が設定されたtree要素なのです。ですが、 Places ビューであるかどうかに関わらず、全てのツリーは <a href="/ja/docs/nsITreeView"><code>nsITreeView</code></a> を使用して、それぞれのデータを表示しているということを思い出してください。そのためビルトイン Places ツリービューは、 view の値に自分自身を持ちます。このビューは、最も一般的かつ最も具体的なものから順に、 {{ Interface("nsINavHistoryResultTreeViewer") }}、 {{ Interface("nsINavHistoryResultObserver") }} {{ gecko_minversion_inline("2.0") }}、そして {{ Interface("nsITreeView") }} といった3つのインターフェイスを実装したオブジェクトです。<code>nsINavHistoryResultTreeViewer</code> は、それぞれの行の索引と、それぞれの列中に含まれている {{ Interface("nsINavHistoryResultNode") }} を配置します。<code>nsINavHistoryResultObserver</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>によって提供されているインターフェイスは非常に一般的なものであるのですが、ビューに直接実装されたメソッドとプロパティは非常に特殊なものです。時々、相互作用する4つの手段のうち、主に使用している手段とは別の手段の使用が必要となることがあります。</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 id="Convenience_methods" name="Convenience_methods">便利なメソッド</h4>
<p>ビルトインツリービューは幅広く使用されるものの複雑であるので、よく使われる処理を簡単に記述するために、いくつかの便利なメソッドがツリービューに直接実装されています。</p>
<div class="warning">
  <strong>警告:</strong> {{ Bug(476952) }} が修正されるまでは、 <code>place</code> 属性や <code>place</code> プロパティと併用して、これらのメソッドを使用することによって問題が発生するかもしれません。</div>
<h5 id="applyFilter()">applyFilter()</h5>
<p>特定の検索条件とフォルダに合致した新規クエリを読み込みます。</p>
<pre>
void applyFilter(
  string filterString,
  array folderRestrict
);</pre>
<h6 id=".E3.83.91.E3.83.A9.E3.83.A1.E3.83.BC.E3.82.BF">パラメータ</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 id="load()">load()</h5>
<p>ビューの表示のクエリを設定します。このメソッドは上で述べられているツリーの <code>place</code> プロパティを設定するための代わりの手段としても使われます。</p>
<pre>
void load(
  array queries,
  nsINavHistoryQueryOptions options
);</pre>
<h6 id=".E3.83.91.E3.83.A9.E3.83.A1.E3.83.BC.E3.82.BF">パラメータ</h6>
<dl>
  <dt>
    <code>queries</code></dt>
  <dd>
    {{ Interface("nsINavHistoryQuery") }} オブジェクトの配列。</dd>
  <dt>
    <code>options</code></dt>
  <dd>
    {{ Interface("nsINavHistoryQueryOptions") }} オブジェクト。</dd>
</dl>
<h5 id="selectItems()">selectItems()</h5>
<p>与えられたアイテムIDのそれぞれに合致した、ツリー中の最初のノードを選択します。選択されたアイテムを表示する必要があるため、選択されたアイテムの親ノードを展開します。</p>
<pre>
void selectItems(
  array aIDs,
  array aOpenContainers
);
</pre>
<h6 id=".E3.83.91.E3.83.A9.E3.83.A1.E3.83.BC.E3.82.BF">パラメータ</h6>
<dl>
  <dt>
    <code>aIDs</code></dt>
  <dd>
    アイテムIDの配列。</dd>
  <dt>
    <code>aOpenContainers</code></dt>
  <dd>
    真または未定義の場合、閉じられたフォルダも同様に検索されます。そうでない場合、閉じられたフォルダは検索されません。省略可。</dd>
</dl>
<h5 id="selectNode()">selectNode()</h5>
<p>ツリー中で選択された特定のノードの祖先である全てのコンテナが展開され、選択されたノードが見えるようになります。</p>
<pre>
void selectNode(
  nsINavHistoryNode node
);</pre>
<h6 id=".E3.83.91.E3.83.A9.E3.83.A1.E3.83.BC.E3.82.BF">パラメータ</h6>
<dl>
  <dt>
    <code>node</code></dt>
  <dd>
    選択された {{ Interface("nsINavHistoryResultNode") }}</dd>
</dl>
<h5 id="selectPlaceURI()">selectPlaceURI()</h5>
<p>ツリー中で、特定の placeURI に一致するノードが選択され、そのノードの祖先である全てのコンテナが展開されることにより、選択されたノードが見えるようになります。</p>
<pre>
void selectPlaceURI(
  string placeURI
);</pre>
<h6 id=".E3.83.91.E3.83.A9.E3.83.A1.E3.83.BC.E3.82.BF">パラメータ</h6>
<dl>
  <dt>
    <code>placeURI</code></dt>
  <dd>
    選択する {{ Interface("nsINavHistoryResultNode") }} の URI(文字列型)</dd>
</dl>
<h4 id="Convenience_properties" name="Convenience_properties">便利なプロパティ</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><code>true</code> であれば、コンテナの内部を表示しません。 <code>onOpenFlatContainer</code> プロパティによってコンテナの開閉が設定されている際に機能します。 少なくとも <code>flatList</code> 及び <code>showRoot</code> の一方は <code>false</code> である必要があります。</td>
    </tr>
    <tr>
      <td><code>onOpenFlatContainer</code></td>
      <td><code>string</code></td>
      <td>コンテナが開閉された際に呼び出されます。 <code>flatList</code> プロパティがtrueである場合のみ適用されます。 <code>aContainer</code>という名前の引数として {{ Interface("nsINavHistoryResultNode") }} を渡します。 <code>aContainer</code> に {{ Interface("nsINavHistoryContainerResultNode") }} を <code>QueryInterface</code>します。</td>
    </tr>
    <tr>
      <td><code>showRoot</code></td>
      <td><code>boolean</code></td>
      <td><code>true</code> であれば、ルートの {{ Interface("nsINavHistoryContainerResultNode") }} をツリー最初の列に表示します。 少なくとも <code>showRoot</code> 及び <code>flatList</code> の一方は <code>false</code> である必要があります。</td>
    </tr>
  </tbody>
</table>
<h4 id="Example_uses" name="Example_uses">使用例</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">
var treeView = document.getElementById("myPlacesTreeView");
var rowIndex = treeView.view.treeIndexForNode(historyResultNode);</pre>
<p>ツリー中で特定のURIを持つ行を選択する。</p>
<pre class="brush: js">
var treeView = document.getElementById("myPlacesTreeView");
treeView.selectPlaceURI("some place URI");</pre>
<p>ツリー中で特定の <code>nsINavHistoryResultNode</code> を含む列を選択する。</p>
<pre class="brush: js">
var treeView = document.getElementById("myPlacesTreeView");
treeView.selectNode(someHistoryResultNode);</pre>
<h4 id="PlacesTreeView" name="PlacesTreeView">PlacesTreeView</h4>
<div class="note">
  <strong>註:</strong> この節では、PlacesTreeView の実装の方法について述べています。</div>
<p>ビルトインツリービューの実態は <code>PlacesTreeView</code> のインスタンスであり、プロトタイプは {{ Source("browser/components/places/content/treeView.js") }} で定義されています。 <code>PlacesTreeView</code> の挙動はビルトインツリービューにおいて、 {{ Interface("nsITreeView") }}、及び Places ビューに必要な機能の多くを実装するという2つの機能をなします。後者においては具体的には {{ Interface("nsINavHistoryResultViewer") }} を継承した {{ Interface("nsINavHistoryResultObserver") }} {{ gecko_minversion_inline("2.0") }} が実装されています。</p>
<p>以下のように PlacesTreeView が2つの機能をなすために、検索結果とツリー要素の表示のブリッジを可能としています</p>
<pre class="brush: js">
var result = historyService.executeQuery(query, opts); // あなたのPlaces検索クエリ
var tree = document.getElementById("mytree");          // あなたのツリー要素

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

// ブリッジを行う
result.addObserver(view.QueryInterface(Components.interfaces.nsINavHistoryResultObserver));
tree.view = view.QueryInterface(Components.interfaces.nsITreeView);</pre>
<p>実際にビルトインツリービューはこのように機能しています。上で述べたように、<code>place</code> プロパティを設定したり、<code>load</code> メソッドを呼び出した際も同様に機能しています。</p>
<p>ツリービューをあなた自身によって実装する場合は自由であるのですが、ビルトインツリービューを使用する場合において、<code>place</code> 属性の使用はあまり推奨されません。<code>place</code> 属性はビューの更新を回避してしまい、ビューの検索結果との同期に失敗する原因となってします。そのかわりにビューの <code>load</code> メソッドもしくは <code>place</code> プロパティを使用するようにしてください。もし、<a href="#Creating_custom_views">カスタムツリービュー</a>をあなた自身の手で実装した場合、いくつかの点で似たようなコードを記述する必要があります。</p>
<h3 id="Menu_view" name="Menu_view">メニュービュー</h3>
<p>ポップアップメニューにおける Places ビューについての情報が記述されています。</p>
<div>
  {{ h3_gecko_minversion("Firefox 3.6 以前", "1.9.2", "In_Firefox_3.6_and_earlier") }}</div>
<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>
<div>
  {{ h3_gecko_minversion("Firefox 4", "2.0") }}</div>
<p>以下のようにすることで、ポップアップのように Places の情報を追加することができます。</p>
<pre class="brush: xml">
&lt;menu id="bookmarksMenu"&gt;
  &lt;menupopup placespopup="true"&gt;
    onpopupshowing="if (!document.getElementById('bookmarksMenu')._placesView)
                        new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
  &lt;/menupopup&gt;
&lt;/menu&gt; 
</pre>
<p>メニュービューは {{ Source("browser/components/places/content/menu.xml") }} で実装されています。メニューについての一般的な情報は <a href="/ja/docs/XUL/menupopup" title="XUL/Menupopup"><code>menupopup</code></a> と <a href="/ja/docs/XUL/menu" title="XUL/Menu"><code>menu</code></a> のリファレンス及び <a href="/ja/docs/XUL_Tutorial/Popup_Menus" title="XUL Tutorial/Popup Menus"><code>Popup Menus</code></a> チュートリアルを参照してください。</p>
<h3 id="Toolbar_view" name="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 href="/ja/docs/XUL/toolbaritem" title="XUL/Toolbaritem"><code>toolbaritem</code></a> と <a href="/ja/docs/XUL/toolbar" title="XUL/Toolbar"><code>toolbar</code></a> のリファレンス及び <a href="/ja/docs/XUL_Tutorial/Toolbars" title="XUL_Tutorial/Toolbars"><code>Toolbars</code></a>チュートリアルを参照してください。</p>
<h2 id="Using_a_view" name="Using_a_view">ビューの使い方</h2>
<p>Places ビューの使用の準備ができました。どうやって使用するのでしょうか?</p>
<p>複雑であるため、<a href="#Using_the_tree_view" title="#Using the tree view">ビルトインツリービュー</a>は簡単に捜査できるように特別なインターフェイスを提供しています。もし、あなた自身の手で複雑なビューを実装した場合、似たようなインターフェイスも実装しているかもしれません。</p>
<p>ですが、矛盾無く一般的な操作を行えるように全ての Places ビューが最低限のインターフェイスを提供すべきです。この理由から、{{ Interface("nsIPlacesView") }} インターフェイスは実装されています。ビューを表示し、選択したノードを実行する {{ Interface("nsINavHistoryResult") }} インスタンスを取得すると言ったことが可能です。事実、上で述べらていた特殊な <a href="#Connecting_a_view_to_its_data" title="#Connecting a view to its data"><code>place</code>プロパティ</a> は、このインターフェイスのプロパティです。</p>
<h2 id="Creating_custom_views" name="Creating_custom_views">カスタムビューの作成</h2>
<p>提供されているビルトインビュー以上の柔軟性を必要をする場合は、カスタムビューの作成が可能です。カスタムビューを必要とする場合は以下のようなものです。(もちろん、これだけに限定されるわけではありません)</p>
<ul>
  <li>ビルトインビューによって提供されるカラムのとなりに独自のカラムを表示したい。</li>
  <li>ビルトインビューによる日付や他のデータのデータの表示方法を変更したい。</li>
  <li>基底部データに定義されていない情報の表示をしたい。</li>
  <li>ビルトインビューで提供されていない要素によって Places の情報を表示したい。</li>
</ul>
<p>潜在的に悪い理由があるとして、カスタムビューが推奨されないのは以下の場合です。(これもまた、これだけに限定されるわけではありませんが)</p>
<ul>
  <li>新たなコンテンツを表示するというよりも、ビルトインビューの表面的な外見を変更したい場合。CSS を適用してください。</li>
  <li>ビルトインツリービューの列を隠したい場合。<code>treecol</code> 要素を除外してください。</li>
  <li>クリックやコマンド操作、他のユーザーの操作を受け取ったビューの挙動の管理をしたい場合。この不満を別の問題として捉えないでください。表示されているデータにとってのビューであり、ロジックにとっての操作方法です。あなたは標準的なPlacesのコマンドやコンテクストメニューをビルトインビューに接続したり、独自のコマンドを操作するためのコントローラーの作成が可能です。詳しくは、 <a href="/ja/docs/Places/View_Controller" title="Places/View Controller">View Controller</a> を参照してください。</li>
</ul>
<p>あなたが書いたビューの種類に関わらず、 {{ Interface("nsIPlacesView") }} インターフェイスを実装してください。(現在、 <code>nsIPlacesView</code> は実際にはインターフェイスではありません。組み込みビューは単純に、メソッド及び属性を直接実装しています) 『<a href="#Using_a_view" title="#Using a view">ビューの使い方</a>』の章での前述のとおり、このインターフェイスは、ビューを扱う一般的な方法に矛盾しないコントローラや呼び出しを提供します。</p>
<p>あなたのビューにおいては、同様の実装を行うか、ビューを {{ Interface("nsINavHistoryResultObserver") }} のインスタンスとして保持してください。あなたの view と {{ Interface("nsINavHistoryResult") }} オブジェクトを接続するため、結果の <code>addObserver()</code> メソッドを呼び出し、 <code>nsINavHistoryResultObserver</code> のインスタンスである <code>viewer</code> オブジェクトを渡してください。もし、あなたの view が <code>nsINavHistoryResultObserver</code> を実装しているのであれば、 <code>viewer</code> オブジェクトを渡す事ができます。もし、あなたの view が <code>nsINavHistoryResultObserver</code> のインスタンスを内部に含んでいるならば、代わりにそれを渡すことができます。(3種類の組み込みビューもこのアプローチを採用しています) result オブジェクトはこのインスタンスを通じてあなたのviewと連絡を取り、基底データの変更の通知を受けた場合に表示を更新するのです。</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);

// この myView とはあなたの view です。そして myView.viewer は nsINavHistoryResultObserver を実装したオブジェクトです。
result.addObserver(myView.viewer);</pre>
<p>より本質的な意味において、上記コードは、あなたの view のメソッドへと送信されます。呼び出し元は検索結果または検索クエリを渡し、あなたは検索クエリを実行し、 <code>addObserver()</code> を使用して観察対象のクライアントとしてあなたの view を追加します。(ビルトインツリービューの <a href="#load()" title="#load()"><code>load</code> メソッド</a> での処理手法です)</p>
<p>あなたの view が結果を観察した時、 result は与えられた <code>nsINavHistoryResultObserver</code> の <code>result</code> プロパティに設定されます。<em>あなたは <code>result</code> プロパティを明確に設定するべきではありません。</em> result とビューの接続を解除するには、 あなたの結果から <code>removeObserver(view)</code> を呼び出してください。</p>
<p>循環参照に注意してください。ビューと result は、両方とも互いへの参照を持ちます。 JavaScript のガベージコレクションによってこれらのオブジェクトが解放されるためには、 <code>result.removeObserver(view)</code> を呼び出すことによって、この循環を消去する必要があります。(例えば、ビルトインツリービューでは自動的にこの作業を行っています。ツリーが破棄された、または別の {{ Interface("nsITreeView") }} とツリーが関連付けられた場合、ツリーは古い方の <code>nsITreeView</code> の <code>tree</code> プロパティを <code>null</code> に設定します。 <code>nsITreeView</code> を実装したオブジェクトは <code>nsINavHistoryResultObserver</code> も実装しているため、ビューは <code>removeObserver()</code> を呼び出し、 result から自分自身への接続を解除します。)</p>
<div class="note">
  <code>nsINavHistoryResultObserver</code> が使用されている場合でも正確な内容でしょうか?</div>
<h3 id="Creating_custom_views" name="Creating_custom_views">カスタムツリービューの作成</h3>
<p>カスタムPlacesツリービューは多くのことができます。要求するアイディアを得るには、前述した『<a href="#Using_the_tree_view" title="#Using the tree view">ツリービューの使い方</a>』の章を参照してください。一般的に、カスタム <code>nsITreeView</code> は多くの作業を要します。カスタム <code>nsITreeView</code> に慣れ親しんでいない場合は、 XUL チュートリアルの <a href="/ja/docs/XUL_Tutorial/Custom_Tree_Views" title="XUL Tutorial/Custom Tree Views">Custom Tree Views</a> と <a href="/ja/docs/XUL_Tutorial/Tree_View_Details" title="XUL Tutorial/Tree View Details">Tree View Details</a> のページを参照してください。ここには関連情報について述べます。</p>
<p>幸運にも、あなたの目的に合致したビルトインツリービューの一部を拝借することが可能です。ビルトインビューである <a href="#PlacesTreeView"><code>PlacesTreeView</code></a> の実態である JavaScript プロトタイプは、特に役立つ {{ Interface("nsINavHistoryResultTreeViewer") }}、 {{ Interface("nsINavHistoryResultObserver") }}、 {{ Interface("nsITreeView") }} の 3 種を実装しています。そのため、非カスタム機能と退屈な作業を肩代わりさせるために、<code>PlacesTreeView</code> 上にあなたのカスタム機能を実装することができます。ひとつの方策としては、カスタム機能を提供するオブジェクトにこれらのインターフェイスを全て実装して、これらカスタムされた振る舞いを全てサポートすることで、カスタムオブジェクトを <code>PlacesTreeView</code> のインスタンスに偽装するというものがあります。ですが、おそらくより簡単な方策としては、下記のサンプルのように、既に存在する <code>PlacesTreeView</code> インスタンスを修正するものがあります。</p>
<p>下記の JavaScript では、新たに <code>PlacesTreeView</code> インスタンスを作成していますが、ビルトインツリービューに表示されていない列を表示するために、<code>nsITreeView</code> のメソッドを二つ上書きしています。</p>
<pre class="brush: js">
var view = new PlacesTreeView();

view._getCellText = view.getCellText;
view.getCellText = function (aRowIndex, aCol) {
  // 特殊な列を操作する。PlacesTreeViewとして、列のidまたはanonidを認識する。
  switch (aCol.id || aCol.element.getAttribute("anonid"))
  {
  // (ブックマークのような)URIノードではなく、(フォルダのような)すべてのノードのURI
  case "fullURI":
    return this.nodeForTreeIndex(aRowIndex).uri;
    break;
  // 親コンテナ内でのノードのindex
  case "indexInParent":
    return this.nodeForTreeIndex(aRowIndex).bookmarkIndex;
    break;
  // 偶数列か奇数列か
  case "parity":
    return (aRowIndex % 2 === 0 ? "even" : "odd");
    break;
  }
   // そうでない場合、オリジナルのgetCellTextメソッドに偽装する
  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":
    // ここで列の再ソートが可能
    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);

// result's viewerとカスタムビューのツリーのnsITreeViewの接続
var treeView = document.getElementById("myTreeView");
result.addObserver(view);
treeView.view = view;
</pre>
<p>下記の XUL では、JavaScript 中から参照される <code>myTreeView</code> 要素を定義しています。</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>このページ下部の<a href="#page-files">ファイル</a>の項目に、コードの全文が挙げられています。</p>
<p>上記のツリー要素は、ビルトインビューの際の特殊な <code>type</code> 属性を持たないことを注記しておきます。上記のツリーはあなたが Places クエリと接続した通常のツリーであって、ビルトインツリービューのような便利な<a href="#Convenience_properties" title="#Convenience properties">プロパティ</a>や<a href="#Convenience_methods" title="#Convenience methods">メソッド</a>は存在しません。もし、カスタムビューがあなたのアプリケーションにおいてのみ使用されるのであれば、コードは上述したようなもので十分でしょう。上記のコードでは、<code>PlacesTreeView</code> オブジェクトを 1 つ作成し、修正を加え、通常のツリーに組み込むのに充分です。しかしながらもし、あなたのビューが幅広く使われるのであればビルトインビューのように <code>PlacesTreeView</code> に似通った JavaScript プロトタイプと <a href="/ja/docs/XBL" title="XBL">XBL</a> ツリーバインディングを作成し、ビューと表示と接続作成に多くの作業を費やすべきでしょう。</p>
<p><code>nsINavHistoryResultNode.viewIndex</code> 属性は、ビューを使用することによって明示的に提供されます。この値はそれぞれのノードが生成されたときは <code>-1</code> に初期化されています。あなたは、可視ノードの追跡を保存するためにこの値を使用します。 <code>PlacesTreeView</code> は、列中で有効になっているノードの index を保存するためにこの値を使用します。</p>
<h2 id="See_also" name="See_also">関連記事</h2>
<ul>
  <li>{{ Interface("nsIPlacesView") }}</li>
  <li><a href="/ja/docs/Querying_Places" title="Querying Places">Querying Places</a></li>
  <li><a href="/ja/docs/Places/View_Controller" title="Places/View Controller">View Controller</a></li>
</ul>
Revert to this revision