作成したviewエレメントを選択すると、[プロパティ]ビューで設定を編集することができます。classプロパティには既定値が設定されています。拡張ポイント<org.eclipse.ui.views>のドキュメントを見てみましょう。
注:class 属性の値は、org.eclipse.ui.IViewPartをインプリメントするクラスの完全修飾名でなければなりません。新規ビューを開発する場合は、org.eclipse.ui.part.ViewPartをサブクラス化する方法が一般的です。
これに従い、この拡張ビューを実装するクラスを用意しましょう。classプロパティを選択して右端の[...]ボタンをクリックし、開いたウィザードで[新規Javaクラスの生成]を選択し[終了]をクリックします(画面10)。
画面10 [新規Javaクラスの生成]を選択し[終了]をクリック
こうすることで、「ViewPart1」という、ViewPartクラスを継承したクラススケルトンが生成されます。
画面11 エディタ上にスケルトンが生成されたところ
このスケルトンには、以下の3つのメソッドが提供されています。これらを具体的に実装することで、プラグインの機能を実現していくわけです。
スケルトンの内容 |
package org.ocharake.matobaa.analogClock;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.ViewPart;
/**
* TODO: "ViewPart1" の説明を指定します。
* @see ViewPart
*/
public class ViewPart1 extends ViewPart {
/**
* TODO: "ViewPart1" コンストラクターをインプリメントします。
*/
public ViewPart1() {
}
/**
* TODO: "createPartControl" をインプリメントします。
* @see ViewPart#createPartControl
*/
public void createPartControl(Composite parent) {
}
/**
* TODO: "setFocus" をインプリメントします。
* @see ViewPart#setFocus
*/
public void setFocus() {
}
} |
それではスケルトンに実装を追加していきましょう。実装例(ViewPart1.java)と併せて読み進めてください。
ビューが生成されたときにまず実行されるのは<createPartControl(Composite)メソッド>です。ここで、SWTの<Canvas>を1つ貼り付けておきます。このcanvasに、自分自身を<PaintListener>として<add>しておきます。
/** canvasを貼り、1秒ごとに再描画するスレッドを起動する */
/* ビューを生成したときに呼ばれる。 */
public void createPartControl(Composite parent) {
canvas = new Canvas(parent,SWT.NONE);
canvas.addPaintListener(this);
updateThread = new Thread(this);
updateThread.start();
} |
描画は、PaintListenerクラスのpaintControl()メソッドをオーバーライドすることで実装していきます。現在時刻を基に、時針、分針、秒針を描画すればよいでしょう。
/** アナログ時計を描く。*/
public void paintControl(PaintEvent e) {
// 現在時刻を取得し、針の角度を算出する
Calendar now = new GregorianCalendar(Locale.getDefault());
double hour = (double)now.get(Calendar.HOUR) / 12 * 2 * Math.PI;
double minutes = (double)now.get(Calendar.MINUTE) / 60 * 2 * Math.PI;
double seconds = (double)now.get(Calendar.SECOND) / 60 * 2 * Math.PI;
minutes += seconds / 60;
hour += minutes / 12;
// 外周を描く
Rectangle bounds = canvas.getBounds();
// 楕円を円に補正
if(bounds.height < bounds.width) {
bounds.x = (bounds.width - bounds.height) / 2;
bounds.width = bounds.height;
} else {
bounds.y = (bounds.height - bounds.width) / 2;
bounds.height = bounds.width;
}
e.gc.drawArc(bounds.x,bounds.y,bounds.width-1, bounds.height-1,0,360);
// 針を描く
Point center = new Point(bounds.x + bounds.width / 2,
bounds.y + bounds.height / 2);
e.gc.drawLine(center.x, center.y,
(int)(center.x + bounds.width / 4 * Math.sin(hour)),
(int)(center.y - bounds.height / 4 * Math.cos(hour)));
e.gc.drawLine(center.x, center.y,
(int)(center.x + bounds.width / 3 * Math.sin(minutes)),
(int)(center.y - bounds.height / 3 * Math.cos(minutes)));
e.gc.drawLine(
(int)(center.x + bounds.width / 2.5 * Math.sin(seconds)),
(int)(center.y - bounds.height / 2.5 * Math.cos(seconds)),
(int)(center.x + bounds.width / 2.1 * Math.sin(seconds)),
(int)(center.y - bounds.height / 2.1 * Math.cos(seconds)));
} |
周期的に再描画を行うためのスレッドを実装します。まず、自分自身にRunnableインターフェイスのrun()メソッドを実装しておきます。
/** 一秒に一回、再描画を行う。*/
/* UpdateThreadの実行ロジックである。*/
public void run() {
try {
Display display = canvas.getDisplay();
while (true) {
// displayにcanvas.redraw()を実行してもらう
display.asyncExec(new Runnable() {
public void run() {
if(!canvas.isDisposed()) canvas.redraw();
}});
Thread.sleep(1000);
}
} catch (InterruptedException ignore) { // 割り込みを受けたら終了する
}
} |
そして、createPartControl()メソッド内で、自分自身を引数としたスレッドを新たに作って走らせておきましょう。
/** canvasを貼り、1秒ごとに再描画するスレッドを起動する */
/* ビューを生成したときに呼ばれる。 */
public void createPartControl(Composite parent) {
canvas = new Canvas(parent,SWT.NONE);
canvas.addPaintListener(this);
updateThread = new Thread(this);
updateThread.start();
} |
run()メソッドの中ではcanvasの再描画を行えばいいわけですが、非UIスレッドからは直接描画することができないので、<スレッド化の問題>に記述されている要領で、Display#asyncExec()を経由してcanvas.redraw()してもらうようにします。
/** 一秒に一回、再描画を行う。*/
/* UpdateThreadの実行ロジックである。*/
public void run() {
try {
Display display = canvas.getDisplay();
while (true) {
// displayにcanvas.redraw()を実行してもらう
display.asyncExec(new Runnable() {
public void run() {
if(!canvas.isDisposed()) canvas.redraw();
}});
Thread.sleep(1000);
}
} catch (InterruptedException ignore) { // 割り込みを受けたら終了する
}
} |
最後に、このビューが破棄されるときに、再描画用のスレッドを停止する処理を実装すれば、一通りの完成となります。
さて、一通り完成したところで実行してみましょう。Eclipseのプラグイン開発環境は、開発したプラグインをその場で実行してみる機能を備えています。[実行]メニューの[次を実行]から、[ランタイムワークベンチ](読者は以前からこのメニューが気になっていたのではないでしょうか?)を選択してみてください(画面12)。
画面12 [実行]メニューの[次を実行]から、[ランタイムワークベンチ]を実行
しばらくすると、新しいEclipseワークベンチが起動します。これが、ランタイムワークベンチという、プラグインのテストを行うための実行環境です。新しく起動したワークベンチでアナログクロックプラグインを実行してみます。[ウィンドウ]メニューから[ビューの表示]→[その他]を選択します(画面13)。
画面13 新しく起動したEclipseワークベンチで[ビューの表示]→[その他]を選択
すると[ビューの表示]ダイアログが開きます。[その他]ツリーを展開すると、org.ocharake.matobaa.analogClock.view1というビューが登録されています(画面14)。
画面14 org.ocharake.matobaa.analogClock.view1というビューが登録されている
これをクリックすれば、ランタイムワークベンチにアナログ時計が表示されるはずです。うまく動きましたか?
画面15 1秒に1回秒針が動くアナログ時計が表示される
さて、これだけではプラグインの機能としては少し寂しいですね。また、このままではプラグインとして再配布することができません。次回は、アクションを拡張とプラグインの国際化(日本語化)を行い、プラグインとして配布可能なようにプラグインのパッケージ化を行います。
的場 聡弘(まとば あきひろ;matobaa)
現在、株式会社NTTデータ ビジネス開発事業本部に所属。社内技術支援業務に携わり、主にJ2SEおよびJ2EEを用いたシステム構築に係わる方式設計や障害対応を担当している。おちゃらけプログラマ七号機(http://www.ocharake.org/)や、ぱ〜む脱力ゲーム協会(http://palmgames.tripod.co.jp/)の公認脱力作家という顔も持つ。
岡本 隆史(おかもと たかし)
NTTデータ 技術開発本部 所属。Debian GNU/Linuxの優れたメンテナンス性と他のディストリビューションを圧倒するパッケージ数に引かれDebianを使い始めたのをきっかけに、Debian プロジェクトの開発者となりJavaサポートの強化を行う。『Jakartaプロジェクト徹底攻略』(技術評論社)、『WEB+DB PRESS』(技術評論社)、『Java World』(IDGジャパン)、『JAVA Developer』(ソフトバンクパブリッシング)などで執筆活動を行っている。