batik入門記その6 〜イベントハンドリング編1〜

理想としては、

  1. ViewerPort上でマウスクリック
  2. ポイント上のElementでイベントFire
  3. Elementを使ってフガフガ…

ってのを追い求めていたのだけれど、一向にやり方が、というか出来るのかもよくわからない。Scriptingのドキュメントを見たら独自にbatikで定義したメソッドをJavaScriptから呼び出すというサンプルがありました。これだとSVGにonclickだとかのハンドリングを自分でパースするときに書き込まないといけないから、スマートじゃないしやりたい事とは違うんだけれど、最悪の場合を想定してこの方法で出来ることをまずは確認しておくことにする。

で早速サンプルをコピペしてさくっと動作確認するぞと思いきや、全然ダメダメじゃないですか…セミコロンついてなかったり、定義されていないデフォルトコンストラクタを呼び出していたり…これほんとに動くの?って感じ。とりあえずコンパイルエラーをなくしたのがこれ。

public class ExtendedRhinoInterpreter extends RhinoInterpreter {
    public ExtendedRhinoInterpreter(URL documentURL) {
        super(documentURL); // build RhinoInterpreter
        final String names = { "print" };
        try {
            getGlobalObject().
	            defineFunctionProperties(names, 
	                          ExtendedRhinoInterpreter.class,
                                  ScriptableObject.DONTENUM);
        } catch (PropertyException e) {
            throw new Error(e.getMessage());
        }
    }
    
    public static void print(Context cx, Scriptable thisObj,
                             Object args, Function funObj) {
        for (int i=0; i < args.length; i++) {
            if (i > 0)
                System.out.print(" ");
	    
            // Convert the arbitrary JavaScript value into 
            // a string form.
            String s = Context.toString(args[i]);
	    
            System.out.print(s);
        }
        System.out.println();
    }
}

”print”ってメソッドを定義してそいつをECMAScriptってJavaScriptのエンジンに登録する。こいつをSVGのScriptの中で呼んであげればその引数がコンソールに出力されるはず、…だよね。後はこいつのFactoryをこんな感じで作って、

public class ExtendedRhinoInterpreterFactory extends RhinoInterpreterFactory {
  public Interpreter createInterpreter(URL documentURL) {
     return new ExtendedRhinoInterpreter(documentURL);
  }
}

JSVGCanvasのサブクラスでcreateBridgeContext()をオーバーライドして、BridgeContextに上のFactoryを登録してあげればよい。

class CustomSVGCanvas extends JSVGCanvas {
  protected BridgeContext createBridgeContext() {
    BridgeContext ctx = super.createBridgeContext();
    ctx.getInterpreterPool().putInterpreterFactory(
        "text/ecmascript", new ExtendedRhinoInterpreterFactory());
    return ctx;
  }
}

んで、適当にSVGファイルを作ってあげて例えばマウスクリックでprintを呼び出す場合は、

 

としてやればよい。circleで描画された円の中でマウスをクリックするたびにコンソールにonclickと表示される。

ScriptingをSVGBrowserでAdobeのビューワと同じように処理していたけど、今まで何でどうやってるんだろ?って不思議に思わなかったのが不思議。Adobeの場合はIEプラグインだからIEJavaScriptエンジン使ってるんだろう。batikの場合はECMAScriptエンジン(js.jar)に処理を委譲してエンジンが勝手にScriptを解釈してくれている。この辺て昔JavaScript⇔Applet間の相互呼び出しでハマッてた頃に知ってたらなぁ…としみじみ。確かJava-Plugin使うとJavaScriptからAppletが呼び出せなくて苦労したような。

で、ここでふと思ったのは、onclickのイベント自体は何らかの形でbatikが拾ってくるはず。パースの際にどっかに持ってるのかどうかようわからんが、その辺を調べればbatik上からマウスクリックを直接とれてもいいんでないか?と。

これで最悪の場合の逃げ道も確保できたわけだし、たまには答えに向かって一直線につっぱしろうとせずに、遠回りしてみるのも…あっ、これがいわゆる急がば回れってことですな。うん、納得!

追記:
 ドキュメントを見たら”Scripting With Java”ってのがあったよ…きっとここに求めていたものが書かれているように思える。はぁ、今回はただの遠回りに終わったか。