Applet作成
GRINEditアプレット作成中。元のGRINEditはSWTなので org.eclipse.swt.graphics.GC に対して描画を行っています。しかしアプレットはSwingで作ることにしたので、java.awt.Graphics に対して描画を行います。renderという関数を作ったとしても受け取る引数が異なるからSWTとSwingの両方をサポートするのは無理だ…と思っていたのですが、描画を担当するオブジェクトをVertexに持たせてデリゲーションすればいいだけのような気がしてきました。今はRenderableVertexのサブクラス化で描画のタイプの異なる頂点を作っていますが、RenderableVertexにaddRendererって感じのメソッドがあればいいだけかもしれませんね。
addRendererじゃなくてsetRendererにしとこう。一つの頂点が複数のレンダラーを持つなんて機能は必要なかろう。
あー、そうか、CircleVertexはdiameterを持ち、BoxVertexはtextを持つのか。頂点の種類によって必要とする値が異なるわけですね。全部を親のRenderableVertexに持たせるのはきなくさい。解決方法は簡単なのが一つあるけど、それを選択する前にちょっと考えてみよう。そもそも継承ではなく委譲でポリモルフィズムするメリットはなんなのでしょうか。
継承ならフィールドを追加するのも自然にできます。自分のrenderメソッドが自分のフィールドを読むだけだから自然です。委譲なら、Rendererが個別のVertexの属性を持つのはおかしいし、抽象的なVertexクラスに具体的なフィールドをつけるのもおかしいので、僕が思いつく一番まっとうな解決法はVertexクラスにpublic Hashtable propertiesをつけるなんてのになります。プロパティへのアクセスのたびに型キャストが必要です。
renderメソッドの引数をObjectにしておいてSWTのGCでもAWTのGraphicsでも受け取れるようにした上で、リフレクションで分岐すればいいだけですし。
まぁ、RenderableVertexのrenderメソッドはObjectを受け取ってthis.rendererに委譲する用にしておいても、継承したらどうせrenderを上書きしてしまうんだから支障はないですね。
RenderableGraphのdrawOnBackgroundはダブルバッファリングを管理しているオブジェクトを受け取っているけど、呼び出しているところは1カ所なのでそこで描画対象を取り出して渡してやればRenderableGraphは単にVertexとEdgeに中継するだけになりますね。
後輩にEclipseの設定の仕方を教えたりしつつ、とりあえずアプレット上で頂点が100個あるグラフの表示はできました。ダブルバッファリングつき。後は肝心のマウス周りですね。
マウスイベントのハンドリングは基本的には、MouseListenerとMouseMoveListenerをimplementsしたMouseMediatorが、受け取ったイベントを具体的なクラスに渡す設計になっています。その渡す対象がどれか、というのをMouseMediatorが持つことで「右や左のボタンの意味」を簡単に切り替えられるようになっています。さて…、今はorg.eclipse.swt.events.MouseEventを投げ渡していますけど、これをx,yの座標だけ渡すように抽象化してしまえばいいですね。MouseMediatorだけがSWTとAWTの両方に対応し、具体的なMouseOperationクラスには座標の値だけが渡る、と。
MouseOperationのdrawメソッド内でシングルトンのDoubleBuffererにアクセスしていますねぇ。これは範囲選択のドラッグ時の枠を書くためなのですが…。ダブルバッファリングを使いたくないシチュエーションってあるかなぁ…。ああ、すでに自前のダブルバッファリングを持っている場合とかはこちらが用意したDoubleBuffererを使いたくないかも知れないですね。じゃぁ、ここもやっぱり描画対象をObject型で取得してリフレクションで分岐する方針で。
10KBのソースコードが8KBに縮みました(笑)
とりあえずマウス操作周りをSWTから切り離したところでCVSにコミットしておくとしますか。
さてと、次はMouseMediatorをAWTにも対応させる必要がありますね。
できました。でもアプレットビューワーでは動くけどブラウザで見るにはどうしたらいいのだっけ…。
うーん。
if(target.getClass() == Graphics.class ||
target.getClass() == SunGraphics2D.class){
このコードが「java.security.AccessControlException: access denied (java.lang.RuntimePermission accessClassInPackage.sun.java2d)」って感じにセキュリティモデルに引っかかるみたいで動きません。アプレットビューワーでなら動くんだけどなぁ…。やっぱりリフレクションで分岐するんじゃなくて委譲するようにするべきだったのかな…。いや委譲しないにしてもせめてSWT用とAWT用のクラスを分離すべきなのか。 SWTまわりのクラスがインポート宣言に含まれているせいで、実際には使われないのに「クラスがロードできない!」と怒られ、結局アプレットにswt.jarもつけてサイズがふくれあがってしまったり。やっぱりアプレットは何かと難しいなぁ。