13 Feb 2010

XULのtextboxのオートコンプリート 【追記あり】

XULのtextboxには"Toolkit autocomplete"という仕組みがあります.

textbox (Toolkit autocomplete) - Mozilla | MDN

これを使うと, 入力履歴からの自動補完などが簡単に実現できます. しかし, フォームの入力履歴からの補完で少しつまってしまったので, ここにメモしときます.

基本

type="autocomplete" autocompletesearch="history"/>

こんな風に, type属性に"autocomplete"と指定してあげるだけで自動補完してくれるようになります. autocompletesearch属性には, どの情報から補完するかを指定します. 指定できるのは以下の3種類のようです.

  • history: ロケーションバーのurl入力履歴から補完します.
  • form-history: フォームの入力履歴から補完します.
  • file: ファイル名から補完します. ローカルのファイルからのようです.

スペース区切りで複数指定もできます. これらとは別に独自のデータから補完させたい場合は, XPCOMのコンポーネントを書かないといけません. 詳しくは下のチュートリアルをどうぞ.

How to implement a custom autocomplete search component - Mozilla | MDN

form-historyの使い方

autocompletesearchが"history"の場合は, 本当にtextboxの属性を加えるだけでokなんですが, form-historyの場合は少しjsの方にも手を加える必要があります.

まずはxulの方の説明から. "type", "autocompletesearch"に加えて"autocompletesearchparam"という属性も追加します. これはフォームを識別するラベルのようなもののようです.

id="my-form" type="autocomplete" autocompletesearch="form-history" autocompletesearchparam="my-form-history"/>

次にjsのコード. "Components.interfaces.nsIFormHistory2"というコンポーネントのaddEntry()というメソッドに, 先ほどのautocompletesearchparamで指定した名前と追加したい文字を渡します. こうすることで, 渡した文字列が履歴として保存されます. textboxのonkeypressやフォームのsubmitボタンなどに, このコードを呼び出す関数を渡しておくと, フォームにテキストが入力される度にそのテキストが履歴として保存されていきます. これだけで補完してくれるようになります.

function addFormHistory() {
  var entry = document.getElementById('my-form').value;
  var formHistory = Components.classes["@mozilla.org/satchel/form-history;1"]
    .getService(Components.interfaces.nsIFormHistory2 || Components.interfaces.nsIFormHistory);
  formHistory.addEntry("my-form-history", entry);
}

こちらのコード例も参考にしてください.

xul - Save drop-down history in a Firefox Toolbar - Stack Overflow

こちらのchaikaという2chブラウザのコードも参考にさせてもらいました.

Error 404 (Not Found)!!1

nsIFormHistory

(下の節に追記あり. あわせてお読みください. 2010-02-20)

一つ謎だったのが, nsIFormHistoryというコンポーネントです. 手元の環境(Firefox 3.6, Mac OSX)でこのコンポーネントを呼び出すと, 以下のエラーが返ってきました.

Error: Component returned failure code: 0x80570018 (NS_ERROR_XPC_BAD_IID) [nsIJSCID.getService]

代わりにnsIFormHistory2を呼び出すとうまく動作しました. よくわからないのですが, 上記のstackoverflowの解答で,

  .getService(Components.interfaces.nsIFormHistory2 || Components.interfaces.nsIFormHistory);

と両方に対応できるようにしていたので, 現状これを真似しています.

追記(2010-02-20)

"nsIFormHistory"は現在もう無いそうです.

MXR is retired

Firefox1.5までは"nsIFormHistory"だったのですが, Firefox2.0より"nsIFormHistory2"に変わったようです.

mozilla1.8 mozilla/toolkit/components/satchel/public/nsIFormHistory.idl (Firefox2.0)

mozilla1.8.0 mozilla/toolkit/components/satchel/public/nsIFormHistory.idl (Firefox1.5)

というわけで, Firefox2.0以降に対応するだけだったら, nsIFormHistory2にしておけば大丈夫のようです.

  var formHistory = Components.classes["@mozilla.org/satchel/form-history;1"]
    .getService(Components.interfaces.nsIFormHistory2);

コメントにてid:piro_orさんに教えていただきました. ありがとうございます!


参考リンク