[Eclipseプラグイン開発] Workbench関連


# 目的

- Eclipseプラグインに関連する知識を（自分的に）再整理したい
- 主にWorkbench関連について整理する
- 関連する主なプラグインは
    -  プラグインID : `org.eclipse.ui`
    -  プラグイン本体 : `PlatformUI`
- 環境は Eclipse 4.5 (3.2 以降ならあまり変わらないはず）

# 要素

Eclipseプラグインを作ると必ず見かける「IWorkbench なんとか」。
それぞれ何を示しているかの理解が重要な気がします。

| インタフェース名 | 意味 |
|:--|:--|
| IWorkbench | 作業台。 Eclipse インスタンスに必ず１つある。 Eclipse インスタンスそのものをさすと思えばよいかと。 |
| IWorkbenchWindow | ウィンドウ。 Eclipse の「画面」。 １インスタンスに複数画面を開ける。 |
| IWorkbenchPage | ページ。Eclipse 画面に表示しているページ。 ここにパースペクティブが描画されている。 １ウィンドウに０～１ページある。 |
| IWorkbenchPart | 画面部品。 ページ内に描画されているパーツ。エディタとビューがある。 |
| IEditorPart | エディタ。 IWorkbenchPart の子インタフェースでエディタをあらわす。 一般的にページの中央に表示されて、ソースやファイルなどの編集を行うエリア。 |
| IViewPart | ビュー。 IWorkbenchPartの子インタフェースでビューをあらわす。 一般的にエディタの周りに表示され、各種情報を表示する。 |

![workbench.png](https://qiita-image-store.s3.amazonaws.com/0/97350/09521d53-80b1-f459-be25-46a2c42234f7.png)

## IWorkbench

Eclipseのインスタンスが起動していれば、必ず１つ存在します。
ウィンドウや、エディタ・ビュー、その他の情報を取得する際の基点となることが多いです。

以下のように `PlatformUI#getWorkbench()` で取得できます。

```java
IWorkbench workbench = PlatformUI.getWorkbench();
```

他にも `AbstractUIPlugin#getWorkbench()` で取得できますが、中では  `PlatformUI#getWorkbench()` を呼び出しているだけです。

## IWorkbenchWindow

Eclipse で開いている画面をあらわすオブジェクトです。
通常、Eclipse を起動すると自動的に１画面が開かれます。
またメニューから "Window" → "New Window" とすれば、新しいウィンドウを開けます。
ウィンドウがすべて閉じれば、ワークベンチ（Eclipse）も終了します。
ただし、プラグインの動作タイミングによっては（起動中や終了中など）ウィンドウが１つも開いていないこともあると思います。

一般的に IWorkbench からアクティブウィンドウを取得することが多いです。
また、IWorkbench からすべてのウィンドウを取得することも可能です。

```java
IWorkbenchWindow activeWindow = workbench.getActiveWorkbenchWindow();
IWorkbenchWindow[] windows = workbench.getWorkbenchWindows();
```

その他、イベント情報からアクティブウィンドウを取得することもできる場合があります（以下の例だと `ExecutionEvent` から取得してます）。
イベント発生時のアクティブウィンドウと、処理時のアクティブウィンドウが異なる場合も考えられるため、こういった場合にはイベント情報から取得することが正しいのだと思います。

適当なハンドラーを作って（新しい Plug-in プロジェクトを作って、テンプレートとして "Hello, World command" を指定すれば簡単に作れると思います）、以下のように実装するとウィンドウのタイトルが取得できます。いくつかウィンドウを開いてから実行してみてください。

```java:SampleHandler.java
package com.exsample.plugin.handlers;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.handlers.HandlerUtil;

public class SampleHandler extends AbstractHandler {

	@Override
	public Object execute(ExecutionEvent event) throws ExecutionException {
		IWorkbench workbench = PlatformUI.getWorkbench();
		IWorkbenchWindow activeWindow = HandlerUtil.getActiveWorkbenchWindowChecked(event);
		Shell shell = activeWindow.getShell();

		IWorkbenchWindow[] windows = workbench.getWorkbenchWindows();
		StringBuilder buff = new StringBuilder();
		for (IWorkbenchWindow window : windows) {
			String title = window.getShell().getText();
			buff.append(title + "\n");
		}
		MessageDialog.openInformation(shell, "Windows", buff.toString());
		return null;
	}
}
```

## IWorkbenchPage

Eclipse 画面には、パースペクティブが表示されていると思います。
このパースペクティブが表示されている場所が「ページ」です。

各種 API を見ると、１画面に複数ページを持つことができるように思えます。
しかし `IWorkbenchWindow#openPage()` の Javadoc を見ると以下のように書いてあります。

> **Note:** Since release 2.0, a window is limited to contain at most one page. If a page exist in the window when this method is used, then another window is created for the new page. Callers are strongly recommended to use the `IWorkbench.showPerspective` APIs to programmatically show a perspective.

つまり「リリース2.0以降は、ウィンドウには最大１ページしか持てないよ。ページがあるときにこのメソッドを呼ぶと新しいウィンドウにページを開いちゃうんだ。パースペクティブの表示には別のメソッドを使うことを強く推奨するよ！」って感じでしょうか。

ページを取得する方法は、ほとんどの場合 `IWorkbenchWindow#getActivePage()` になると思います。
ただし、パースペクティブをすべて閉じた状態だとページが無いので、 `null` が返ってくるので注意が必要です。

## IWorkbenchPart

画面部品です。エディタとビューがあります。

画面部品は、遅延初期化されることがあります。
たとえばEclipse を起動した直後は、前回開いてたエディタやビューが開いた状態になっていると思います。
しかしエディタやビューが重ねて表示されている場合、一番上の物以外はまだ初期化されていないことがあります。そうしたものは、タブを切り替えて表示しようとしたときに初期化がされます。おそらくは操作感の向上が目的なんでしょうね。

`IWorkbenchPart` はまだ初期化されていないけど、画面には（タブだけ）表示されているため、代わりに `IWorkbenchPartReference` というものがあります（エディタ用、ビュー用に拡張されたインタフェースもあります）。
`IWorkbenchPartReference#getPart()` を true で呼び出せば、初期化されます。

以前、実際に経験したこととして、エディタのタブにあるアイコンを動的に切り替えるロジックを組んだとき、Eclipse起動直後のエディタが裏にある場合には plugin.xml で指定したデフォルトのアイコンが表示され、エディタをクリックするとその時初めてアイコンが動的に切り替わるという現象が起きました（覚書）。

### IEditorPart

エディタです。
ページから各種メソッドで取得することができます。

- `IWorkbenchPage#getActiveEditor()` ： アクティブなエディタを取得。 null もあり得る。
- `IWorkbenchPage#openEditor()` ： 入力情報を指定して処理できるエディタを開く
- `IWorkbenchPage#findEditor()` ： 入力情報を指定して処理できるエディタを探す
- `IWorkbenchPage#getEditors()` ： 非推奨
- `IWorkbenchPage#getEditorReferences()` ： エディタ参照を取得
- その他、いろいろ

ちなみに入力情報(IEditorInput)とは、エディタで扱う情報を抽象化したものです。
ほとんどの場合はファイルが対象だと思いますが、それ以外のものをエディタで開く（あるいは何も無いのにエディタを開く）こともできます。
ファイルであれば、具象クラス(FileEditorInput)があるのでそれを使えば大丈夫でしょう。

### IViewPart

ビューです。
ページから各種メソッドで取得・表示することができます。

- `IWorkbenchPage#findView()` ： ビューIDを指定して探す
- `IWorkbenchPage#showView()` ： ビューIDを指定してビューを開く
- `IWorkbenchPage#getViews()` ： 非推奨
- `IWorkbenchPage#getViewReferences()` ： 初期済みのビュー参照を取得
- `IWorkbenchPage#findViewReference()` ： ビューIDを指定してビュー参照を探す
- その他、いろいろ