流程设计器开发五(移动和删除活动部分)

      这一节我们来介绍如何在编辑器中移动活动,改变活动的大小和删除活动,在流程控制器中已经安装的策略WorkflowProcessXYLayoutEditPolicy,可以接受移动活动和改变活动大小的请求,但不能接受删除活动的请求,要处理删除活动的请求,必须在活动控制器中安装策略。

       我们还是先来看WorkflowProcessXYLayoutEditPolicy这个类,要实现改变活动大小和位置的功能,应该覆盖父类的createChangeConstraintCommand方法,具体代码如下:

/** */ /**ResizeorMoveAbstractActivityintheWorkflowProcessEditor*/
    
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
           returnnew AbstractActivitySetConstraintCommand(
                   (AbstractActivity)child.getModel(), request, (Rectangle) constraint);
       }

       returnsuper.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发生变化,这可在模型的定义中看到:

publicvoid setLocation(Point newLocation)  {
       
if (newLocation == null{
           thrownew IllegalArgumentException();
       }

       location.setLocation(newLocation);
       firePropertyChange(LOCATION_PROP, 
null, location);
    }

    
    
    publicvoid setSize(Dimension newSize) 
{
       
if (newSize != null{
           size.setSize(newSize);
           firePropertyChange(SIZE_PROP, 
null, size);
       }

    }

活动控制器AbstractActivityEditPart应该根据这些属性的变化来刷新视图,代码如下:
publicvoid propertyChange(PropertyChangeEvent evt)  {
       String prop 
= evt.getPropertyName();      
       
if(AbstractActivity.SIZE_PROP.equals(prop) 
              
||AbstractActivity.LOCATION_PROP.equals(prop)){
           refreshVisuals();
       }
      
    }

而refreshVisuals()方法我们已经在前面实现好了,这样我们运行项目,就可以改变活动的位置和大小了,


接下来,我们介绍如何在编辑器中删除活动,要实现这功能,应该在活动控制器AbstractActivityEditPart中安装能接受删除活动请求的策略,代码如下:

protectedvoid 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, 
1270),
           getActionRegistry().getAction(ActionFactory.DELETE.getId()));      
    }

    returnsharedKeyHandler;
}

该方法的作用是定义我们支持的键名,上面定义了删除键,并且把这个键和删除Action邦定,当按删除键,就执行对应的Action,sharedKeyHandler可以供编辑器和大纲视图公用,关于大纲视图在以后会讲。
接下来还需要在编辑器视图中注册这些这些公共键,代码如下:
protectedvoid configureGraphicalViewer()  {      
       
super.configureGraphicalViewer();
       GraphicalViewer viewer 
= getGraphicalViewer();
       viewer.setEditPartFactory(
new WorkflowProcessEditPartFactory());
       viewer.setRootEditPart(
new ScalableFreeformRootEditPart());
       
       getGraphicalViewer().setKeyHandler(
new GraphicalViewerKeyHandler(getGraphicalViewer())
       .setParent(getCommonKeyHandler()));
    }
  

这样运行项目,我们就可以用键盘的删除键来删除活动了,我们注意到无论我们向编辑器中增加活动,还是删除活动,编辑器的状态始终没变,不像其它编辑器修改之后,提示编辑器处于未保存状态,我们要实现这个功能其实很简单,只需在WorkflowProcessEditor类中覆盖父类的方法commandStackChanged,代码如下:
publicvoid commandStackChanged(EventObject event)  {
       firePropertyChange(IEditorPart.PROP_DIRTY);
       
super.commandStackChanged(event);
    }

其实这个方法的原理很简单,当编辑器的命令堆栈发生改变时,修改编辑器状态为未保存。我们知道我们进行增加活动,删除活动等操作时,都是执行了一个个命令,而这些命令都保存在编辑器的命令堆栈里,所以我们每执行一个命令,编辑器的命令堆栈就发生改变。
再运行项目,我们就可以看到在编辑器处于已保存状态时,我们再进行操作,例如增加活动,删除活动,编辑器的状态就变成未保存了,至于在未保存状态,点保存按钮,还是未保存,这在以后,我们会解决。下一节,我们将介绍如何在活动之间增加和删除转移,如何在转移上增加拐点。

你可能感兴趣的:(流程设计器开发五(移动和删除活动部分))