FROM:http://www.blog.edu.cn/user1/19180/archives/2005/1025573.shtml
4.4 请求说明
处于MVC核心地位的EditPart,负责控制模型和视图,以及接受用户的请求。请求(Request)构成了用户一种用户接口(UI),系统将各种操作(鼠标、键盘)封装成不同的Request传递给EditPart进行处理。
另一个和请求有关的概念是EditPolicy,这可以看成是一种代理,它可以分担EditPart处理请求的责任,增加软件的复用性。前面说过EditPolicy不是必需的,因为所有的请求都可以在EditPart中进行处理,然而使用EditPolicy还有一个好处就是——它可对请求进行进一步的分发(Dispatch),使得结构更清晰更容易理解(但如果你去看Policy的源代码,你会发现其实它也拥有一个getCommand操作,分发就是在那里进行的)。
下面我给出对块状元素的3个请求,并且给出应用哪些EditPolicy可以处理这个请求。
4.4.1 创建一个块状元素
创建一个块状元素的请求是CreateRequest,你可以通过比较RequestConstants.REQ_CREATE来确定这个请求(就是说你可以调用Request.getType来比较)。
LayoutEditPolicy的getCreateCommand函数可以用来处理这个请求。
通过调用CreateRequest.getNewObject方法,你可以创建一个新的元素,而通过调用CreateRequest.getNewObjectType函数可以获得新元素的类型,这是一个Class对象。通过调用CreateRequest.getLocation函数可以获得块状元素的左上点的坐标,调用CreateRequest.getSize函数则可以获得块状元素的大小,或者调用EditPolicy的getConstraintFor函数直接获得Rectangle。如果你用的是XYLayoutEditPolicy(对应XYLayout),那么获得大小和位置的操作是有意义的,如果你使用的是OrderedLayoutEditPolicy(对应ToolbarLayout和FlowLayout等),那么这两个属性将没有意义。这两个Policy分别对应不同的布局方法,不能混用。
4.4.2 重设一个块状元素的边界
重设一个块状元素的边界的请求是ChangeBoundsRequest,它对应两个请求类型:REQ_MOVE和REQ_RESIZE(同样在RequestConstants中定义,下同)。按照GEF的帮助所说,如果新的大小和原大小相同,GEF将产生一个REQ_MOVE请求,否则产生REQ_RESIZE请求,但显然REQ_RESIZE请求包含REQ_MOVE请求的情况。如果你只想处理移动请求,而不想(或者说不允许)处理REQ_RESIZE请求,那么你可以在EditPart中仅仅对REQ_MOVE进行相应,否则你需要对这两个进行处理。如果你使用父亲处理子女的移动请求(通常是这样做的),那么对应的请求类行是REQ_MOVE_CHILDREN和REQ_RESIZE_CHILDREN。
对于父EditPart处理的情况,如果你使用的是XYLayoutEditPolicy,那么XYLayoutEditPolicy.createChangeConstraintCommand函数将用来处理这两个请求,它对应的是XYLayout布局。如果你使用的是诸如流布局一类的布局(也就是使用OrderedLayoutEditPolicy及其子类),那么createMoveChildCommand函数将用来处理移动元素的请求(这类布局不支持子元素的变形),而它的移动策略也不过是改变子元素的顺序,具体的可以参考GEF的API。
通过调用ChangeBoundsRequest的getTransformedRectangle函数获得新的Rectangle(参数是原来的Rectangle,它会进行位置变换)。
4.4.3 删除一个块状元素
删除一个块状元素的请求是GroupRequest(它还能代表其它的请求),其中类型是REQ_DELETE。这个请求只能由被删除的元素进行相应。
ComponentEditPolicy的createDeleteCommand可以处理删除消息。
然而,你可能会发现,即使你完成了所有的代码,但是无论你怎么按下Delete键,元素就是不会被删除(实际上,请求根本就没有产生)。这就是删除请求的不同之处:删除请求的发起者实际上是之前我们忽略的actionBar(工具栏),Delete键是工具栏的一个快捷操作而已。因此为了能让删除操作有效,我们必须定义工具栏。首先要实现工具栏的类,下面的例子能够帮助你实现一个简单的工具栏:
Request简介之SELECTION_FEEDBACK_ROLE
这个角色专门用来处理和选择有关的请求,当一个对象被选中时(可能是由于各种原因被选中),GEF会使用到这个角色。因此,我们可以安装这个角色,来处理这些选择消息。
安装这个角色的方法是,继承并安装SelectionEditPolicy,不过单纯继承GraphicalEditPolicy却更为简便,SelectionEditPolicy是raphicalEditPolicy的子类。所以下面通过继承一个GraphicalEditPolicy,来介绍这个角色。GraphicalEditPolicy并不是抽象类,因此没有抽象函数以待实现。对于常见的应用,我们关心以下几个操作:
showTargetFeedback
eraseTargetFeedback
getTargetEditPart
前两个操作在默认情况下什么也不做,第三个操作默认情况下返回null。首先我们看showTargetFeedback和eraseTargetFeedback的作用,为了说明这个问题,我们假定一个场景,我们希望一个矩形的元素,当我们向其中加入其他模型时,这个元素可以具有“热敏”风格,这个时候,我们可以重载showTargetFeedback函数如下:
- public void showTargetFeedback(Request request){
- if(request.getType().equals(RequestConstants.REQ_ADD)||
- request.getType().equals(RequestConstants.REQ_CREATE)
- ) {
- /*
- * 保存元素的背景色
- * 将矩形元素高亮显示
- */
- }
- }
REQ_ADD是想该矩形元素添加已经存在的模型时的请求,REQ_CREATE是向该元素中创建新的子模型。当然,这里的请求类型还可以是REQ_MOVE、REQ_CLONE、REQ_CONNECTION_START、REQ_CONNECTION_END等等。需要说明的是,REQ_MOVE并非移动这个Policy所在的模型,而是指一些子模型在这个模型内部移动,换句话说就是移动内部模型。然后再看看eraseTargetFeedback函数,它用来还原showTargetFeedback造成的影响,因此比较简单:
- public void eraseTargetFeedback(Request request){
- /*
- * 如果模型被高亮显示,则恢复其原有的背景色
- * 反之什么也不用做
- */
- }
现在再来看看getTargetEditPart函数,这个函数的作用是返回选中的EditPart,在默认情况下,它返回null或者自己,则GEF选中这个EditPart。对于一些特殊需求,我们允许它返回不是自己的EditPart,比如下面的情况:我们希望当我第一次选中一个元素时,则选中这个元素,否则选中它的父亲(容器)。为了达到这个目的,我们可以这样写:
- public EditPart getTargetEditPart(Request request){
- if(request.getType().equals(RequestConstants.REQ_SELECTION)) {
- EditPart part = getHost();
- if(!(part.getParent() instanceof DiagramPart)&&
- part.getSelected()!=EditPart.SELECTED_NONE) {
- part = part.getParent();
- }
- return part;
- }
- return null;
- }
在这里我们假设DiagramPart是根EditPart,因为选中根EditPart没有意义。另一点需要说明的是,getSelected的返回值,包括SELECTED_NONE、SELECTED、SELECTED_PRIMARY,详细地的信息,可以参考GEF的帮助。
- package pku.oo.ui.actions;
- import org.eclipse.gef.ui.actions.ActionBarContributor;
- import org.eclipse.gef.ui.actions.DeleteRetargetAction;
- import org.eclipse.gef.ui.actions.RedoRetargetAction;
- import org.eclipse.gef.ui.actions.UndoRetargetAction;
- import org.eclipse.jface.action.IToolBarManager;
- import org.eclipse.ui.actions.ActionFactory;
- public class DiagramActionBarContributor extends ActionBarContributor {
- protected void buildActions() {
- addRetargetAction(new UndoRetargetAction());
- addRetargetAction(new RedoRetargetAction());
- addRetargetAction(new DeleteRetargetAction());
- }
- protected void declareGlobalActionKeys() {
- // TODO Auto-generated method stub
- }
- public void contributeToToolBar(IToolBarManager toolBarManager) {
- toolBarManager.add(getAction(ActionFactory.UNDO.getId()));
- toolBarManager.add(getAction(ActionFactory.REDO.getId()));
- toolBarManager.add(getAction(ActionFactory.DELETE.getId()));
- }
- }
同时,在plugin.xml中在Extensions里设置好contributorClass,详细请看第一部分。
4.4.4 处理连接(connection)
Connection是个复杂的话题,这里先不阐述处理连接相关的方法(虽然和处理块状元素类似)。在以后的章节中,会专门解决连接的问题(包括绘制、控制、常用操作等等)。
4.4.5 添加EditPolicy
之前说过,每个EditPolicy都扮演一个角色(role),只有正确设置角色,EditPolicy才能正常工作。你可以在EditPart的createEditPolicies中为这个EditPart添加EditPolicy。
下面给出一些角色和它们对应的EditPolicy,其中也包括上面提到的几个EditPolicy:
EditPolicy.LAYOUT_ROLE:对应所有的LayoutEditPolicy,对于具有子EditPart的Part是必须的。
EditPolicy.COMPONENT_ROLE:对应所有的ComponentEditPolicy,元素能够被删除的话,你应该添加这个角色。
EditPolicy.DIRECT_EDIT_ROLE:对应DirectEditManager,它用来处理在位编辑(In place edit),关于在位编辑的话题,也许将来会在一个小品文中叙述。
EditPolicy.GRAPHICAL_NODE_ROLE:对应GraphicalNodeEditPolicy,如果这个块状元素能够被连接附着,那么它应该使用这个policy,我会在讲连接的时候说明这个policy的用法。
EditPolicy.SELECTION_FEEDBACK_ROLE:对应GraphicalEditPolicy,这是一个处理鼠标选中的反馈角色,比如你想另元素具有热敏特性,可以使用这个角色。同样,关于这个角色的话题,也会出现在某个小品文中。
EditPolicy.CONNECTION_ENDPOINTS_ROLE:对应ConnectionEndpointEditPolicy,这是一个关于连接的角色,这个类是可以直接被实例化的。