重定向Retarget Action 小结 博客分类: eclipseGEF 菜单重定向retarget action
根据资料系统整理、明确一下Retarget Action这个概念(以在GEF中的应用为例)。
概念
它是一种具有一定语义但没有实际功能的action。它唯一的作用是在主菜单或是主工具条上占据一个位置,编辑器将具有实际功能的action映射到这个Retarget Action上,当编辑器被激活时,这个Retarget action将具有那个action的功能。
举例,eclipse提供了IWorkbenchActionConstants.COPY这个retarget Action,它的文字、图标登时预先定义好的,而ABCAction是一个具体的动作类,通过在某个editor中 getEditorSite().getActionBars().setGlobalActionHandler(IWorkbenchActionConstants.COPY, ABCAction)就实现了映射。
一般地,eclipse自带的action(redo/undo等)绑定到eclipse自带的常量id上。自己定义的action绑定到自己的常量id上。当然,也可以交叉。
Eclipse引入RetargetAction的目的是为了尽量减少菜单/工具条的重建消耗,利用用户使用上的一致性。
重定向操作分类
重定向的操作分为eclipse中已定义的操作和未定义的操作,已定义的操作如redo/undo等;未定义的操作就是自己需要定义的操作。
重定向绑定id方式
1通过contributorClass的实现类,在该类内部的buildActions()方法中新建,并用addRetargetAction()方法绑定。
2不用buildActions内创建新类,隐含在GEF的editor内部的createActions()方法中调用super.createActions()也可以创建(对于非eclipse定义的操作,我们在createActions()中自己添加)。这时可以在contributorClass类的declareGlobalActionKey()方法通过调用addGlobalActionKey()来绑定。
3不利用contributorClass类,而在GEF的编辑器初始化时,在createActions()方法中创建action添加到actionRegistry中,并利用类似下面的代码绑定操作:
比如,在initializeGraphicalViewer()方法中通过getEditorSite().getActionBars()得到actionBars,并通过getActionRegistry()方法得到actionRegistry。
//………
//准备从ActionRegistry中找actionId对应的处理动作
ActionRegistry registry = getActionRegistry();
//actionBar上按钮的重定向
IActionBars bars = this.getEditorSite().getActionBars();
IToolBarManager toolBarManager = bars.getToolBarManager();
//action的id
String id = ActionFactory.UNDO.getId();
bars.setGlobalActionHandler(id, registry.getAction(id));
//动作绑定到工具栏上
toolBarManager.add(registry.getAction(id));
GEF编辑器菜单/工具条
与视图不同编辑器没有自己的菜单和工具条,它的菜单只能添加到主菜单内,因此在扩展编辑器的时候,元素有一个contributorClass属性。通过这个属性定义的类的buildActions()方法将菜单重定向到主菜单上。
protectedvoid buildActions() {
addRetargetAction(new UndoRetargetAction());
addRetargetAction(new RedoRetargetAction());
addRetargetAction(new DeleteRetargetAction());
}
publicvoid contributeToToolBar(IToolBarManager toolBarManager) {
toolBarManager.add(getAction(ActionFactory.UNDO.getId()));
toolBarManager.add(getAction(ActionFactory.REDO.getId()));
}
publicvoid contributeToMenu(IMenuManager menuManager)
{
super.contributeToMenu(menuManager);
IMenuManager mgr = new MenuManager("&Node","Node");
//插入到主菜单上“编辑”的后面
menuManager.insertAfter(IWorkbenchActionConstants.M_EDIT, mgr);
mgr.add(getAction(IConstants.ACTION_A));
mgr.add(getAction(IConstants.ACTION_B));
mgr.add(getAction(IConstants.ACTION_C));
}
/*
* 对于已经有的Retarget Action,我们应该在这个方法中调用addGlobalActionKey()方法来声明
* 当编辑器被激活时,在globalActionKeys中的id指向的action将被映射到regargetAction上。
* 不再需要setGlobalActionHandler("id",handler).因此上面buildActions()中可以删除,变为下面形式
*/
protectedvoid declareGlobalActionKeys() {
addGlobalActionKey(ActionFactory.UNDO.getId());
addGlobalActionKey(ActionFactory.REDO.getId());
addGlobalActionKey(ActionFactory.DELETE.getId());
}
创建自己的action
在GEF内没有定义的相应的action,这些action是这个编辑器所独有的。假定,我们选择的3个figure,要给这3个figure统一设定优先级属性。即当选中这几个figure时,我们插入到主菜单中的设定优先级的action可用。
我们建立的action应该是gef中SelectionAction的子类,因为我们需要对选中的figure操作。还需要将action添加到selectionActions中。
privatevoid createMyActions() {
// subSelectionAction1应该是gef中SelectionAction的子类
IAction subSelectionAction1 = null;
subSelectionAction1.setId("");
getActionRegistry().registerAction(subSelectionAction1);
getSelectionActions().add(subSelectionAction1.getId());
IAction action2 = null;
action2.setId("");
getActionRegistry().registerAction(action2);
getSelectionActions().add(action2.getId());
}
这个selectionAction需要自己实现calculateEnabled()方法。另外,因为在GEF编辑器中的动作都是支持redo/undo的。因此我们需要在这个action.run()的时候创建命令并推入命令栈,action的运行是靠命令,而不是直接运行。如果选中的figure有3个,可能需要创建3个命令,这时为执行redo/undo也能一次性操作,而不是一个一个的来,可能需要一个CompoundCommand.将3个命令组合成一个命令。注意的是:在创建命令之前判断选中的figure对应的editpart(getSelectedObjects())是否是可以执行action的对象。
GEF编辑器上下文菜单
在GEF Editor中的configureGraphicalViewer()方法中,添加代码
//右键菜单
MyContextMenuProvider provider = new MyContextMenuProvider(getGraphicalViewer(), getActionRegistry());
this.getGraphicalViewer().setContextMenu(provider);
即可。
其中MyContextMenuProvider是gef中contexMenuProvider的子类(而contextMenuProvider是jface.action中MenuManager的子类),我们需要实现buildContextMenu()方法,在该方法中一般第一句调用GEFActionConstantS.addStandardActionGroups(menu);以对上下文菜单项添加默认分组。然后将action添加到分组中(action复用actionRegistry中的action)。