在Eclipse的About对话框上添加自己的图标

在Eclipse的About对话框上添加自己的图标的步骤如下:

1、建立一个feature;

2、在feature.xml的”Overview“属性页里设定一个“Branding Plug-in”;

3、把一个32x32的图片(名字可以任意起,例如“mylogo.gif”)放在Branding Plug-in的根目录下;

4、在Branding Plug-in的根目录下建立“about.ini”文件,内容为“featureImage=mylogo.gif”;

5、最后,输出这个feature即可。

图1 添加在About对话框里的图标

注意1:Branding Plug-in的MANIFEST.MF的Build页里一定要选择输出mylogo.gif和about.ini这两个文件

注意2:我输出feature时如果选中“Package features and plug-ins as individual JAR archives”则feature不会被安装(在About的Feature Details列表里没有出现),勾掉这个选项则正常。

参考: How Can I Give My Eclipse Blob An Icon In The Flippin' About Dialog?

下载:包含feature和branding plug-in的工程打包

将Eclipse插件转换为RCP应用程序(下)

上一篇里我们为一个普通的Eclipse插件添加了Application扩展,剩下来的工作就很简单了,甚至不需要再编写一行代码。在 Eclipse 3.1里,把具有Application的插件包装成RCP并输出的过程是通过建立产品配置文件(Product Configuration)来完成的。

在主菜单选择File->New->Other命令,在对话框里选择新建一个产品配置文件,这个文件可以建立在任何位置,为方便起见我 们就把它放在需要转换为RCP的插件的主目录下好了。产品配置文件是一个xml格式的文件,不过Eclipse 3.1提供了一个编辑器界面来编辑它的内容,所以不用像以前那样记住所有的tag了。这个编辑器分为三个页面:Overview、 Configuration和Branding。

首先在Overview页面指定产品的ID,按下“Product ID”右边的“New...”按钮,在对话框里输入插件的ID、新的产品ID以及缺省的Application的ID,见图1。关闭对话框后,选择一个要 运行的Application,并填写产品名称。下面有一个选项让你选择产品基于plug-in还是feature,feature是多个插件的集合,如 果只包含一个插件,选择基于plug-in即可;如果包含多个插件,利用feature可以让这些插件按功能分类,便于管理,建议使用基于feature 的方式,不过你要先建立feature才行。

图1 新建Product ID对话框

然后,来到Configuration页面,先把我们的插件添加到左边的插件列表里(如果前面选择了基于features方式,这里是 feature列表),再按“Add Required Plug-ins”按钮让Eclipse自动添加被依赖的其他插件。config.ini文件的作用是设置了一些变量值,RCP程序运行时会根据它们改变 一些外观或行为,例如可以在这里规定透视图切换器的停靠位置(org.eclipse.ui/DOCK_PERSPECTIVE_BAR=left)等 等;在页面的右下方可以设置一些运行参数。

图2 选择需要的插件

最后翻到Branding页面,这个页面的功能就是定制一些外观元素,例如启动时显示的splash图像,将想显示的图像以 “splash.bmp”命名保存到插件的根目录下,然后在“Splash Screen”里指定这个插件ID即可;定制窗口的图标,包括16x16和32x32两种格式,都是.gif文件;还有就是“关于...”对话框里的图片 和文字,根据自己的需要填写即可。启动器名称(Launcher Name)就是启动RCP的命令名称,在Linux里是一个脚本文件,在Windows里则是一个.exe文件。还可以定制启动器的图标,由于时间关系我 的例子里省去了这个定制项目。

需要注意一点,这些图像无论放在哪个目录里(比如icons目录)都应该确保会被输出到产品里,否则运行产品时会看不到它或看到红色的小方块,方法是在插件的build.properties编辑器里勾选需要输出的文件和目录。

上面这些都配置好以后,回到Overview页面,先点击“Launch the product”看一下产品运行后的效果,确认没有问题后,就可以点击“Eclipse Product export wizard”输出你的产品了,在弹出的对话框里填写要输出的位置,可以选择输出为目录或是打包为单个文件(.zip格式),见图3。

图3 输出产品

输出后最好再确认一下运行效果,如果和刚才有所不同,则很可能是build.properties写的有些问题,请仔细检查。

怎么样,很容易吧!

将Eclipse插件转换为RCP应用程序(上)

有不少朋友问到如何把一个已有的Eclipse插件转换为RCP应用程序,其实这个过程并不复杂,因为RCP应用也是基于插件的结构,可以说RCP 就是精简后的Eclipse平台,只是我们要对这个平台做一些定制工作。将任何一个传统的Eclipse插件项目转换到RCP可以分为两个步骤,这篇先介 绍第一个步骤:建立应用程序。

GEF入门系列(三、应用实例)里我曾做过一个精简的GEF应用程序(下载),这一篇里我就一步一步的把这个例子转换为RCP应用程序(点击下载转换后的项目打包)。应用程序(Application)是通过扩展org.eclipse.core.runtime.applications扩展点建立的,其作用 是让Eclipse知道你的RCP需要什么样的功能,比如界面上有哪些视图,菜单和工具条,应用程序窗口的初始大小等等。在plugin.xml里添加应 用程序的定义很简单,像下面这样指定一个id和一个类名就可以了。

<extension
      id="myapplication"
      point="org.eclipse.core.runtime.applications">
   <application>
      <run class="com.example.application.MyApplication"/>
   </application>
</extension>

接下来我们的主要任务是实现这个类,MyApplication必须实现 org.eclipse.core.runtime.IPlatformRunnable接口,这个接口只定义了一个run()方法,对于Eclipse Platform来说这个方法就相当于传统java程序的main()方法,是入口方法。所有RCP应用程序里这个方法的实现几乎是完全一样的,即启动 Workbench,并把一个WorkbenchAdvisor实例作为参数传给它,如下所示:

public class MyApplication implements IPlatformRunnable {

    public Object run(Object args) throws Exception {
        Display display = PlatformUI.createDisplay();
        try {
            int returnCode = PlatformUI.createAndRunWorkbench(display, new MyWorkbenchAdvisor());
            if (returnCode == PlatformUI.RETURN_RESTART) {
                return IPlatformRunnable.EXIT_RESTART;
            }
            return IPlatformRunnable.EXIT_OK;
        } finally {
            display.dispose();
        }
    }
}

所以应用程序的定制实际上是通过这个WorkbenchAdvisor实例实现的。现在我们要构造 org.eclipse.ui.application.WorkbenchAdvisor类的一个子类,也就是上面代码里出现的 MyWorkbenchAdvisor,然后覆盖它的一些方法。比较重要的是这两个方法:createWorkbenchWindowAdvisor() 返回一个WorkbenchWindowAdvisor实例,从类名不难看出它的作用是定制应用程序窗口,包括菜单和工具条,稍后将详细介绍; getInitialWindowPerspectiveId()返回一个透视图的id字符串,这个透视图定义RCP应用程序的界面布局,所以如果在原来 的插件里你没有定义透视图,现在必须要新定义一个了。

public class MyWorkbenchAdvisor extends WorkbenchAdvisor {

    private static final String PERSPECTIVE_ID = "com.example.ui.MyPerspective";

    public WorkbenchWindowAdvisor createWorkbenchWindowAdvisor(
            IWorkbenchWindowConfigurer configurer) {
        return new MyWorkbenchWindowAdvisor(configurer);
    }

    public String getInitialWindowPerspectiveId() {
        return PERSPECTIVE_ID;
    }

    public void initialize(IWorkbenchConfigurer configurer) {
        super.initialize(configurer);

        //The workaround call
        WorkbenchAdapterBuilder.registerAdapters();
    }
}

注意:因为我们这个RCP里用到了Resource视图,而这个视图依赖org.eclipse.ui.ide,所以要在上面的 initialize()方法里手动注册一下Adapter,否则Resource视图里无法显示现有项目。(Resource视图在RCP里不推荐使 用,这个调用是无奈之举,请参考这条bug报告

现在来看一下前面代码里MyWorkbenchWindowAdvisor是怎样实现的,它继承自 org.eclipse.ui.application.WorkbenchWindowAdvisor类,为了定义窗口大小和标题要覆盖 preWindowOpen()方法,可以看到我们还顺便隐藏了工具条;要定义窗口的菜单和工具条,应该覆盖 createActionBarAdvisor()方法,返回的ActionBarAdvisor实例马上会介绍到。

public class MyWorkbenchWindowAdvisor extends WorkbenchWindowAdvisor {

    public MyWorkbenchWindowAdvisor(IWorkbenchWindowConfigurer configurer) {
        super(configurer);
    }

    public ActionBarAdvisor createActionBarAdvisor(
            IActionBarConfigurer configurer) {
        return new MyActionBarAdvisor(configurer);
    }

    public void preWindowOpen() {
        IWorkbenchWindowConfigurer configurer = getWindowConfigurer();
        configurer.setInitialSize(new Point(700, 500));
        configurer.setShowCoolBar(false);
        configurer.setShowStatusLine(false);
        configurer.setTitle("My RCP Application");
    }
}

有没有注意到,我们新建(和即将新建)的几个类有这样的引用关系:MyApplication->MyWorkbenchAdvisor- >MyWorkbenchWindowAdvisor->MyActionbarAdvisor,在3.1M5以前的Eclipse RCP版本中,还没有ActionbarAdvisor这个类,大部分应用程序定制工作都是在WorkbenchWindowAdvisor这一个类中做 的,带来的问题是这个类的代码很长,可复用的程度比较低;采用现在这种方式就方便多了,比如可以定义几个ActionbarAdvisor然后在 WorkbenchWindowAdvisor中根据需要做出选择,得到的应用程序就具有不同的功能,等等。

现在就来看看MyActionbarAdvisor是怎么实现的,它继承 org.eclipse.ui.application.ActionBarAdvisor类,我们先在makeActions()里构造需要出现在菜单 或工具条上的命令,注意要调用register()方法注册这些命令,作用是在应用程序结束后释放资源,同时支持快捷键操作;然后在 fillMenuBar()方法里把这些命令加入主菜单,因为我们隐藏了工具条,所以没有覆盖fillCoolBar()方法,另外你还可以通过覆盖 fillStatusLine()定义自己的状态栏。我们的这个类实现得很简单,只是一个退出程序菜单项,你应该根据需要添加自己的命令。

public class MyActionBarAdvisor extends ActionBarAdvisor {

    private IWorkbenchAction exitAction;

    public MyActionBarAdvisor(IActionBarConfigurer configurer) {
        super(configurer);
    }

    protected void makeActions(final IWorkbenchWindow window) {
        exitAction = ActionFactory.QUIT.create(window);
        register(exitAction);
    }

    protected void fillMenuBar(IMenuManager menuBar) {
        MenuManager fileMenu = new MenuManager("&File",
                IWorkbenchActionConstants.M_FILE);
        menuBar.add(fileMenu);
        fileMenu.add(exitAction);
    }
}

现在,应用程序需要的类都写好了,让我们检查一下应用程序是否可以正常启动。在Eclipse主菜单上选择Run->Debug...命令, 在对话框左边的“Eclipse Application”组下新建一个运行项“gefpractice-rcp”,在“Program to Run”组下选择“Run an application”,然后在下拉列表里找到我们的应用程序id,要说明的是在applications扩展点里我们指定的id是 “myapplication”,而这里列出的id则添加了插件id作为前缀,变成了“GefPractice-RCP.myapplication”, 如图1所示。

图1 设置为运行应用程序

因为缺省运行会启动Eclipse的全部插件,这样在应用程序里会出现多余的菜单项和功能,所以要设置为只启动我们的这一个插件,方法是切换到 Plug-ins属性页,选择“Choose plug-ins and fragments to launch from the list”,点击右边的“Deselect All”按钮清空选择列表,勾选上我们的插件项目,再按“Add Required Plug-ins”让Eclipse自动添加它依赖的其他插件就可以了,如图2所示。

图2 只启动我们的这一个插件

现自使用这个运行配置启动我们的应用程序,会得到一个很“干净”的界面,如图3所示,如果不是那些Eclipse特有的编辑器/视图的标题栏,你能猜出它是一个Eclipse应用程序吗?作为对比,这是Eclipse插件的版本的运行截图

图3 运行中的应用程序

建立了应用程序,代码的部分就算是完成了,但要得到一个完整的可独立运行的产品这样还不够,下一个帖子里将介绍另一个步骤:将应用程序包装为产品。如果等不及可以先看Branding Your Application这篇文章,只是这篇文章写得比较早,我下个部分要写的是使用.product配置产品,可以更方便的达到相同的目的。关于建立应用程序的更多内容请参考Rich Client Tutorial,这个教程共有三个部分,我当时就是通过它学习的,后来它按照RCP API的发展又及时更新了内容,是难得的入门材料。

把第三方jar文件包装为plugin

前面说过,输出eclipse插件的时候问题比较多(google搜索3rd party jars site:dev.eclipse.org),特别是使用了第三方jar文件的时候。有一个比较方便的办法是把这些jar文件包装为一个单独的插件,然后 你的功能插件用dependencies的方式引用它们,这也是eclipse推荐的方式。

在eclipse里包装jar包很容易:按ctrl+n,在新建对话框里选择"Plug-in from existing JAR archives",按下一步选择你需要的jar文件,再下一步指定这个plugin的名称,注意这一步里一般要把最下面的"Unzip the JAR archives into the project"选项清除,否则eclipse会把jar文件全部展开为.class文件树,最后按Finish就可以了。

奇怪的是,我不使用上面这种方式,用建立普通plugin项目的方式怎么也无法建立好包装plugin,表现为其它plugin依赖它后找不到里面的那些类。

导出Plugin的一点心得

Eclipse升级到了3.1以后,在Plugin manifest editor上做了相当大的改动,可能是刚刚完全支持OSGi的缘故吧,感觉这个editor的小bug挺多的。比如很多情况下表示dirty的星号不会消失,有时会弹出带红叉的对话框等等,不过我相信在eclipse 3.2版本里会得到解决,因为关于它的bug report已经不少了。

除了一些小bug以外,我发现在3.1里导出一个Plugin也变得有些“莫名其妙”,最近经过实验摸索,得到以下一些心得:

1、开发plugin的时候,不要在project properties的java build path里做任何修改,应该在plugin配置文件的dependencies、runtime和build这几个部分添加。

2、在build页上面部分,Library的缺省情况是".",我发现有时候指定为"."会造成class不被输出。比较保险的方法是指定一个.jar名字,对应的folder还是src/,勾中下面的Include selected library选项。

3、在build页中间部分指定要输出的文件,一般除了缺省的那些以外,还要包含如lib、icons、log4j.properties等目录或文件。

4、在build页下面部分,也就是“Extra Classpath Entries”,指定你的应用程序用到的外部包,例如log4j.jar等等。

5、需要注意的是,如果Library里指定了abc.jar,则输出时会生成abc.jar这个文件,但Plugin在运行时会报ClassNotFound异常,原因是你没有把abc.jar指定到这个Plugin的classpath里。解决的办法是在Manifest.mf文件的Bundle-ClassPath项里手动加上abc.jar,例如“Bundle-ClassPath: abc.jar,lib/log4j-1.2.11.jar”这样。

6、若你有两个Plugin,多个的情况也一样,假设B依赖A,那么不仅在B的dependencies里要加上A,还要保证A的Runtime页里export了B所需的所有包,也就是在A的Manifest.mf文件里指定正确的“Export-Package”项。

7、输出为目录或输出为单个jar包在运行时没有什么区别,感觉jar包方便一些,输出的jar包名就是项目名。

希望能有帮助。

Plugin.xml -> Manifest.mf

为了更好的实现动态加载/卸载插件,Eclipse从3.0开始实现OSGI规范,原先在plugin.xml文件里定义的很多内容都被推荐放在manifest.mf文件里,只有<extension>和<extension-point>是例外。下面是两种文件格式中各元素的对照表:

plugin.xml tag/attribute
manifest.mf header
<plugin id=> Bundle-SymbolicName
<plugin version=> Bundle-Version
<plugin name=> Bundle-Name
<plugin provider=> Bundle-Vendor
<plugin class=> Bundle-Activator
<fragment plugin-id=> Fragment-Host
<fragment plugin-version=> Fragment-Host: <id>; bundle-version=
<requires>, <import> Require-Bundle
<runtime>, <library> Bundle-ClassPath

具体的转换方法在这里可以看到,也就是说,我们在Eclipse 3.0以上版本中开发插件的时候,最好使用manifest.mf文件的方式(实际上Eclipse自带的Plugin Manifest Editor在这方面已经帮我们做了不少工作,新版本的Eclipse在创建新项目时会自动把可以放在manifest.mf文件中的内容转移过去)。

宣传一下黄老大的ET2项目

我曾经的supervisor黄老大开发了一个功能十分强大的Eclipse Toolkit,提供了一些Eclipse应用程序经常需要用到的组件,如高度可定制的表格控件等等,是对目前SWT/JFace/GEF的扩展和增强。

“Here is the link: http://sourceforge.net/projects/eclipsetoolkit2/.
An old link is http://www.cs.umb.edu/~huangjun/.

I release all files under epl v1.0.

Sorry for bare documents and samples coz mid-term is coming! I will try my
best to make up.

I do believe there are many ways to impl such stuffs and I am not sure my
way is good enough. I am open to any comments. Thanks for your attention!”

我曾经的supervisor黄老大开发了一个功能十分强大的Eclipse Toolkit,提供了一些Eclipse应用程序经常需要用到的组件,如高度可定制的表格控件等等,是对目前SWT/JFace/GEF的扩展和增强。

“Here is the link: http://sourceforge.net/projects/eclipsetoolkit2/.
An old link is http://www.cs.umb.edu/~huangjun/.

I release all files under epl v1.0.

Sorry for bare documents and samples coz mid-term is coming! I will try my
best to make up.

I do believe there are many ways to impl such stuffs and I am not sure my
way is good enough. I am open to any comments. Thanks for your attention!”

et2.jpg

设置Eclipse RCP程序的外观和首选项

RCP应用程序的缺省外观是一个空白窗口,一般我们要通过一个WorkbenchAdvisor类对界面进行定制。 WorkbenchAdvisor有很多回调方法,可以在preWindowOpen()方法里设置菜单、工具条、状态栏、进度栏、透视图切换工具是否可 见,在fillActionBars()方法里添加菜单和工具条项,在getInitialWindowPerspectiveId()方法里指定首选的 透视图。

缺省情况下,透视图切换工具位于窗口左上角,在Eclipse里可以通过Window->Preferences-> Workbench->Appearance改变它的位置,那么怎样用程序控制它呢?有两个方法,第一个是使用如下代码设置 IPreferenceStore中的变量:

IPreferenceStore apiStore = PrefUtil.getAPIPreferenceStore(); 
apiStore.setValue(IWorkbenchPreferenceConstants.DOCK_PERSPECTIVE_BAR, IWorkbenchPreferenceConstants.TOP_RIGHT);

另一个方法是在plugin所在目录建一个名为plugin_customization.ini的文件,里面写如下内容:

your.plugin.id/DOCK_PERSPECTIVE_BAR = topRight

其他与plugin相关的Preference值可以用同样方法设置。

Update:在最新的Eclipse 3.1M5a版本中,对RCP应用程序菜单和工具条的定制方法有所改变,应该使用新加入的ActionBarAdvisor类来完成此项工作。

给表格的单元格增加编辑功能(In place edit)

使用纯粹的SWT可以实现在单元格中编辑(In place edit)的功能,代码见这个例子,这里要说的是利用jface完成差不多的工作:用户单击单元格,出现一个下拉菜单,用户通过选择来改变单元格所代表的该行对象的属性。

对org.eclipse.jface.viewers.TableViewer.TableViewer的介绍这里就不赘述了,一般我们都是像下面这样定义TableViewer的:

TableViewer viewer = new TableViewer(...); 
viewer.getTable().setHeaderVisible(true); 
viewer.setContentProvider(...); 
viewer.setLabelProvider(...); 
//Add table columns 
TableColumn column = new TableColumn(viewer.getTable(), SWT.NONE); 
column.setText("...");

要增加编辑功能,首先得定义一个org.eclipse.jface.viewers.CellEditor数组,对应TableViewer的每一列,在我们这个例子里,第二列(Column 1)需要用下拉框编辑,所以可以这样定义这个CellEditor数组。

final CellEditor[] editors = new CellEditor[tvOre.getTable().getColumnCount()]; 
editors[1] = new ComboBoxCellEditor(viewer.getTable(), new String[] {}, SWT.READ_ONLY); 
viewer.setCellEditors(editors);
viewer.setColumnProperties(columnNames);

再定义这个TableViewer的CellModifier(org.eclipse.jface.viewers.ICellModifier),该接口定义了三个方法,canModify()指出该单元格是否可被编辑;getValue()应返回单元格的当前值,因为我们使用的是下拉框,所以要返回一个Integer表示当前选中的index;modify()方法中我们将用户修改的值反映到实际模型中。代码如下,注意代码里IOre是我们模型的一部分,表格中每一行是一个IOre对象,IOre.getOrderParams()得到的就是下拉框中的选项,每行的选项与那一行代表的IOre对象有关:

viewer.setCellModifier(new ICellModifier() { 
    public boolean canModify(Object element, String property) { 
        IOre ore = (IOre) element; 
        String[] items = new String[ore.getOrderParams().size()]; 
        for (int i = 0; i < ore.getOrderParams().size(); i++) { 
            NameValuePair pair = (NameValuePair) ore.getOrderParams().get(i); 
            items[i] = pair.getValue(); 
        } 
        editors[1] = new ComboBoxCellEditor(viewer.getTable(), items, SWT.READ_ONLY); 
        return property.equals(columnNames[1]); 
    } 
 
    public Object getValue(Object element, String property) { 
        if (property.equals(columnNames[1])) { 
            IOre ore = (IOre) element; 
            for (int i = 0; i < ore.getOrderParams().size(); i++) { 
                NameValuePair pair = (NameValuePair) ore.getOrderParams().get(i); 
                if (pair.getValue().equals(ore.getOrderParamValue())) 
                    return new Integer(i); 
            } 
            return new Integer(0); 
        } 
        return null; 
    } 
 
    public void modify(Object element, String property, Object value) { 
        TableItem item = (TableItem) element; 
        IOre ore = (IOre) item.getData(); 
        NameValuePair pair = (NameValuePair) ore.getOrderParams().get(((Integer) value).intValue()); 
        ore.setOrderParamValue(pair.getValue()); 
        viewer.refresh(); 
    } 
});

接下来……哦,原来已经大功告成了,真是太容易了!不相信吗,请看下面的运行结果。

给Eclipse插件的View加上菜单和工具条

Eclipse的每个视图(View)都有自己的菜单和工具条,View通过与自己相关的IViewSite对象与这些东西打交道,确切的说,是通过这个IViewSite对象的IActionBars对象来管理,ActionBars对象负责菜单、工具条和状态栏。

一个典型的View(继承org.eclipse.ui.part.ViewPart)的代码结构会是这样,作为例子,假设我们有三个功能项:Open、Remove和Reload,我们的View是一个简单的表格TableViewer,里面显示一些条目列表,允许用户进行多选:

TableViewer tvResult;
OpenAction openAction;
RemoveAction removeAction;
ReloadAction reloadAction;

public void createPartControl(Composite parent) { 
    //创建视图界面 
     
    //创建菜单 
    createActions(); 
    createMenu(); 
    createContextMenu(); 
    createToolbar(); 
    hookGlobalActions(); 

其中,createActions()是创建必要的IAction对象,这些对象可用在菜单、工具条里;createMenu()的作用是把刚刚创建的IAction对象放进与View相关的MenuManager里,就像前面所说,MenuManager可以通过getViewSite().getActionBars().getMenuManager()方法得到;createToolbar()则是把同样的对象放在工具条里,获得工具条的方法与菜单类似;createContextMenu()则是建立鼠标右键触发的上下文菜单,方法是建立一个新的MenuManager,然后由它建立一个Menu对象,再将Menu对象与控件联系;hookGlobalActions()的作用是把IAction对象与系统菜单(而不是View菜单联系),达到同一菜单项对不同View具有不同响应的效果。下面来看一下具体代码:
package net.sf.solo.actions; 
 
import java.io.IOException; 
import java.util.Iterator; 
import net.sf.solo.model.IInstance; 
import org.eclipse.jface.action.Action; 
import org.eclipse.jface.viewers.ISelectionChangedListener; 
import org.eclipse.jface.viewers.IStructuredSelection; 
import org.eclipse.jface.viewers.SelectionChangedEvent; 
 
public class OpenAction extends Action implements ISelectionChangedListener { 
 
    IStructuredSelection selection; 
 
    public OpenAction() { 
        setEnabled(false); 
    } 
     
    public void run() { 
        for (Iterator iter = selection.iterator(); iter.hasNext();) { 
            IInstance ins = (IInstance) iter.next(); 
            try { 
                //TODO Only in windows can do this. 
                Runtime.getRuntime().exec("cmd /E:ON /c start " + ins.getReferenceURL()); 
            } catch (IOException e) { 
                e.printStackTrace(); 
            } 
        } 
    } 
 
    public void selectionChanged(SelectionChangedEvent event) { 
        selection = (IStructuredSelection) event.getSelection(); 
        setEnabled(selection.size() > 0); 
    } 
 
    public String getText() { 
        return "&Open in browser"; 
    } 
}

上面是一个例子IAction,它的作用是在触发时将用户选中的条目在浏览器里打开。这个类同时还实现了ISelectionChangeAction,这样就可以在用户没有选中任何条目的时候将自己变为不可用。当然,你要把它作为监听器加入某个列表对象的监听器列表,像下面代码里这样:

private void createActions() { 
    openAction = new OpenAction(); 
    removeAction = new RemoveAction(tvResult); 
    reloadAction = new ReloadAction(tvResult); 
    tvResult.addSelectionChangedListener(openAction); 
    tvResult.addSelectionChangedListener(removeAction); 
    tvResult.addSelectionChangedListener(reloadAction); 

注意,最后的三句就是加入监听列表的功能。有些IAction需要改变所监听对象(比如一个TableViewer)的行为,所以要把那个对象作为参数传递给它才行。下面是把IAction对象加入菜单的代码:
private void createMenu() { 
    IMenuManager mgr = getViewSite().getActionBars().getMenuManager(); 
    mgr.add(openAction); 
    mgr.add(removeAction); 
    mgr.add(reloadAction); 

把IAction对象加到工具条的代码几乎完全一样,只是第一句有所不同:
private void createMenu() { 
    IToolBarManager mgr = getViewSite().getActionBars().getToolBarManager(); 
    mgr.add(openAction); 
    mgr.add(removeAction); 
    mgr.add(reloadAction); 
}

上下文菜单就显得有些麻烦了,因为View并没有一个“PopupMenuManager”这样的东西,所以我们只能半手动的来建立:

private void createContextMenu() { 
    MenuManager mgr = new MenuManager(); 
    mgr.setRemoveAllWhenShown(true); 
    mgr.addMenuListener(new IMenuListener() { 
        public void menuAboutToShow(IMenuManager manager) { 
            fillContextMenu(manager); 
        } 
    }); 
    Menu menu = mgr.createContextMenu(tvResult.getControl()); 
    tvResult.getControl().setMenu(menu); 
    getSite().registerContextMenu(mgr, tvResult); 
}

这个MenuManager和我们在createMenu()里通过getMenuManager()得到的是同一个类(但不是同一个实例哦),setRemoveAllWhenShown(true)的作用是清空以前显示的菜单项,当触发了menu事件时,重新填充(fillContextMenu),所以如果你不把removeAllWhenShow置为true的话,每点一下右键你就会看到菜单项多出一倍来。Menu是swt的控件(刚才说的MenuManager、ToolbarManager都是jface里的东西,jface给swt包了一层),用MenuManager可以创建出一个Menu对象,然后我们用表格的setMenu方法将表格控件与Menu控件联系在一起就好了。

最后还有一句,它是为扩展这个上下文菜单用的,例如你可以在plugin.xml里统一指定给某种类型的元素都加上某个菜单项(例如,如果用户选中了一个.zip文件,会多出一个“解压缩”选项)。那么新加的菜单项会出现在上下文菜单的哪里呢,最上方还是最下方,还是……?所以呢,要在fillContextMenu的时候指定一下:

protected void fillContextMenu(IMenuManager manager) { 
    manager.add(openAction); 
    manager.add(removeAction); 
    manager.add(reloadAction); 
    manager.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS)); 
}

前三句都没什么特别,最后一句就是指定了上面我们说的这个“增加点”,这样,你想让后来的菜单放在哪里都行了。

最后,Eclipse的Workbench提供了一些比较通用的系统菜单项,如下:

public static final String [] GLOBAL_ACTIONS = { 
        UNDO, 
        REDO, 
        CUT, 
        COPY, 
        PASTE, 
        PRINT, 
     DELETE, 
        FIND, 
     SELECT_ALL, 
        BOOKMARK 
}; 

当你的焦点在不同的View或Editor里时,同一个系统菜单项会有不同的作用产生。例如在文本编辑器中delete项是删除当前选中的文字,而在你的视图里,你希望delete的作用是删除用户选中的表格条目,刚好是removeAction的功能。所以你要把你的IAction对象和系统菜单挂在一起:
private void hookGlobalActions() {

      IActionBars bars = getViewSite().getActionBars(); 
      bars.setGlobalActionHandler(IWorkbenchActionConstants.DELETE, removeAction); 

注意,要选择语义上比较相近的系统菜单项来挂接,否则会造成用户的困扰。比如你非要把COPY实现为openAction,当用户在系统菜单里选了copy命令,本以为会把当前选中的条目复制到剪贴板,你却给人家打开了这些条目,多滑稽。

好了,菜单方面基本上就这些内容。可以看出,Eclipse的Workbench的确为我们提供了很多方便,特别是如果能够灵活利用plugin来进行定义,不仅可以节约大量的代码,还能让我们始终保持对系统的掌握。所以说,RCP的风行可不是没有道理哦。