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

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

file

一个典型的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的风行可不是没有道理哦。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2005/01/14/92122.html

[Eclipse]国际化你的应用程序(上)

file

记得几年前汉化软件一般是用二进制编辑工具在编译后的文件中查找和替换英文单词,这个过程需要使用很多技巧,非常的麻烦。而现在,在开发时对应用程序进行国际化处理已经越来越成为一个必不可少的步骤了。例如这次我参与的项目是给台湾客户做的,他们要求英文和繁体中文两个版本,幸好我们使用的开发工具是Eclipse,利用它的国际化功能可以很方便的将写在代码里的字符串提出到独立的资源文件中,这里用一个简单的例子说明一下这个过程。

在Eclipse里建立一个名为nls-test的工程(也可以国际化已有工程),新建一个类NLSTest,内容如下:

public class NLSTest {
   public NLSTest() {
      String str1="Hello world!";
      System.out.println(str1);
   }
   public static void main(String[] args) {
       new NLSTest();
   }
}

现在,这个类的输出是在代码里写死的,如果要改变就必须修改代码然后重新编译。下面我们利用Eclipse解决这个问题。在导航器(Package Explorer)里右键单击这个文件,选择Source -> Externalize Strings,就会打开一个对话框,在这里列出了该类中尚未国际化的字符串,见下图。

file
图1 国际化向导第一步

单击每个字符串前面的小方块可以选择对该串 1、进行国际化处理 2、永不进行国际化处理 3、这次不处理。我们选择要对这个字符串进行国际化处理,并把它的Key修改为比较好理解的名称hello,注意在对话框最上方可以指定一个通用的前缀,这个值会加在每个Key前面作为最终写在资源文件(扩展名是.properties)里的Key名称。好,按下一步继续。

在这里要指定properties文件的名称,如果需要的话要指定一个用于从properties文件中取资源的类,一般是XXXMessages的格式,再按下一步,会显示一个所作更改的确认列表,确认后按Finish按钮完成向导。

可以看到Eclipse为我们生成了NLSTestMessages.java和NLSTest.properties,打开后者会看到里面只有这么一句:

NLSTest.hello=Hello world!

而前者NLSTestMessages的作用是根据参数Key从后者取对应的字符串值,看一看现在的NLSTest.java就知道了,它的内容现在是这样的(只列出构造方法,main方法同上):

public NLSTest() {
    String str1=NLSTestMessages.getString("NLSTest.hello"); //$NON-NLS-1$
    System.out.println(str1);
}

后面的注释是Eclipse自己用的,标有这个注释的字符串在国际化将被忽略。注释中的数字1表示要忽略的是该行中第一个字符串,如果一行语句里有多个字符串被忽略,将会有多个这样的注释,但数字会各不相同,像这样:

//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 

好了,现在这个类的国际化处理就算完成了。要想让这个类可以根据用户所在地区输出不同语言的结果,可以在NLSTest.properties同一目录下创建名为NLSTest_XX.properties的文件,其中XX表示国家名称,例如中国是zh_CN或zh_TW,法国是FR等等。新创建的文件里也要有和原来文件相同的名值对,但值是不同语言的,NLSTestMessages类会根据用户机器的地区设置值自动从不同的资源文件里取值,这样就达到了国际化的目的。要在自己的机器上测试运行结果,可以在Eclipse的运行设置里面加上这样的参数:-nl zh_TW,这样就不用费力气设置区域了。(2012/5/4更新:此方法可能有误,在Eclipse3.6里是启动程序时在VM arguments里加上-Duser.language=XXX即可以所需要的语言环境启动程序)

国际化的原理很简单,Eclipse提供的这个功能使国际化变得更容易了。不过关于国际化还有一些细节问题,包括对含参数资源的处理,字符编码处理等等,下篇将对它们进行讨论。

搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2004/08/08/31262.html

[转载]HibernatePlugIn for Struts

这个Plugin的作用是在Struts应用程序启动时进行hibernate的初始化操作,原文请移步 HibernatePlugIn for Struts

步骤很简单:

1、在struts-config.xml里增加:

<plug-in className="org.haree.struts.HibernatePlugIn"> 
  <!-- 'path-to-config-file' is relative to the root of the class 
       path.  It MUST start with a '/'. The default is 
       "/hibernate.cfg.xml --> 
  <set-property property="configFilePath" value="path-to-config-file" /> 
  <set-property property="storeInServletContext" value="true-or-false" /> 
</plug-in> 

2、HibernatePlugIn.java的内容

package org.haree.struts; 

import java.net.URL; 

import javax.servlet.ServletContext; 
import javax.servlet.ServletException; 

import net.sf.hibernate.SessionFactory; 
import net.sf.hibernate.cfg.Configuration; 

import org.apache.commons.logging.Log; 
import org.apache.commons.logging.LogFactory; 
import org.apache.struts.action.ActionServlet; 
import org.apache.struts.action.PlugIn; 
import org.apache.struts.config.ModuleConfig; 

/** 
 * Implements the <code>PlugIn</code> interface to configure the Hibernate 
 * data persistence library.  A configured 
 * <code>net.sf.hibernate.SessionFactory</code> is stored in the 
 * <code>ServletContext</code> of the web application unless the property 
 * <code>storedInServletContext</code> is set to <code>false</code>. 
 * 
 * <p> 
 * <plugin class="net.sf.hibernate.plugins.struts.HibernatePlugIn"> 
 *   <set-property name="configFilePath&quot" 
 *                value="path-to-config-file"/> 
 *   <set-property name="storedInServletContext&quot" 
 *                value="true-or-false"/> 
 * </plugin> 
 * 
 * @author  <a href="mailto:bhandy@users.sf.net">Bradley M. Handy</a> 
 * @version 1.0 
 */ 
public class HibernatePlugIn implements PlugIn { 

    /** 
     * the key under which the <code>SessionFactory</code> instance is stored 
     * in the <code>ServletContext</code>. 
     */ 
    public static final String SESSION_FACTORY_KEY 
            = SessionFactory.class.getName(); 

    private static Log _log = LogFactory.getLog(HibernatePlugIn.class); 

    /** 
     * indicates whether the <code>SessionFactory</code> instance will be stored 
     * in the <code>ServletContext</code>, or not. 
     */ 
    private boolean _storedInServletContext = true; 

    /** 
     * the path to the xml configuration file.  the path should start with a 
     * '/' character and be relative to the root of the class path. 
     * (DEFAULT:  "/hibernate.cfg.xml") 
     */ 
    private String _configFilePath = "/hibernate.cfg.xml"; 

    private ActionServlet _servlet = null; 
    private ModuleConfig _config = null; 
    private SessionFactory _factory = null; 

    /** 
     * Destroys the <code>SessionFactory</code> instance. 
     */ 
    public void destroy() { 
        _servlet = null; 
        _config = null; 

        try { 
            _log.debug("Destroying SessionFactory"); 

            _factory.close(); 

            _log.debug("SessionFactory destroyed"); 
        } catch (Exception e) { 
            _log.error("Unable to destroy SessionFactory(exception ignored)", 
                    e); 
        } 
    } 

    /** 
     * Initializes the <code>SessionFactory</code>. 
     * @param servlet the <code>ActionServlet</code> instance under which the 
     *        plugin will run. 
     * @param config the <code>ModuleConfig</code> for the module under which 
     *        the plugin will run. 
     */ 
    public void init(ActionServlet servlet, ModuleConfig config) 
    throws ServletException { 
        _servlet = servlet; 
        _config = config; 

        initHibernate(); 
    } 

    /** 
     * Initializes Hibernate with the config file found at 
     * <code>configFilePath</code>. 
     */ 
    private void initHibernate() throws ServletException { 
        Configuration configuration = null; 
        URL configFileURL = null; 
        ServletContext context = null; 

        try { 
            configFileURL = HibernatePlugIn.class.getResource(_configFilePath); 

            context = _servlet.getServletContext(); 

            if (_log.isDebugEnabled()) { 
                _log.debug("Initializing Hibernate from " 
                        + _configFilePath + ""); 
            } 

            configuration = (new Configuration()).configure(configFileURL); 
            _factory = configuration.buildSessionFactory(); 

            if (_storedInServletContext) { 
                _log.debug("Storing SessionFactory in ServletContext"); 

                context.setAttribute(SESSION_FACTORY_KEY, _factory); 
            } 

        } catch (Throwable t) { 
            _log.error("Exception while initializing Hibernate."); 
            _log.error("Rethrowing exception", t); 

            throw (new ServletException(t)); 
        } 
    } 

    /** 
     * Setter for property configFilePath. 
     * @param configFilePath New value of property configFilePath. 
     */ 
    public void setConfigFilePath(String configFilePath) { 
        if ((configFilePath == null) || (configFilePath.trim().length() == 0)) { 
            throw new IllegalArgumentException( 
                    "configFilePath cannot be blank or null."); 
        } 

        if (_log.isDebugEnabled()) { 
            _log.debug("Setting 'configFilePath' to '" 
                    + configFilePath + "'"); 
        } 

        _configFilePath = configFilePath; 
    } 

    /** 
     * Setter for property storedInServletContext. 
     * @param storedInServletContext New value of property storedInServletContext. 
     */ 
    public void setStoredInServletContext(String storedInServletContext) { 
        if ((storedInServletContext == null) 
                || (storedInServletContext.trim().length() == 0)) { 
            storedInServletContext = "false"; 
        } 

        if (_log.isDebugEnabled()) { 
            _log.debug("Setting 'storedInServletContext' to '" 
                    + storedInServletContext + "'"); 
        } 

        _storedInServletContext 
                = new Boolean(storedInServletContext).booleanValue(); 
    } 

}