这一节我们来介绍如何在编辑器中移动活动,改变活动的大小和删除活动,在流程控制器中已经安装的策略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命令,用这个命令来修改活动的位置,大小属性,这个命令的具体代码如下:
我们知道修改了活动的大小,位置属性之后,活动模型会通知控制器它的LOCATION_PROP,SIZE_PROP发生变化,这可在模型的定义中看到:
活动控制器AbstractActivityEditPart应该根据这些属性的变化来刷新视图,代码如下:
策略AbstractActivityComponentEditPolicy的代码如下:
这个策略继承了ComponentEditPolicy类,并且覆盖了createDeleteCommand方法,在这个方法中,我们创建了AbstractActivityDeleteCommand命令,这个命令完成从流程模型中删除当前活动模型,我们知道在活动模型中,还维护着输入转移和输出转移两个列表,而转移对象中又维护着转移的起始活动和终止活动,由于删除了活动,转移就成了只有源点或者只要目标点,因此,我们还应该从还维护着这些转移的活动中删除这些转移,该命令的代码如下:
这个命令中从流程模型中删除了活动模型,流程模型通知控制器,它的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邦定,当按删除键,就执行对应的Action,sharedKeyHandler可以供编辑器和大纲视图公用,关于大纲视图在以后会讲。
接下来还需要在编辑器视图中注册这些这些公共键,代码如下:
这样运行项目,我们就可以用键盘的删除键来删除活动了,我们注意到无论我们向编辑器中增加活动,还是删除活动,编辑器的状态始终没变,不像其它编辑器修改之后,提示编辑器处于未保存状态,我们要实现这个功能其实很简单,只需在WorkflowProcessEditor类中覆盖父类的方法commandStackChanged,代码如下:
其实这个方法的原理很简单,当编辑器的命令堆栈发生改变时,修改编辑器状态为未保存。我们知道我们进行增加活动,删除活动等操作时,都是执行了一个个命令,而这些命令都保存在编辑器的命令堆栈里,所以我们每执行一个命令,编辑器的命令堆栈就发生改变。
再运行项目,我们就可以看到在编辑器处于已保存状态时,我们再进行操作,例如增加活动,删除活动,编辑器的状态就变成未保存了,至于在未保存状态,点保存按钮,还是未保存,这在以后,我们会解决。下一节,我们将介绍如何在活动之间增加和删除转移,如何在转移上增加拐点。
我们还是先来看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());
}
}
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);
}
}
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();
}
}
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());
}
//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);
}
}
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);
}
}
}
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邦定,当按删除键,就执行对应的Action,sharedKeyHandler可以供编辑器和大纲视图公用,关于大纲视图在以后会讲。
接下来还需要在编辑器视图中注册这些这些公共键,代码如下:
protectedvoid configureGraphicalViewer()
{
super.configureGraphicalViewer();
GraphicalViewer viewer = getGraphicalViewer();
viewer.setEditPartFactory(new WorkflowProcessEditPartFactory());
viewer.setRootEditPart(new ScalableFreeformRootEditPart());
getGraphicalViewer().setKeyHandler(new GraphicalViewerKeyHandler(getGraphicalViewer())
.setParent(getCommonKeyHandler()));
}
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);
}
firePropertyChange(IEditorPart.PROP_DIRTY);
super.commandStackChanged(event);
}
其实这个方法的原理很简单,当编辑器的命令堆栈发生改变时,修改编辑器状态为未保存。我们知道我们进行增加活动,删除活动等操作时,都是执行了一个个命令,而这些命令都保存在编辑器的命令堆栈里,所以我们每执行一个命令,编辑器的命令堆栈就发生改变。
再运行项目,我们就可以看到在编辑器处于已保存状态时,我们再进行操作,例如增加活动,删除活动,编辑器的状态就变成未保存了,至于在未保存状态,点保存按钮,还是未保存,这在以后,我们会解决。下一节,我们将介绍如何在活动之间增加和删除转移,如何在转移上增加拐点。