流程設計器開發四(改變活動的位置部分)

 
       這一節我們來介紹如何在編輯器中移動活動,改變活動的大小和刪除活動,在流程控制器中已經安裝的策略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, 127, 0),
           getActionRegistry().getAction(ActionFactory.DELETE.getId()));     
    }
    returnsharedKeyHandler;
}
該方法的作用是定義我們支持的鍵名,上面定義了刪除鍵,並且把這個鍵和刪除Action邦定,當按刪除鍵,就執行對應的ActionsharedKeyHandler可以供編輯器和大綱視圖公用,關於大綱視圖在以後會講。
接下來還需要在編輯器視圖中註冊這些這些公共鍵,代碼如下:
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);
    }
其實這個方法的原理很簡單,當編輯器的命令堆棧發生改變時,修改編輯器狀態爲未保存。我們知道我們進行增加活動,刪除活動等操作時,都是執行了一個個命令,而這些命令都保存在編輯器的命令堆棧裏,所以我們每執行一個命令,編輯器的命令堆棧就發生改變。
再運行項目,我們就可以看到在編輯器處於已保存狀態時,我們再進行操作,例如增加活動,刪除活動,編輯器的狀態就變成未保存了,至於在未保存狀態,點保存按鈕,還是未保存,這在以後,我們會解決。下一節,我們將介紹如何在活動之間增加和刪除轉移,如何在轉移上增加拐點。
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章