假设GMF为你生成的项目名称为com.example.diagram,现在要在右键菜单里增加一个自定义命令,并关联在名为Activity的模型元素上,即只有在Activity类型的元素上点右键,弹出菜单里才有这个自定义命令。此命令的功能是简单的把该Activity的Name属性改为Modified Activity
。实现的步骤如下:
1、如果之前没有创建过,则创建一个名为com.example.diagram.custom的plugin项目(以下简称为custom项目
),新建这个项目的目的是把自己的定制与GMF生成的代码分开;
2、在custom项目里实现org.eclipse.ui.popupMenus扩展点,这样会在右键菜单里多出一个"Change"菜单项,下面有"Name"命令;
<extension
point="org.eclipse.ui.popupMenus">
<objectContribution
adaptable="false"
id="com.example.custom.objectContribution.ActivityEditPart"
objectClass="com.example.diagram.edit.parts.ActivityEditPart">
<menu
id="BMAChange"
label="&Change"
path="additions">
<separator name="group1"/>
</menu>
<action
class="com.example.diagram.popup.ChangeActivityNameAction"
enablesFor="1"
id="com.example.diagram.popup.ChangeActivityNameAction"
label="&Name"
menubarPath="BMAChange/group1"/>
</objectContribution>
</extension>
3、实现上一步里定义的Action类ChangeActivityNameAction,这个类不仅要实现IObjectActionDelegate(popupMenus扩展点的要求),还要继承自AbstractActionDelegate这个类(GMF的要求)。我们要做的是实现doRun()方法,首先取得当前选中的editpart,然后创建一个SetRequest实例,它包含了改变属性操作的所有信息,包括目标对象、属性的名字和新属性值。因为GMF里editpart的getModel()
方法不是业务模型里的元素了,而是View对象,要再调用View#getElement()
才能得到业务模型里的元素,所以代码里我们利用ViewUtil#resolveSemanticElement()
方法直接得到Activity对象。另外,GMF使用了EMFT的Transaction项目来操作模型,所以editpart.getEditingDomain()
方法得到的会是一个TransactionalEditingDomain类型。
有了request,我们用它作为构造参数创建一个SetValueCommand(),这是一个GMF命令(实现org.eclipse.gmf.runtime.common.core.command.ICommand),用来改变属性值。最后要执行这个命令,我们知道command是要用CommandStack来执行的,这样才能undo/redo,但editpart.getDiagramEditDomain().getDiagramCommandStack()得到的CommandStack只能执行GEF的命令(org.eclipse.gef.commands.Command),所以要把我们的command用ICommandProxy()包装一下,这样就没问题了。
public class ChangeActivityNameAction extends AbstractActionDelegate
implements IObjectActionDelegate {
protected void doRun(IProgressMonitor progressMonitor) {
// Get the selected edit part
IStructuredSelection structuredSelection = getStructuredSelection();
Object selection = structuredSelection.getFirstElement();
IGraphicalEditPart editpart = (IGraphicalEditPart) selection;
// Create a command to modify its property
SetRequest request = new SetRequest(
editpart.getEditingDomain(),
ViewUtil.resolveSemanticElement((View) editpart.getModel()),//The semantic model
BmaPackage.Literals.ACTIVITY__NAME,//Name feature of activity
"Modified Activity");//New name value
SetValueCommand command = new SetValueCommand(request);
//Do the work
editpart.getDiagramEditDomain().getDiagramCommandStack().execute(new ICommandProxy(command));
}
}
Update: 可以用IGraphicalEditPart#resolveSemanticElement()直接取得editpart对应的EObject,IGraphicalEditPart#getNotationView()是得到View对象,和getModel()
作用一样。
运行效果如下,选择修改名字命令后,Activity1的名字改为Modified Activity,并且可以undo/redo:
参考资料
GMF提供的Logic例子中CreateLogicElementActionDelegate.java文件
GMF Tips,Change Names Of Newly Created Elements小节
GMF Tutorial Part 3
Update(2007/07/17): GMF更推荐使用IOperationHistory来修改模型,例如在Action的doRun()方法里像下面这样写:
AbstractTransactionalCommand command = new AbstractTransactionalCommand(
editingDomain,
"Modifying the model", Collections.EMPTY_LIST) {
protected CommandResult doExecuteWithResult(
IProgressMonitor monitor, IAdaptable info)
throws ExecutionException {
//在这里修改模型
return CommandResult.newOKCommandResult();
}
};
try {
OperationHistoryFactory.getOperationHistory().execute(command,
new SubProgressMonitor(progressMonitor, 1), null);
} catch (ExecutionException e) {
MindmapDiagramEditorPlugin.getInstance().logError(
"Unable to create model and diagram", e); //$NON-NLS-1$
}
因为在GMF新闻组里提到过(原文链接):
in a GMF application, you should probably never execute commands in a CommandStack, because it will not be worth the effort of coordinating the IUndoContexts applied to these commands and your GMF AbstractTransactionalCommands to ensure that the Undo/Redo menus make sense.
搬家前链接:https://www.cnblogs.com/bjzhanghao/archive/2006/09/06/496394.html
请保留原始链接:https://bjzhanghao.com/p/2782