流程设计器开发四(改变活动的位置部分)

 
       这一节我们来介绍如何在编辑器中移动活动,改变活动的大小和删除活动,在流程控制器中已经安装的策略WorkflowProcessXYLayoutEditPolicy,可以接受移动活动和改变活动大小的请求,但不能接受删除活动的请求,要处理删除活动的请求,必须在活动控制器中安装策略。
       我们还是先来看WorkflowProcessXYLayoutEditPolicy这个类,要实现改变活动大小和位置的功能,应该覆盖父类的createChangeConstraintCommand方法,具体代码如下:
/**Resize or Move AbstractActivity in the WorkflowProcessEditor*/
    protected Command createChangeConstraintCommand(ChangeBoundsRequest request, EditPart child, Object constraint) {
       if (child instanceof AbstractActivityEditPart && constraint instanceof Rectangle){
           //return a command that can move and/or resize a AbstractActivity
           return new AbstractActivitySetConstraintCommand(
                   (AbstractActivity)child.getModel(), request, (Rectangle) constraint);
       }
       return super .createChangeConstraintCommand(request, child, constraint);
    }  
 
在这个方法中,我们创建一个 AbstractActivitySetConstraintCommand 命令,用这个命令来修改活动的位置,大小属性,这个命令的具体代码如下:
package com.example.workflow.commands;
 
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.RequestConstants;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.requests.ChangeBoundsRequest;
 
import com.example.workflow.model.AbstractActivity;
 
/**
 * A command to resize and/or move a shape.
 * The command can be undone or redone.
 */
public class AbstractActivitySetConstraintCommand extends Command{
      
       /** Stores the new size and location. */
       private final Rectangle newBounds;
       /** Stores the old size and location. */
       private Rectangle oldBounds;
       /** A request to move/resize an edit part. */
       private final ChangeBoundsRequest request;
      
       /** AbstractActivity to manipulate. */
       private final AbstractActivity abstractActivity;
      
       /**
        * Create a command that can resize and/or move a AbstractActivity.
        * @param abstractActivity the AbstractActivity to manipulate
        * @param req             the move and resize request
        * @param newBounds the new size and location
        * @throws IllegalArgumentException if any of the parameters is null
        */
       public AbstractActivitySetConstraintCommand(AbstractActivity abstractActivity,
                     ChangeBoundsRequest req,Rectangle newBounds){
              if (abstractActivity == null || req == null || newBounds == null) {
                     throw new IllegalArgumentException();
              }
              this.abstractActivity = abstractActivity;
              this.request = req;
              this.newBounds = newBounds.getCopy();
              setLabel("move / resize");
       }
      
       /* (non-Javadoc)
        * @see org.eclipse.gef.commands.Command#canExecute()
        */
       public boolean canExecute() {
              Object type = request.getType();
              // make sure the Request is of a type we support:
              return (RequestConstants.REQ_MOVE.equals(type)
                            || RequestConstants.REQ_MOVE_CHILDREN.equals(type)
                            || RequestConstants.REQ_RESIZE.equals(type)
                            || RequestConstants.REQ_RESIZE_CHILDREN.equals(type));
       }
      
       /* (non-Javadoc)
        * @see org.eclipse.gef.commands.Command#execute()
        */
       public void execute() {
              oldBounds = new Rectangle(abstractActivity.getLocation(), abstractActivity.getSize());
              redo();
       }
 
       /* (non-Javadoc)
        * @see org.eclipse.gef.commands.Command#redo()
        */
       public void redo() {
              abstractActivity.setSize(newBounds.getSize());
              abstractActivity.setLocation(newBounds.getLocation());
       }
 
       /* (non-Javadoc)
        * @see org.eclipse.gef.commands.Command#undo()
        */
       public void undo() {
              abstractActivity.setSize(oldBounds.getSize());
              abstractActivity.setLocation(oldBounds.getLocation());
       }
 
}
我们知道修改了活动的大小,位置属性之后,活动模型会通知控制器它的LOCATION_PROP,SIZE_PROP发生变化,这可在模型的定义中看到:
public void setLocation(Point newLocation) {
       if (newLocation == null ) {
           throw new IllegalArgumentException();
       }
       location .setLocation(newLocation);
       firePropertyChange( LOCATION_PROP , null , location );
    }
   
   
    public void setSize(Dimension newSize) {
       if (newSize != null ) {
           size .setSize(newSize);
           firePropertyChange( SIZE_PROP , null , size );
       }
    }
活动控制器AbstractActivityEditPart应该根据这些属性的变化来刷新视图,代码如下:
public void propertyChange(PropertyChangeEvent evt) {
       String prop = evt.getPropertyName();     
       if (AbstractActivity. SIZE_PROP .equals(prop)
              ||AbstractActivity. LOCATION_PROP .equals(prop)){
           refreshVisuals();
       }     
    }
refreshVisuals() 方法我们已经在前面实现好了,这样我们运行项目,就可以改变活动的位置和大小了,效果如下:
 
 
接下来,我们介绍如何在编辑器中删除活动,要实现这功能,应该在活动控制器AbstractActivityEditPart中安装能接受删除活动请求的策略,代码如下:
protected void createEditPolicies() {
       //allow removal of the associated model element
       installEditPolicy(EditPolicy. COMPONENT_ROLE , new AbstractActivityComponentEditPolicy());
      
    }
策略 AbstractActivityComponentEditPolicy 的代码如下:
package com.example.workflow.policy;
 
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.editpolicies.ComponentEditPolicy;
import org.eclipse.gef.requests.GroupRequest;
 
import com.example.workflow.commands.AbstractActivityDeleteCommand;
import com.example.workflow.model.AbstractActivity;
import com.example.workflow.model.WorkflowProcess;
 
 
public class AbstractActivityComponentEditPolicy extends ComponentEditPolicy{
 
       protected Command createDeleteCommand(GroupRequest deleteRequest) {
              Object parent = getHost().getParent().getModel();
              Object child = getHost().getModel();
              if(parent instanceof WorkflowProcess && child instanceof AbstractActivity){
                     return new AbstractActivityDeleteCommand((WorkflowProcess)parent,(AbstractActivity)child);
              }
              return super.createDeleteCommand(deleteRequest);
       }
 
}
这个策略继承了ComponentEditPolicy类,并且覆盖了createDeleteCommand方法,在这个方法中,我们创建了AbstractActivityDeleteCommand命令,这个命令完成从流程模型中删除当前活动模型,我们知道在活动模型中,还维护着输入转移和输出转移两个列表,而转移对象中又维护着转移的起始活动和终止活动,由于删除了活动,转移就成了只有源点或者只要目标点,因此,我们还应该从还维护着这些转移的活动中删除这些转移,该命令的代码如下:
package com.example.workflow.commands;
 
import java.util.Iterator;
import java.util.List;
 
import org.eclipse.gef.commands.Command;
 
import com.example.workflow.model.AbstractActivity;
import com.example.workflow.model.Transition;
import com.example.workflow.model.WorkflowProcess;
 
 
/**
 * A command to remove a AbstractActivity from its parent.
 * The command can be undone or redone.
 */
public class AbstractActivityDeleteCommand extends Command{
      
       /** AbstractActivity to remove. */
       private final AbstractActivity child;
       /** WorkflowProcess to remove from. */
       private final WorkflowProcess parent;
       /** Holds a copy of the outgoing transitions of child. */
       private List sourceTransitions;
       /** Holds a copy of the incoming transitions of child. */
       private List targetTransitions;
       /** True, if child was removed from its parent. */
       private boolean wasRemoved;
 
       /**
        * Create a command that will remove the AbstractActivity from its parent.
        * @param parent the WorkflowProcess containing the child
        * @param child    the AbstractActivity to remove
        * @throws IllegalArgumentException if any parameter is null
        */
       public AbstractActivityDeleteCommand(WorkflowProcess parent, AbstractActivity child) {
              if (parent == null || child == null) {
                     throw new IllegalArgumentException();
              }
              setLabel("activity deletion");
              this.parent = parent;
              this.child = child;
       }
 
       /**
        * Reconnects a List of Transitions with their previous endpoints.
        * @param transitions a non-null List of connections
        */
       private void addConnections(List transitions) {
              for (Iterator iter = transitions.iterator(); iter.hasNext();) {
                     Transition tran = (Transition) iter.next();
                     tran.reconnect();
              }
       }
 
       /* (non-Javadoc)
        * @see org.eclipse.gef.commands.Command#canUndo()
        */
       public boolean canUndo() {
              return wasRemoved;
       }
 
       /* (non-Javadoc)
        * @see org.eclipse.gef.commands.Command#execute()
        */
       public void execute() {
              // store a copy of incoming & outgoing transitions before proceeding
              sourceTransitions = child.getSourceTransitions();
              targetTransitions = child.getTargetTransitions();
              redo();
       }
 
       /* (non-Javadoc)
        * @see org.eclipse.gef.commands.Command#redo()
        */
       public void redo() {
              // remove the child and disconnect its connections
              wasRemoved = parent.removeChild(child);
              if (wasRemoved) {
                     removeTransitions(sourceTransitions);
                     removeTransitions(targetTransitions);
              }
       }
 
       /**
        * Disconnects a List of Transitions from their endpoints.
        * @param transitions a non-null List of transitions
        */
       private void removeTransitions(List transitions) {
              for (Iterator iter = transitions.iterator(); iter.hasNext();) {
                     Transition tran = (Transition) iter.next();
                     tran.disconnect();
              }
       }
 
       /* (non-Javadoc)
        * @see org.eclipse.gef.commands.Command#undo()
        */
       public void undo() {
              // add the child and reconnect its transitions
              if (parent.addChild(child)) {
                     addConnections(sourceTransitions);
                     addConnections(targetTransitions);
              }
       }
}
 
这个命令中从流程模型中删除了活动模型,流程模型通知控制器,它的CHILD_REMOVED_PROP属性发生了变化,要控制器刷新它维护的活动集。
要实现删除操作,我们必须通过删除菜单或者键盘的删除键,但现在编辑器还不支持这些功能,无论是通过删除菜单还是删除键,都是执行了一个Action,由这个Action来执行我们定义的删除命令(关于Action和菜单的实现,我们在以后会讲),这儿只介绍如何让编辑器支持键盘操作,要实现键盘操作,在WorkflowProcessEditor类中定义一个KeyHandler 类型的成员变量sharedKeyHandler;
接下来定义一个方法:
protected KeyHandler getCommonKeyHandler() {
    if ( sharedKeyHandler == null ) {
       sharedKeyHandler = new KeyHandler();
       sharedKeyHandler .put(
           KeyStroke.getPressed(SWT. DEL , 127, 0),
           getActionRegistry().getAction(ActionFactory. DELETE .getId()));     
    }
    return sharedKeyHandler ;
}
该方法的作用是定义我们支持的键名,上面定义了删除键,并且把这个键和删除 Action 邦定,当按删除键,就执行对应的 Action sharedKeyHandler 可以供编辑器和大纲视图公用,关于大纲视图在以后会讲。
接下来还需要在编辑器视图中注册这些这些公共键,代码如下:
protected void configureGraphicalViewer() {     
       super .configureGraphicalViewer();
       GraphicalViewer viewer = getGraphicalViewer();
       viewer.setEditPartFactory( new WorkflowProcessEditPartFactory());
       viewer.setRootEditPart( new ScalableFreeformRootEditPart());
      
       getGraphicalViewer().setKeyHandler( new GraphicalViewerKeyHandler(getGraphicalViewer())
       .setParent(getCommonKeyHandler()));
    }  
这样运行项目,我们就可以用键盘的删除键来删除活动了,我们注意到无论我们向编辑器中增加活动,还是删除活动,编辑器的状态始终没变,不像其它编辑器修改之后,提示编辑器处于未保存状态,我们要实现这个功能其实很简单,只需在WorkflowProcessEditor类中覆盖父类的方法commandStackChanged,代码如下:
public void commandStackChanged(EventObject event) {
       firePropertyChange(IEditorPart. PROP_DIRTY );
       super .commandStackChanged(event);
    }
其实这个方法的原理很简单,当编辑器的命令堆栈发生改变时,修改编辑器状态为未保存。我们知道我们进行增加活动,删除活动等操作时,都是执行了一个个命令,而这些命令都保存在编辑器的命令堆栈里,所以我们每执行一个命令,编辑器的命令堆栈就发生改变。
再运行项目,我们就可以看到在编辑器处于已保存状态时,我们再进行操作,例如增加活动,删除活动,编辑器的状态就变成未保存了,至于在未保存状态,点保存按钮,还是未保存,这在以后,我们会解决。下一节,我们将介绍如何在活动之间增加和删除转移,如何在转移上增加拐点。
 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值