GEF中定义的模型一般包括两个部分。
1. 业务模型。类似POJO,模型要代表一定的业务含义,需要能体现所表示业务的含义。特别是那些需要图形表示的属性,如本案例中的长宽、船名等。从实践的角度来看,通常我把从需求分析得到的领域对象作为Model一个自定义属性。
2. 保存图形的状态信息。如图形的颜色、辅助线、位置、Listerner等。
创建“org.jport.gef.berth.model”package。
可视化船舶调度系统,需要展示的数据模型就是船舶信息,如到离泊时间,装卸进度,位置等信息,从领域建模的角度来看,这就是一个领域对象。从程序角度来看,就是一个POJO。
既然是POJO,那么可以进行EJB3注解实现持久化,也可以是一个DTO,这样就为GEF的显示与数据分离奠定了基础,使GEF成为一个轻量级的客户端。
ShipInfo.java

package org.jport.gef.berth.domain;
 
import java.util.Date;
 
public class ShipInfo{
    //船名
    public String shipName;
    //船载货物的名称
    public String CargoName;
    //货物数量
    public String CargoAmount;
    //预计靠泊日期、时间
    public Date forcastDate;
    //预计停泊泊位
    public String berthNum;
    //getter and setter method
    ……
}
 
容器的创建。为了更加清晰的显示GEF的模型信息,通常创建一个父类来处理GEF通用的属性,如listener, 父子关系等。
1.       BaseModel.java

package org.jport.gef.berth.model;
 
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.draw2d.geometry.Rectangle;
public class BaseModel{
 
    private String name;
    //定义模型的位置和大小
    private Rectangle layout;
    private List<BaseModel> children;
    private BaseModel parent;
    private PropertyChangeSupport listeners;
    //定义与模型操作相关的产量
    public static final String PROPERTY_LAYOUT = "ModelLayout";
    public static final String PROPERTY_ADD = "ModelAddChild";
    public static final String PROPERTY_REMOVE = "ModelRemoveChild";
    public static final String PROPERTY_RENAME = "ModelRename";
   
    public BaseModel(){
       this.name = "modelName";
       this.layout = new Rectangle();
       this.children = new ArrayList<BaseModel>();
       this.parent = null;
       this.listeners = new PropertyChangeSupport( this);
    }
 
 
    public void setName(String name) {
       String oldName = this.name;
       this.name = name;
       getListeners().firePropertyChange( PROPERTY_RENAME, oldName, this.name);
    }
   
    public String getName() {
       return this.name;
    }
   
    public void setLayout(Rectangle newLayout) {
       Rectangle oldLayout = this.layout;
       this.layout = newLayout;
       getListeners().firePropertyChange( PROPERTY_LAYOUT, oldLayout, newLayout);
    }
   
    public Rectangle getLayout() {
       return this.layout;
    }
   
    public boolean addChild(BaseModel child) {
       boolean b = this.children.add(child);
       if (b) {
           child.setParent( this);
           getListeners().firePropertyChange( PROPERTY_ADD, null, child);
       }
       return b;
    }
   
    public boolean removeChild(BaseModel child) {
       boolean b = this.children.remove(child);
       if (b)
           getListeners().firePropertyChange( PROPERTY_REMOVE, child, null);
       return b;
    }
   
    public List<BaseModel> getChildrenArray() {
       return this.children;
    }
   
    public void setParent(BaseModel parent) {
       this.parent = parent;
    }
   
    public BaseModel getParent() {
       return this.parent;
    }
   
    public void addPropertyChangeListener(PropertyChangeListener listener) {
       listeners.addPropertyChangeListener(listener);
    }
     
    public PropertyChangeSupport getListeners() {
        return listeners;
    }
     
    public void removePropertyChangeListener(PropertyChangeListener listener) {
       listeners.removePropertyChangeListener(listener);
    }
    public boolean contains(BaseModel child) {
       return children.contains(child);
    }
}
 
BaseModel具有GEF模型的基本属性,是通用的。可以通过扩展这个类,以减少模型复杂性。
抽出父类后,ShipModel就简洁多了。

package org.jport.gef.berth.model;
 
import org.jport.gef.berth.domain.ShipInfo;
 
public class ShipModel extends BaseModel{
   
    public ShipInfo shipInfo;
 
    public ShipInfo getShipInfo() {
       return shipInfo;
    }
 
    public void setShipInfo(ShipInfo shipInfo) {
       this.shipInfo = shipInfo;
    }
}
在GEF框架下,每个模型都对应一个figure,在下一节中创建ShipModel对应的Firgure。创建:DiagramModel.java。DiagramModel将作为所有ShipModel的父类容器。创建父类并不是GEF必须的,但这样做会带来方便。

package org.jport.gef.berth.model;
 
import org.jport.gef.berth.domain.ShipInfo;
 
public class DiagramModel extends BaseModel{
   
}
 
 
Figure是视图部分,即MVC的View部分。Gef的视图部分采用的是Draw2d,本文在前面简单的介绍过Draw2d。
创建ShipFigure.java,

package org.jport.gef.berth.figure;
 
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.RectangleFigure;
import org.eclipse.draw2d.XYLayout;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.swt.graphics.Color;
import org.jport.gef.berth.util.ColorCache;
 
public class ShipFigure extends RectangleFigure {
    // 位置第一列 标签
    private Label shipNameCH = new Label("船名:");
    private Label shipNameCHValue = new Label();
 
    @Override
    protected void fillShape(Graphics graphics) {
       super.fillShape(graphics);
       drawBorders(graphics, bounds);
    }
 
    public ShipFigure() {
       setLayoutManager( new XYLayout());
 
       setLocation(shipNameCH);
       getLayoutManager().setConstraint(shipNameCH,
              new Rectangle(5, 5, 80, 15));
       add(shipNameCH);
       setLocation(shipNameCHValue);
       getLayoutManager().setConstraint(shipNameCHValue,
              new Rectangle(85, 5, 100, 15));
       add(shipNameCHValue);
    }
 
    private void setLocation(Label label) {
       // TODO Auto-generated method stub
       label.setTextAlignment(PositionConstants. LEFT);
       label.setLabelAlignment(PositionConstants. LEFT);
       label.setIconAlignment(PositionConstants. LEFT);
       label.setForegroundColor(ColorConstants. blue);
    }
 
    private static void drawBorders(Graphics gc, Rectangle bounds) {
       // 这里控制背景颜色
       Color foreColor, backColor, borderColor;
 
       foreColor = ColorCache. tooltipBg[0];
       backColor = ColorCache. tooltipBg[1];
       borderColor = ColorCache. tooltipBg[2];
 
       gc.setForegroundColor(foreColor);
       gc.setBackgroundColor(backColor);
       gc.fillGradient(bounds.x, bounds.y, bounds.width, bounds.height, true);
       // draw border
       gc.setForegroundColor(borderColor);
       gc.drawRectangle(bounds.x, bounds.y, bounds.width - 1,
              bounds.height - 1);
    }
}
 
这个类实现的图形是这样:
 

创建:DiagramFigure.java,这个类只是作为将来模型的背景。

package org.jport.gef.berth.figure;
 
import org.eclipse.draw2d.ColorConstants;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.RectangleFigure;
import org.eclipse.draw2d.XYLayout;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.swt.graphics.Color;
import org.jport.gef.berth.util.ColorCache;
 
public class DiagramFigure extends RectangleFigure {
 
    @Override
    protected void fillShape(Graphics graphics) {
       super.fillShape(graphics);
       drawBorders(graphics, bounds);
    }
 
    public DiagramFigure() {
       setLayoutManager( new XYLayout());
    }
    private static void drawBorders(Graphics gc, Rectangle bounds) {
       // 这里控制背景颜色
       Color foreColor, backColor, borderColor;
 
       foreColor = ColorCache. tooltipBg[0];
       backColor = ColorCache. tooltipBg[1];
       borderColor = ColorCache. tooltipBg[2];
 
       gc.setForegroundColor(foreColor);
       gc.setBackgroundColor(backColor);
       gc.fillGradient(bounds.x, bounds.y, bounds.width, bounds.height, true);
       // draw border
       gc.setForegroundColor(borderColor);
       gc.drawRectangle(bounds.x, bounds.y, bounds.width - 1,
              bounds.height - 1);
    }
}
 
 
在前面我们创建了Model和Figure,但Model与Figure相互间还不认识对方。在MVC框架下,控制器是模型和视图之间的桥梁。
控制器的实现较为复杂,下图是控制器的结构图:
 

    从图中可以看出Controller主要包括EidtorPart、Eidtting Policy、Commad三部分。GEF的处理流程大致如下:
Model在创建时,GEF通过BerthEditorPartFactory创建与Model对应的EditorPart。

    public EditPart createEditPart(EditPart context, Object model) {
       AbstractGraphicalEditPart part = null;
       if (model instanceof DiagramModel) {
           part = new DiagramPart();
       } else if (model instanceof ShipModel) {
           part = new ShipPart();
       }
       part.setModel(model);
       return part;
}
当模型发生改变时,GEF把模型改变装换为request并发送给EditorPart,EditorPart根据不同的Role类型,分发给安装的Editting Policy处理:

public class ShipPart extends AbstractEditPart{
    @Override
    protected void createEditPolicies() {
       installEditPolicy(EditPolicy. LAYOUT_ROLE, new BerthEditLayoutPolicy());
    }
……
}
EditPolicy获得处理请求的事件后,就会根据request的类型,把请求分发给Command处理。

@Override
    protected Command createChangeConstraintCommand(EditPart child, Object constraint) {
       AbstractLayoutCommand command = null;
       if (child instanceof ShipPart) {
           command = new ShipChangeLayoutCommand();
       }
       else if(child instanceof DiagramPart)
       {
           command = new DiagramChangeLayoutCommand();
       }
        command.setModel(child.getModel());
        command.setConstraint((Rectangle)constraint);
        return command;
 }
在EditPolicy中对Command进行赋值后,Command就会执行:

public class ShipChangeLayoutCommand extends AbstractLayoutCommand {
……
      public void execute() {
         this.model.setLayout(layout);
     }
……
}
那么当Command执行后,怎么影响视图的改变呢?
Model发生改变后,就会发出FirePropertyChange事件,

 public void setLayout(Rectangle newLayout) {
       Rectangle oldLayout = this.layout;
       this.layout = newLayout;
       getListeners().firePropertyChange( PROPERTY_LAYOUT, oldLayout, newLayout);
    }
 
因为model对应的EditPart会在被激活时向model注册监听,

 
public abstract class AbstractEditPart extends AbstractGraphicalEditPart implements PropertyChangeListener {
    public void activate() {
       super.activate();
       ((BaseModel) getModel()).addPropertyChangeListener( this);
    }
……
}
editorPart听到改变事件后,即可处理视图的改变:

public class ShipPart extends AbstractEditPart{
 
    protected void refreshVisuals(){
       ShipFigure figure = (ShipFigure)getFigure();
       ShipModel model = (ShipModel)getModel();
       figure.setShipName(model.getShipInfo().getShipName());
       figure.setLayout(model.getLayout());
    }
    public void propertyChange(PropertyChangeEvent evt) {
        if (evt.getPropertyName().equals(BaseModel. PROPERTY_LAYOUT)) refreshVisuals();
……
    }
在前面的几节中,创建了GEF应用所需的模型、视图、控制器,下一个问题:如何显示?创建“BerthGraphicalEditor.java”

package org.jport.gef.berth.ui;
 
……
 
public class BerthGraphicalEditor extends GraphicalEditor {
 
    public static final String ID = "org.jport.gef.berth.ui.BerthGraphicalEditor";
   
    private DiagramModel model;
 
    public BerthGraphicalEditor() {
       setEditDomain( new DefaultEditDomain( this));
    }
 
    public void doSave(IProgressMonitor monitor) {
    }
 
    public void doSaveAs() {
    }
 
    public boolean isDirty() {
        return false;
    }
 
    public boolean isSaveAsAllowed() {
        return false;
    }
 
    public DiagramModel createBusiness(){
       DiagramModel diagramModel = new DiagramModel();
       ShipModel shipModel= new ShipModel();
       shipModel.setName("TestShipName");
       shipModel.setLayout( new Rectangle(30, 50, BerthConstants. SHIP_LENGTH, BerthConstants. SHIP_WIDTH));
       diagramModel.addChild(shipModel);
       return diagramModel;
    }
    protected void initializeGraphicalViewer() {
       GraphicalViewer viewer = getGraphicalViewer();
       model = createBusiness();
       viewer.setContents(model);
    }
    protected void configureGraphicalViewer() {
       double[] zoomLevels;
      
         super.configureGraphicalViewer();    
        GraphicalViewer viewer = getGraphicalViewer();
        viewer.setEditPartFactory( new AppEditPartFactory());
    }
}
 
每个Editor都会对应一个EditorInput,用来初始化Editor。
在plugin.xml中,配置editor。

<extension
         point="org.eclipse.ui.editors">
      <editor
            class="org.jport.gef.berth.ui.BerthGraphicalEditor"
   contributorClass="org.jport.gef.warehouse.ui.
OverviewGraphicalEditorActionBarContributor"
            default="true"
            icon=" icons/sample.gif"
            id="org.jport.gef.berth.ui.BerthGraphicalEditor"
            name="泊位调度">
      </editor>
</ extension >
此时,在运行项目,可以得到下面的界面: