【原创】GEF的命令及其它实现(二十五)

  GEF是一个完善的图形编辑框架,它提供了图形编辑的解决方案,用户可以通过它的机制构造出功能非常强大的图形编辑器,但GEF还是比较复杂的框架,在提供了图形编辑的各个环节松耦合的灵活性之时,也带来了用户必须了解各环节实现的学习时间。

  学习GEF框架,读者除了需要掌握模型、视图和控制器外,还应该了解请求和编辑策略、命令以及和Eclipse相关的其它技术等。

  命 令

  为了提高代码的重用性,以及实现GEF编辑器的功能,GEF采用命令模式提供了对模型的操作方式。

  命令的功能

  在EditPolicies中新建了命令(Commands),如果返回的命令不为空,GEF框架会执行命令,对模型进行相应的修改。

  Command是GEF中的一个抽象类,主要实现如下几个方法。

  1. execute:这是命令的执行方法,当请求结束并获得Command后,GEF框架会负责执行此方法。

  2. undo:对模型修改后,可以通过undo进行撤销。

  3. redo:当用户撤销后,能通过redo重复上一次的操作。

  命令的实现

  GEF通过一个命令堆栈(CommandStack)保存Command对象。用户可以通过命令堆栈撤销或重复对模型所做的操作。下面代码演示模型位置改变的命令,如例程1所示。

  例程1 MoveNodeCommand.java

  public class MoveNodeCommand extends Command {

  //命令对应的模型

  private Node node;

  //原始位置

  private Point oldPos;

  //新位置

  private Point newPos;

  //设置新位置

  public void setLocation(Point p) {

  this.newPos = p;

  }

  //设置命令对应的模型

  public void setNode(Node node) {

  this.node = node;

  }

  //执行命令

  public void execute() {

  //保存模型原来的位置

  oldPos = this.node.getLocation();

  node.setLocation(newPos);

  }

  public String getLabel() {

  return "Move Node";

  }

  //重复上一操作

  public void redo() {

  this.node.setLocation(newPos);

  }

  //撤销设置新位置的操作

  public void undo() {

  this.node.setLocation(oldPos);

  }

  }

  通过命令堆栈(CommandStack),GEF编辑器能轻松完成用户的撤销和重做操作,GEF框架会维护命令堆栈。

  模 型 同 步

  为了保证数据和展现的同步,必须要有一种消息通知机制,当模型被修改时通知相应的视图进行更新,下面将介绍如何实现这种消息机制。

  模型同步的功能

  模型是GEF中的实体,当模型的属性被修改后,控制器会监听到模型属性改变的事件,然后通知视图进行更新,模型和视图之间没有直接的联系,如图1所示。

13081368_200807210854431.gif

图1 模型、控制器和视图的关系

  要通过事件的通知机制获得模型同步的步骤如下。

  1. 模型中包含注册监听器的机制,可以通过PropertyChangeSupport和PropertyChangeListener机制添加模型监听器。

  2. 把控制器注册为模型监听器,在此通过控制器实现PropertyChangeListener接口,并把自己添加到模型的监听器即可。

  3. 当模型触发属性改变的事件后,控制器监听到事件并相应地更新视图。

  另外,并不是模型的所有属性的修改都必须通知控制器,一般情况下,当模型的属性更改后要引起视图的更新时,必须触发事件通知控制器,因为模型和视图之间没有任何的关联。

  模型同步的实现

  典型的模型对象会包含PropertyChangeSupport类型的成员变量,用来触发事件,通知监听器即控制器。当模型的属性被修改后,控制器将更新视图的显示。例如,某个节点的名字修改后,将要触发相应的事件,通知控制器,如下代码如示。

  public void setName(String name) {

  if (this.name.equals(name)) {

  return;

  }

  this.name = name;

  //当模型名字改变后,通知监听器更新名字的显示

  firePropertyChange(PROP_NAME, null, name);

  }

  当模型的名字改变后,将通过firePropertyChange方法触发模型名字被修改的事件,其中第一个参数为事件类型标识(唯一的标识),表示模型的什么属性被改变,第二个参数为改变前的值,第三个参数为改变后的新值。

  模型触发事件后,控制器将监听到模型属性修改的事件,通过事件的类型,即firePropertyChange方法的第一个参数判断是哪一个属性被修改,然后执行相应的操作,如下代码所示。

  //监听模型属性改变

  public void propertyChange(PropertyChangeEvent evt) {

  if (evt.getPropertyName().equals(Node.PROP_LOCATION))

  //更新视图

  refreshVisuals();

  else if (evt.getPropertyName().equals(Node.PROP_NAME))

  refreshVisuals();

  else if (evt.getPropertyName().equals(Node.PROP_INPUTS))

  //更新连接

  refreshTargetConnections();

  else if (evt.getPropertyName().equals(Node.PROP_OUTPUTS))

  refreshSourceConnections();

  }

  当控制器监听到模型的改变事件后,将会通过视图的更新,从而实现了模型数据和视图显示的同步。

  选 项 板

  选项板提供了用户选择模型元素,并提供相应的机制把模型和视图加入图形编辑器的能力。

  选项板(PaletteRoot)的功能

  GEF是一个图形编辑框架,它的主编辑区域是一个图形的视图(类似JFace的Viewer),另外,用户还可以选择选项板上相应的模型,把选择的模型对应的视图加入到图形编辑区域进行编辑。

  在GEF中,图形编辑器实现类的getPaletteRoot 方法将返回一个PaletteRoot对象,用户可以在PaletteRoot中添加模型选项入口项(ToolEntry)。例如,在选择板中加入一个连线模型的入口,可以加入“new ConnectionCreationToolEntry("Connection", "Create a connection", null, null, null);”,ToolEntry的参数描述了选项板中选项的显示标签、显示图标、提示信息、模型的模板类及模型的创建工厂类等信息。

  选项板的实现

  在选项板中创建模型的入口,能添加模型的选择项(ToolEntry)和模型分组项(PaletteDrawer),代码如例程2所示。

  例程2 PaletteFactory.java

  public class PaletteFactory {

  public static PaletteRoot createPalette() {

  //新建选项板

  PaletteRoot paletteRoot = new PaletteRoot();

  //添加选项板中的模型选项

  paletteRoot.addAll(createCategories(paletteRoot));

  return paletteRoot;

  }

  private static List createCategories(PaletteRoot root) {

  List categories = new ArrayList();

  //添加分组选项

  categories.add(createControlGroup(root));

  categories.add(createComponentsDrawer());

  return categories;

  }

  private static PaletteContainer createControlGroup(PaletteRoot root) {

  PaletteGroup controlGroup = new PaletteGroup("Control Group");

  List entries = new ArrayList();

  //添加取消选择模型项

  ToolEntry tool = new SelectionToolEntry();

  entries.add(tool);

  //设置SelectionToolEntry为默认选项

  root.setDefaultEntry(tool);

  //添加连线模型选项入口

  tool = new ConnectionCreationToolEntry("Connection", "Create a connection",

  null, null, null);

  entries.add(tool);

  controlGroup.addAll(entries);

  return controlGroup;

  }

  private static PaletteContainer createComponentsDrawer() {

  //添加组件分组页

  PaletteDrawer drawer = new PaletteDrawer("Components");

  List entries = new ArrayList();

  //添加Node模型选项入口

  ToolEntry tool = new CombinedTemplateCreationEntry("Node", "Create a new

  Node", Node.class, new SimpleFactory(

  Node.class), null, null);

  entries.add(tool);

  drawer.addAll(entries);

  return drawer;

  }

  }

  上面代码加入了连线模型、分组项、取消选择项和一个节点项(Node模型)。建立的选项板如图2所示。

13081368_200807210854432.jpg

图2 选项板

  下载(com.free.platform.umleditor.demo.rar)

fj.pngimage001.gif

fj.pngimage002.jpg

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/13081368/viewspace-405460/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/13081368/viewspace-405460/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值