// 下階層用テンプレート
#topicpath
----
//ここにコンテンツを記述します。
#contents

*Eclipse/RCPのメニューバーの構築 [#xfe6d579]

Eclipse/RCPのメニューバーの構築は、以下の2つの方法で行うことができます。
-プログラムからプログラマティカルに
-拡張ポイントから宣言的に

[[TUTORIALS 26 Developing for the Rich Client Platform:http://www.eclipsecon.org/2005/tutorials.php]]
によると
-(メニューと、常に表示しておくアクションなど)最小のスケルトンをプログラマティカルに
-それ以外は宣言的に

となっていますね。



**プログラムからプログラマティカルに [#f392fc18]
さて、スケルトンとなる最小の構成をEclipse/RCPのフレームワークで作ります。

RCPは
 IPlatformRunnable -> WorkbenchAdvisor 
 -> WorkbenchWindowAdvisor -> ActionBarAdvisor 
(の実装クラス)という順番に呼び出されるパタンになっていて、メニューバーはこの
 org.eclipse.ui.application.ActionBarAdvisor
内の
 protected void makeActions(IWorkbenchWindow window);
 protected void fillMenuBar(IMenuManager menuBar) ;
をOverrideすることで実装します。

ここでは下記のように実装しました。



-ActionBarAdvisor の実装クラス

 package nu.mine.kino.plugin.samples.rcp;
 
 import org.eclipse.jface.action.IMenuManager;
 import org.eclipse.jface.action.MenuManager;
 import org.eclipse.jface.action.Separator;
 import org.eclipse.ui.IWorkbenchActionConstants;
 import org.eclipse.ui.IWorkbenchWindow;
 import org.eclipse.ui.actions.ActionFactory;
 import org.eclipse.ui.actions.ActionFactory.IWorkbenchAction;
 import org.eclipse.ui.application.ActionBarAdvisor;
 import org.eclipse.ui.application.IActionBarConfigurer;
 
 public class ApplicationActionBarAdvisor extends ActionBarAdvisor {
 
   private IWorkbenchWindow window;
   private IWorkbenchAction quitAction;
   private IWorkbenchAction aboutAction;
   private IWorkbenchAction helpSearchAction;
 
   public ApplicationActionBarAdvisor(IActionBarConfigurer configurer) {
     super(configurer);
     window = configurer.getWindowConfigurer().getWindow();
   }
 
   protected void makeActions(IWorkbenchWindow window) {
     quitAction = ActionFactory.QUIT.create(window);
     register(quitAction);
     aboutAction = ActionFactory.ABOUT.create(window);
     register(aboutAction);
     helpSearchAction = ActionFactory.HELP_SEARCH.create(window);
     register(helpSearchAction);
   }
 
   protected void fillMenuBar(IMenuManager menuBar) {
     menuBar.add(createFileMenu());
     menuBar.add(createHelpMenu());
   }
 
   /**
    * Creates and returns the File menu.
    */
   private MenuManager createFileMenu() {
     MenuManager menu = new MenuManager("&File",
         IWorkbenchActionConstants.M_FILE);
     menu.add(quitAction);
     return menu;
   }
 
   private MenuManager createHelpMenu() {
     // 根っこのメニュー追加。
     MenuManager menu = new MenuManager("&Help",
         IWorkbenchActionConstants.M_HELP); // helpというキー値
     menu.add(helpSearchAction);
     menu.add(new Separator("group01"));
     // ヘルプと、aboutの間にgroup01というグループを追加
     menu.add(aboutAction);
     return menu;
   }
 }

これで、以下のようにメニューバーが構築されました。


#ref(menu01.png)

 [ワークベンチ]
    |
    |-[File]
    |   -[終了]
    |
    |-[Help]
    |   -[検索]
    |   -[○○について]

ポイントは
-すでに定義済みのアクションは
 quitAction = ActionFactory.QUIT.create(window); <- 終了
などActionFactoryから取得できる

-protected void fillMenuBar(IMenuManager menuBar);

がワークベンチのメニューバーのインスタンスなので、そこにアクションをaddしていく

などです。

***グループ名などについて [#u9f12328]
さて、先のコードで、
 MenuManager menu = new MenuManager("&Help",IWorkbenchActionConstants.M_HELP);
や
 menu.add(new Separator("group01"));
などがありました。この IWorkbenchActionConstants.M_HELP (文字列は"help")や、group01などは、拡張ポイントを使った宣言的メニュー追加で必要になってきます。拡張ポイントから宣言的にメニューにアクションを追加する際に、どこに追加するのかをこの名称で指定します。具体的に説明すると、まず先のコーディングで、各メニューの要素には以下の名前が付けられたことになります。

 [ワークベンチ]
    |
    |-[File]            - "file"  <- IWorkbenchActionConstants.M_FILE
    |   -[終了]
    |
    |-[Help]            - "help"  <- IWorkbenchActionConstants.M_HELP
    |   -[検索]
    |   -"セパレータ"   - "group01"
    |   -[○○について] - 

この場合、"セパレータ"部分は、正式名称((完全修飾メニュー・パスというらしい))は
 help/group01 (メニュー名/グループ名)
となります。この名称を、拡張ポイントでアクションを追加するときに使用するわけです。



**拡張ポイントから宣言的に [#ge84d99c]
では先の"セパレータ"部分にアクションを追加します。セパレータの完全修飾メニュー・パスは
 help/group01
でした。


plugin.xmlに以下を追加します。

 <extension point="org.eclipse.ui.actionSets" >
   <actionSet label="ラベル(つかってる?)"
     visible="true" id="nu.mine.kino.plugin.samples.rcp.actionsets">
     <action
       label="追加アクション。" <- 表示されるアクション名
        class="nu.mine.kino.plugin.samples.rcp.actions.HogeAction" <- 実装クラス
        menubarPath="help/group01" <- さっきの完全修飾メニュー・パス
        id="nu.mine.kino.plugin.samples.rcp.actions.hogeaction">
     </action>
   </actionSet>
 </extension>
これで先のhelp/group01の箇所に "追加アクション。" というアクションが追加されました。

#ref(menu02.png)


図で書くと以下の通りですね。
 [ワークベンチ]
    |
    |-[File]                   - "file"     <- IWorkbenchActionConstants.M_FILE
    |   -[終了]
    |
    |-[Help]                   - "help"     <- IWorkbenchActionConstants.M_HELP
    |   -[検索]
    |   -"セパレータ"          - "group01"
    |   -[追加アクション。]    -            <- plugin.xmlで追加
    |   -[○○について] - 





このように、セパレータなどを用いて、提供側の開発者がメニューを追加できるように「開いておいてくれた」場合、他のプラグイン開発者は、完全修飾メニュー・パスを知ることで容易にアクションを追加することができるわけですね。



*** アクションではなく、サブメニューを追加する [#hed5215b]
次に、サブメニューを追加したいと思います。具体的には次のようにしたいと思います。

 [ワークベンチ]
    |
    |-[File]                   - "file"     <- IWorkbenchActionConstants.M_FILE
    |   -[終了]
    |
    |-[Help]                   - "help"     <- IWorkbenchActionConstants.M_HELP
    |   -[検索]
    |   -"セパレータ"          - "group01"
    |   -[追加メニュー。]      - "menu01"   <- plugin.xmlで追加
    |      -"セパレータ"       - "sepa01"   <- plugin.xmlで追加
    |      -[追加アクション。] -            <- plugin.xmlで追加
    |   -[○○について] - 

セパレータ部分に「追加メニュー。」というサブメニューを追加し、その下にさっきのアクションを追加したいと思います。

plugin.xmlに以下の通り記述します。さっきのactionSetsはいったん削除しましょう。
 <extension point="org.eclipse.ui.actionSets" >
   <actionSet label="ラベル(つかってる?)"
     visible="true" id="nu.mine.kino.plugin.samples.rcp.actionsets">
       <menu
          id="menu01"       <- 名称は menu01
          label="追加メニュー。"
          path="help/group01">  <-このサブメニューはセパレータの場所に追加する
         <separator name="sepa01"/> <-さらにセパレータを下に追加
       </menu>
     <action
       label="追加アクション。"
       class="nu.mine.kino.plugin.samples.rcp.actions.HogeAction"
       menubarPath="help/menu01/sepa01"
       id="nu.mine.kino.plugin.samples.rcp.actions.hogeaction">
     </action>
   </actionSet>
 </extension>

順に見ていきます。
 <menu
   id="menu01"
   label="追加メニュー。"
   path="help/group01">
   <separator name="sepa01"/>
 </menu>
で「pathの箇所にlabelのサブメニューをidの名称で追加します」という意味になります。でその箇所の完全修飾メニュー・パスは help/menu01 となります。[メニュー名/グループ名]なのでhelp/menu01となるんですね。

次にセパレータを追加しています。セパレータの完全修飾メニュー・パスは
 help/menu01/sepa01
となります。

あとはアクションですが、先と同じで
 menubarPath="help/menu01/sepa01"
によって、先のサブメニューの下のセパレータ部分にこのアクションが挿入されることになります。





*** 複数のアクションを追加する [#i51aa936]
続いて以下のように複数のアクションを追加しました。
   <actionSet label="ラベル(つかってる?)"
     visible="true" id="nu.mine.kino.plugin.samples.rcp.actionsets">
     <menu
      id="menu01"
      label="追加メニュー。"
      path="help/group01">
      <separator name="sepa01"/>
     </menu>
     <action
       label="追加アクション。"
       class="nu.mine.kino.plugin.samples.rcp.actions.HogeAction"
       menubarPath="help/menu01/sepa01"
       id="nu.mine.kino.plugin.samples.rcp.actions.hogeaction">
     </action>
     <action
       label="追加アクション2。"
       class="nu.mine.kino.plugin.samples.rcp.actions.HogeAction"
       menubarPath="help/group01" 
       id="nu.mine.kino.plugin.samples.rcp.actions.hogeaction2">
     </action>
    </actionSet>



予想通り

 [ワークベンチ]
    |
    |-[File]                   - "file"     <- IWorkbenchActionConstants.M_FILE
    |   -[終了]
    |
    |-[Help]                   - "help"     <- IWorkbenchActionConstants.M_HELP
    |   -[検索]
    |   -"セパレータ"          - "group01"
    |   -[追加アクション2。]"  - "group01"  <- plugin.xmlで追加
    |   -[追加メニュー。]      - "menu01"   <- plugin.xmlで追加
    |      -"セパレータ"       - "sepa01"   <- plugin.xmlで追加
    |      -[追加アクション。] - "sepa01"   <- plugin.xmlで追加
    |   -[○○について] - 


となりました。



*** 存在しない完全修飾メニュー・パスを指定する [#v4898d72]
さらに以下の内容をplugin.xmlに追加します。
 <action
   id="nu.mine.kino.plugin.samples.rcp.action3"
   label="追加アクション3"
   menubarPath="help/hogehoge" <- 存在しない完全修飾メニュー・パス
 />

すると
#ref(menu03.png)

一番下に追加されました。

***まとめると、以下のようになります。 [#caa1b6d6]
-プログラムで完全修飾メニュー・パスを追加して、メニューを「開いて」おく
-完全修飾メニュー・パスを指定して、アクションを追加できる
-完全修飾メニュー・パスを指定して、サブメニューを追加できる(そのとき下にセパレータを追加しよう)
-サブメニュー追加時は、新しい名称をid要素で定義する(help/menu01など)
-サブメニューの下に追加したセパレータの箇所へのアクションの追加はhelp/menu01/sepa01の様に、サブメニューの完全修飾メニュー・パス/セパレータ名となる
-サブメニューは子要素がないと表示されない
-完全修飾メニュー・パスの箇所には、複数のメニューやアクションを追加できる
-存在しない完全修飾メニュー・パスを指定すると、一番下に追加される





***サブメニューの下にさらにサブメニューを表示する。 [#u28a7585]
次に、以下のような階層的なメニュー構造を作ってみたいと思います。

 [ワークベンチ]
    |
    |-[File]                                - "file"     <- IWorkbenchActionConstants.M_FILE
    |   -[終了]
    |
    |-[Help]                                - "help"     <- IWorkbenchActionConstants.M_HELP
    |   -[検索]
    |   -"セパレータ"                       - "group01"
    |   -[追加メニュー。]                   - "menu01"   <- plugin.xmlで追加
    |      -"セパレータ"                    - "sepa01"   <- plugin.xmlで追加
    |      -[追加アクション。]              -            <- plugin.xmlで追加
    |      -[さらに下にメニュー。]          - "menu02"   <- plugin.xmlで追加
    |        -"セパレータ"                  - "sepa02"   <- plugin.xmlで追加
    |        -[さらに下に追加アクション。]  -            <- plugin.xmlで追加
    |   -[○○について] - 


#ref(menu04.png)



plugin.xmlは以下のようにします。
 <actionSet label="ラベル(つかってる?)"
   visible="true" id="nu.mine.kino.plugin.samples.rcp.actionsets">
   <menu
     id="menu01"
     label="追加メニュー。"
     path="help/group01">
     <separator name="sepa01"/>  <- 完全名は help/menu01/sepa01
   </menu>
   <action
     label="追加アクション。"
     class="nu.mine.kino.plugin.samples.rcp.actions.HogeAction"
     menubarPath="help/menu01/sepa01"
     id="nu.mine.kino.plugin.samples.rcp.actions.hogeaction">
   </action>
   <menu
     id="menu02"
     label="さらに下にメニュー。"
     path="help/menu01/sepa01">   <- help/menu01/sepa01 の場所にサブメニュー追加
     <separator name="sepa02"/>   <- 完全名は help/menu01/menu02/sepa02
   </menu>
   <action
     id="nu.mine.kino.plugin.samples.rcp.action5"
     label="さらに下に追加アクション。"
     menubarPath="help/menu01/menu02/sepa02" >
   </action>
  </actionSet>


ポイントはhelp/menu01/sepa01に追加したサブメニューの完全修飾メニュー・パスは、
 help/menu02/ ではなく help/menu01/menu02/
となることです。即ちそのしたのセパレータも
 help/menu01/menu02/sepa02
となります。従ってそのセパレータに追加するアクションのmenubarPathの指定は
   <action
     id="nu.mine.kino.plugin.samples.rcp.action5"
     label="さらに下に追加アクション。"
     menubarPath="help/menu01/menu02/sepa02" >  <- これ
   </action>
となるわけです。



*** トップレベルのメニューにアクションを追加したい。 [#k713bb2e]
トップレベルのメニュー(ワークベンチが保持するIMenuManagerのメニュー)へメニューを追加する方法です。プログラマティカルにやる方法はまだわかんない(コメントに裏技を載せた)けど、宣言的にやるのは、以下の通り:

 <extension
     point="org.eclipse.ui.actionSets">
  <actionSet
      id="rcpmail.actionSet8"
      label="rcpmail.actionSet8"
      visible="true">  <-これ、重要!!
     <menu
       id="topmenu"
       label="トップメニュー">
      <separator name="sepa01"/>
     </menu>
     <action
       id="rcpmail.action10"
       label="アクション2"
       menubarPath="topmenu/sepa01"
       style="push"/>
     <action
       id="rcpmail.action9"
       label="アクション1"
       menubarPath="topmenu/sepa01"
       style="push"/>
  </actionSet>
 </extension>

参考:~
[[3−3.メニューバー、ツールバーとアクションの紐付け(アクションの登録):http://www.willbe-computing.jp/modules/eclipsercp3/index.php?id=8]]

*** トップレベルのメニューの指定したところに、アクションを追加したい。 [#ad045aac]
Eclipseでトップレベルにメニューを追加するといちばん右ではなく、途中の箇所にメニューが追加されますが、これはRCPとしてのEclipseが、その箇所にGroupMarkerを仕込んでくれているからです。グループマーカはメニュー内だけでなく、トップメニューでも使用できるようです。具体的には
 org.eclipse.ui.internal.ide.WorkbenchActionBuilder
がEclipseのメニューバーを構築しているのですが、そのメソッドfillMenuBarでは
    protected void fillMenuBar(IMenuManager menuBar) {
        menuBar.add(createFileMenu());
        menuBar.add(createEditMenu());
        menuBar.add(createNavigateMenu());
        menuBar.add(createProjectMenu());
        menuBar.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
        menuBar.add(createWindowMenu());
        menuBar.add(createHelpMenu());
    }
となっていて、プロジェクトとウィンドウの間に
 new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS);
が仕込まれています。で、このキー値をplugin.xmlに指定すれば、この場所にメニューが追加されます。((実は指定しなくても、同じ挙動をする。後述。))


実際にさっきのサンプルでもやってみましょう。
ApplicationActionBarAdvisor#fillMenuBar を以下のように変更します。
 protected void fillMenuBar(IMenuManager menuBar) {
   menuBar.add(createFileMenu());
   menuBar.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS));
   menuBar.add(createHelpMenu());     ↑追加
 }

さて、この IWorkbenchActionConstants.MB_ADDITIONS というキー値は、実際の値はadditionsなので、
  <menu id="topmenu" label="トップメニュー"
      path="additions"> <-追加
    <groupMarker name="sepa01"/>
  </menu>
  <action id="nu.mine.kino.plugin.samples.rcp.action1"
    label="アクション。"
    menubarPath="topmenu/sepa01" style="push"/>
 </actionSet>
としておけばGroupMarkerの箇所にメニューが追加されます。

さて、実はIWorkbenchActionConstants.MB_ADDITIONS(値はadditions)は定数定義されているとおり、デフォルト値?という特別な意味があるみたいです。よってplugin.xmlでpath属性を指定しなかった場合はadditionsを指定したことになるみたい。なので、実はpath="additions" を追加しないでも、グループマーカのところにメニューは表示されるのでした。

もちろん、
 menuBar.add(new GroupMarker("hoge"));
としてplugin.xmlを path="hoge" とすると、グループマーカのところのそのメニューが表示されます。よってRCPのプロバイダ側は開いておきたいところにグループマーカを設定しておけばよいわけですね。





***SeparatorとGroupMarkerの違い [#z4f9a06c]
 <separator name="sepa01"/>と<groupMarker name="sepa01"/>
の違いや
 menu.add(new Separator("group01"));
 menu.add(new GroupMarker("group01"));
の違いについてですが、Separatorは違うグループ殿間に「セパレータ」が入るのに対し、GroupMarkerは入らない、という違いがあります。表示順を緻密に制御したい場合にGroupmMarkerを細かく入れて、そこにアクションをひもづける、なんて使い道があるんですね。

**関連ページ [#n86878fd]
[[プラグイン開発のTIPS集/org.eclipse.ui.IWorkbenchWindowActionDelegate>Eclipse/プラグイン開発のTIPS集/org.eclipse.ui.IWorkbenchWindowActionDelegate(メニューバー)]] も参照ください。
-[[メニューの作り方>http://www.limy.org/program/eclipse/plugin/make_menu.html]]
 <action class="sample.SamplePopupAction" id="sample.SamplePopupAction"
  label="Popup Action" menubarPath="sample.menu/additions"> </action> 

**キー値の一覧 [#v8bf1e64]
-[[IWorkbenchActionConstantsのJavaDoc:http://help.eclipse.org/help30/index.jsp?topic=/org.eclipse.platform.doc.isv/reference/api/org/eclipse/ui/IWorkbenchActionConstants.html]] にIWorkbenchActionConstants.MB_ADDITIONSなどのキー値の一覧があります。

**サンプルソース [#r8edf242]
-[[プラグインのソース(ViewVC)>http://www.masatom.in/viewvc/tags/V20090221_01/nu.mine.kino.plugin.samples.rcp/?root=Examples]]

**関連リンク [#m19adebc]
-Eclipse/プラグイン開発のTIPS集/メニューバーを構築する(Eclipse3.3版)

----
この記事は
#vote(おもしろかった[11],そうでもない[0])
#vote(おもしろかった[12],そうでもない[0])
-menu要素(サブメニュー)の場合は、その場所を指定して、そのしたにメニューを追加するので、新しい名前を付ける必要がある。それがid属性なんですね。何となく、わかってきた。。 -- [[きの]] &new{2006-03-02 20:38:36 (木)};
-メニュー自体のインスタンス(IMenuManager)を取得する方法がわかんない。いちおう
   WorkbenchWindow activeWorkbenchWindow = (WorkbenchWindow) PlatformUI
                .getWorkbench().getActiveWorkbenchWindow();
   IMenuManager menuManager = activeWorkbenchWindow.getActionBars()
                .getMenuManager();
でいけるけど、WorkbenchWindow が internalなんだよね。  -- [[きの]] &new{2006-03-02 22:16:08 (木)};
-Eclipse エキスパートガイドによると、xmlタグ付けの順番と表示順は逆なんだって。なんで逆に並んじゃうのかなぁと思ってたんだけど。 -- [[きの]] &new{2006-03-03 03:53:29 (金)};
-追加したとき実際に実行されるアクションの作り方は、[[ワークベンチのメニューバーや、ツールバーにアクションを追加したい>Eclipse/プラグイン開発のTIPS集/org.eclipse.ui.IWorkbenchWindowActionDelegate(メニューバー)]]を参照ください。 -- [[きの]] &new{2006-05-19 11:52:16 (金)};
- org.eclipse.ui.internal.ide.WorkbenchActionBuilder#createWindowMenu をみると、ビューを開くなどのコーディングが分かるよ -- [[きの]] &new{2010-12-07 (火) 02:10:20};

#comment
#topicpath


SIZE(10){現在のアクセス:&counter;}



トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS