CodeZine(コードジン)

特集ページ一覧

Eclipse RCPプログラミング 1:RCPを生かすアーキテクチャ

ViewCVSをすばやくブラウズする拡張ブラウザの作成

  • LINEで送る
  • このエントリーをはてなブックマークに追加
目次

GUI部分を考える

 モデルの定義が完了したら、いよいよGUI部分の検討をおこないます。

RCPの雛形を作る

 RCPとして最低限動くまでを自分で作成するのはいささか面倒です。ここはEclipseのテンプレートを使って、さくっと自動生成しましょう。

 [ファイル]-[新規]-[プロジェクト]-[プラグインプロジェクト]を選び、[ビューを持つRCPアプリケーション]を選んで自動生成を行ってください。

シンプルなビューで画面を作る

 前述のポリシー「複雑なビューは作るな、複雑なパースペクティブにせよ」を考慮して画面を作っていきましょう。画面イメージの「ツリー表示」「ブラウザ表示」をそれぞれ独立したViewPartとして実装します。そして、内部に持つコンポーネントはそれぞれひとつだけにします。

NavigationView.java
public static final String ID =
    "jp.sourceforge.erep.browser.view.NavigationView"; 

private TreeViewer viewer;

@Override
public void createPartControl(Composite parent) {
    viewer = new TreeViewer(parent, SWT.NONE);
    viewer.setContentProvider(new CVSContentProvider());
    viewer.setLabelProvider(new CVSLabelProvider());
}

BrowserView.java
public static final String ID =
    "jp.sourceforge.erep.browser.view.BrowserView";

private Browser browser;

@Override
public void createPartControl(Composite parent) {
    browser = new Browser(parent, SWT.NONE);
}
Perspective.java
public void createInitialLayout(IPageLayout layout) {
    String editorArea = layout.getEditorArea();
    layout.setEditorAreaVisible(false);
    layout.setFixed(true);
    layout.addView(NavigationView.ID, IPageLayout.LEFT,
                   0.3f, editorArea);
    layout.addView(BrowserView.ID, IPageLayout.RIGHT,
                   0.7f, editorArea);
}

 これだけ書けば、画面の概観は出来上がります。

画面概観
画面概観

ContentProvider, LabelProvider

 定義したViewCVSのモデルを画面に表示できるようにLabelProviderContentProviderを作成する必要があります。これらについての情報は本稿の趣旨と外れますので、説明を省きます。詳細は参考資料を見てください。

CVSContentProvider.java
public class CVSContentProvider implements ITreeContentProvider {

    public Object[] getChildren(Object parentElement) {
        if (parentElement instanceof List) {
            return ((List) parentElement).toArray();
        } else if (parentElement instanceof CVSFolder) {
            CVSFolder folder = (CVSFolder) parentElement;
            return folder.getContents().toArray();
        }
        return null;
    }

    public Object getParent(Object element) {
        return null;
    }

    public boolean hasChildren(Object element) {
        return element instanceof CVSFolder;
    }

    public Object[] getElements(Object inputElement) {
        return getChildren(inputElement);
    }

    public void dispose() {
    }

    public void inputChanged(Viewer viewer, Object oldInput,
                             Object newInput) {

    }

}
CVSLabelProvider.java
public class CVSLabelProvider extends LabelProvider {
    @Override
    public String getText(Object element) {
        CVSFile file = (CVSFile) element;
        return file.getName();
    }
    
    @Override
    public Image getImage(Object element) {
        ISharedImages images =
            PlatformUI.getWorkbench().getSharedImages();
        if (element instanceof CVSModule) {
            return images.getImage(ISharedImages.IMG_OBJ_ELEMENT);

        } else if (element instanceof CVSFolder) {
            return images.getImage(ISharedImages.IMG_OBJ_FOLDER);
        } else if (element instanceof CVSFile){
            return images.getImage(ISharedImages.IMG_OBJ_FILE);
        }
        return null;
    }
}

アクションを作る

 本稿の主旨は、このアクションの重要性にあります。アクションを経由してモデルを変更することでビューの余計なモデルへの依存を軽減します。また、アクションがモデルを経由してビューを変更することでビュー同士の依存が無くなり、かつ、アクションからビューへの参照もなくなります。

 また、アクションは自立的な活性/非活性の切り替えを可能にします。これは単純なボタンでは実現できない、アクションならではの機能です。多くの場合、次の方法でアクションの機能を実現します。

  • 各ビューはISelectionProviderを使って、選択の変更をWorkbenchWindowに通知する
  • アクションはISelectionListenerを使って、WorkbenchWindow上の選択の変更を受け取るようにする
  • 変更を受け取ったら、アクションのsetEnable(boolean)を使って活性/非活性を切り替える
NavigationView.java
@Override
public void setFocus() {
    //Viewの動作 = ViewPartの動作.
    getSite().setSelectionProvider(viewer);
}
OpenURLAction.java
public class OpenURLAction extends Action
    implements IWorkbenchAction, ISelectionListener {

    private CVSFile file = null;
    
    public OpenURLAction(IWorkbenchWindow window) {
        super();
        setActionDefinitionId(
            "jp.sourceforge.erep.browser.action.openurl");
        setId("jp.sourceforge.erep.browser.action.openurl");
        setText("開く");
        setEnabled(false);
        setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().
            getImageDescriptor(ISharedImages.IMG_OBJ_FOLDER));
        window.getSelectionService().addSelectionListener(this);
    }

    /**
     * 選択されているものがCVSFileの場合にアクションが有効.
     * @see org.eclipse.ui.ISelectionListener
     */
    public void selectionChanged(
        IWorkbenchPart part, ISelection selection) {

        IStructuredSelection structuredSelection =
            (IStructuredSelection) selection;
        if (structuredSelection.getFirstElement()
            instanceof CVSFile) {
            file = (CVSFile) structuredSelection.getFirstElement();
            setEnabled(true);
        } else {
            setEnabled(false);
        }
    }

    @Override
    public void run() {
        file.open();
    }
    
    /* (非 Javadoc)
     * @see org.eclipse.ui.actions.ActionFactory.IWorkbenchAction
#dispose()
     */
    public void dispose() {

    }
}

 今回は次のアクションを作成しました。それぞれのアクションは単純にモデルに対してメソッドを呼び出すだけです。

  • アプリケーションが起動している場合は常に有効なCVSモジュール(ViewCVSのURL)を追加するアクション
  • ツリーのなんらかのアイテムを選択している場合に有効なURLをブラウザで開くアクション
  • ツリー上のフォルダを選択してる場合のみ有効なURL以下の中身を解析するアクション

モデルとビューを連携させる

 アクションによって、モデルに対して何らかの変更が与えられると、モデルにフックされたビューに変更が通知されます。ビューが変更を受け取るためには、java.beans.PropertyChangeListenerを実装し、次のような処理を加えます。

BrowserView.java
/**
 * -CVSModuleが追加されたらブラウザに指定されたページを表示し解析を行う
 * -CVSFileに対してopenが呼ばれたらブラウザに指定されたページを表示する
 * -CVSFolderに対してopenが呼ばれたら指定されたページを表示し解析を行う
 */
public void propertyChange(PropertyChangeEvent evt) {
    if (CVSModule.EVENT_UPDATE_URL.equals(evt.getPropertyName())) {
        CVSModule module = (CVSModule) evt.getSource();
        openAndAnalize(module);
    } else if (CVSFolder.EVENT_OPEN.equals(evt.getPropertyName())) {
        CVSFile file = (CVSFile) evt.getSource();
        browser.setUrl(file.getUrl());
    } else if (CVSFolder.EVENT_CONTENTS.equals(evt.getPropertyName())) {
        openAndAnalize((CVSFolder) evt.getSource());
    }
}

 どのような経緯でモデルの変更が行われたかをビューが知る必要はありません。とにかく、モデルの変更に興味がある場合はビューの中身を更新します。

HTML解析

 HTMLの解析は、ブラウザでHTMLを表示した後にJavaからブラウザ上で動的にJavaScriptを実行し、その結果を呼び出し元のJavaで受け取るという少々ひねくれた方法で実現しています。この部分の詳細な説明は本稿の主旨と外れるので、省略します。

動作の流れ

 表示するURLの指定から、HTML解析、ビュー更新までの流れをシーケンスで表すと次のようになります。

シーケンス
シーケンス

まとめ

 今回はアクションとモデルを使って、いかにEclipse RCPらしい設計を行うかについて説明しました。必ずしも今回の設計指針に従う必要はありませんが、GUIアプリケーションの開発は指針を決めずに実装を行ってしまうとコードが煩雑になりがちです。次回からは、RCPの掲げる目標であるコマーシャル・クオリティの製品開発を容易にするために提供されている数々の組み込みコンポーネントについて説明を行う予定です。

参考資料



  • LINEで送る
  • このエントリーをはてなブックマークに追加

バックナンバー

連載:Eclipse RCPプログラミング

著者プロフィール

  • NTTデータ先端技術株式会社 志田 隆弘(シダ タカヒロ)

    <NTTデータ先端技術株式会社について> データベース、ネットワーク、OS、ミドルウェアの基盤技術を武器にシステムの技術面でのコンサルテーションや最新製品の調査を行う専門家集団。 <筆者について> Ja-Jakartaプロジェクトの末席にこっそりと参加しています。...

あなたにオススメ

All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5