You are on page 1of 36

A Gentle Introduction to GEF

Building Rich Graphical Editors Using GEF


and Draw2D

© 2006 by Koen Aers; made available under the EPL v1.0 |


Speaker

 Koen Aers
 JBoss jBPM (http://www.jboss.com/products/jbpm)
 JBoss IDE (http://www.jboss.com/products/jbosside)
 => Graphical Process Designer

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
Agenda

 GEF in 3 Slides
 An Empty Editor for Directed Graphs
 Adding Nodes to the Editor
 Moving and Resizing Nodes
 Creating Nodes with the Palette
 Next Steps and Directions
 Conclusion

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
The Graphical Editing Framework

 Consists of 2 Eclipse Plug-ins


 Draw2D: Layout and rendering toolkit for displaying graphics
 GEF: Framework using the old Smalltalk MVC Principles
(Model, Figure and EditPart)
 General Working Mechanism:
 Input events are translated into Requests
 EditParts hand over Requests to EditPolicies
 EditPolicies translate Requests into GEF Commands (when they
know of the particular Request)
 Commands get executed and change the model
 Model is observed by EditPart: upon a change the EditPart
refreshes the Figure

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
Structure of a Typical GEF Editor
GraphEditor
1 PaletteViewer
1
EditDomain PaletteRoot
1
observes
1..n
Graph
GraphicalViewer RootEditPart
1 1..n 1

1..n
1 GraphEditPart
EditPartFactory 1..n Node
1
NodeEditPart
creates observes
1..n 1..n
1
ConnectionEditPart Connection

observes

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
Typical GEF MVC Interactions

NodeDeleteCommand modifies Graph


creates

NodeComponentEditPolicy
1

observes
1..n
1
NodeFigure NodeEditPart Node
1
refreshes
1
modifies
NodeGraphicalNodeEditPolicy

1..n
creates
ConnectionCreateCommand creates Connection

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
An Empty Editor for Directed Graphs
GraphEditor

Graph
GraphicalViewer RootEditPart
1 1..n 1

1 GraphEditPart
EditPartFactory

creates

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
A Minimal GEF Editor in 6 Steps

 Creation of Eclipse plug-in with Editor


 Add the GraphicalViewer: ScrollingGraphicalViewer
 Special kind of EditPartViewer
 EditPartViewers are adapters for SWT controls that manage the EditParts
 They are populated by setting their contents
 Add the RootEditPart: ScalableFreeFormRootEditPart
 Brigdes gap between EditPartViewer and its contents
 Can provide servies such as zooming and freeform figures
 Define the model to be used: Graph
 Initially a simplistic subclass of Object
 Will be a container for Nodes later
 Create the view and the controller: GraphEditPart
 Define the GraphEditPartFactory
 Only Graph objects will be considered initially

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
GraphEditPart and GraphEditPartFactory
public class GraphEditPart extends AbstractGraphicalEditPart {
public GraphEditPart(Graph graph) {
setModel(graph);
}
protected IFigure createFigure() {
FreeformLayer layer = new FreeformLayer();
layer.setLayoutManager(new FreeformLayout());
layer.setBorder(new LineBorder(1));
return layer;
}
protected void createEditPolicies() {}
}
public class GraphicalEditPartFactory implements EditPartFactory {
public EditPart createEditPart(EditPart context, Object model) {
if (model instanceof Graph)
return new GraphEditPart((Graph)model);
else
return null;
}
}
A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
The Initial GraphEditor in Detail
public class GraphEditor extends EditorPart {

public void init(IEditorSite site, IEditorInput input)
throws PartInitException {
setSite(site);
setInput(input);
}

public void createPartControl(Composite parent) {
ScrollingGraphicalViewer viewer =
new ScrollingGraphicalViewer();
viewer.createControl(parent);
viewer.setRootEditPart(new ScalableFreeformRootEditPart());
viewer.getControl().setBackground(ColorConstants.white);
viewer.setEditPartFactory(new GraphEditPartFactory());
viewer.setContents(new Graph());
}

}

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
The Empty Editor in Action

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
Adding Nodes to the Editor
GraphEditor

observes
Graph
GraphicalViewer RootEditPart
1 1..n 1

1..n
1 GraphEditPart
EditPartFactory 1..n Node
1
NodeEditPart
creates observes

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
Showing the Nodes

 Update the model


 Add the Node class: constraint and name attributes
 Make the Graph class manage a list of nodes
 Create an example model: ContentProvider
 Define the Node view: NodeFigure
 Define the Controller : NodeEditPart
 Update the GraphEditPartFactory
 Now we have Graph objects as well as Node objects to consider
 Update the GraphEditPart
 GraphEditPart is responsible for returning a list of child EditParts

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
The Node and Graph Model Implementation
public class Node {
Rectangle constraint;
String name;
public Rectangle getConstraint() { return constraint;}
public void setConstraint(Rectangle constraint) {
this.constraint = constraint;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}

public class Graph {


List nodes;
public List getNodes() {
if (nodes == null) nodes = new ArrayList();
return nodes;
}
public void addNode(Node node) { getNodes().add(node); }
}
A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
The Node View Implementation : NodeFigure
public class NodeFigure extends Figure {
private Label label;
private RectangleFigure rectangle;
public NodeFigure() {
setLayoutManager(new XYLayout());
rectangle = new RectangleFigure();
add(rectangle);
label = new Label();
add(label);
}
public void paintFigure(Graphics g) {
Rectangle r = getBounds().getCopy();
setConstraint(rectangle, new Rectangle(0, 0, r.width, r.height));
setConstraint(label, new Rectangle(0, 0, r.width, r.height));
rectangle.invalidate();
label.invalidate();
}
public Label getLabel() {
return label;
}
A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
The Node Controller Implementation :
NodeEditPart
public class NodeEditPart extends AbstractGraphicalEditPart {
public NodeEditPart(Node node) {
setModel(node);
}
protected IFigure createFigure() {
return new NodeFigure();
}
protected void createEditPolicies() { }
public void refreshVisuals() {
NodeFigure figure = (NodeFigure)getFigure();
Node node = (Node)getModel();
GraphEditPart parent = (GraphEditPart)getParent();
figure.getLabel().setText(node.getName());
Rectangle r = new Rectangle(node.getConstraint());
parent.setLayoutConstraint(this, figure, r);
}
}

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
GraphEditPart and GraphEditPartFactory Revisited
public class GraphEditPart extends AbstractGraphicalEditPart {

protected List getModelChildren() {
return ((Graph)getModel()).getNodes();
}
}

public class GraphicalEditPartFactory implements EditPartFactory {


public EditPart createEditPart(EditPart context, Object model) {
if (model instanceof Graph)
return new GraphEditPart((Graph)model);
else if (model instanceof Node)
return new NodeEditPart((Node)model);
else
return null;
}
}

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
The Node-enabled Graph Editor

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
Moving and Resizing Nodes

 Add the EditDomain To the Editor


 EditDomain == GEF’s Spine
 Manages the state of a GEF application (CommandStack,
EditPartViewer(s), active Tool)
 Tied with an Eclipse EditorPart
 Revisit GraphEditPart: Maintain a list of EditPolicies
 Transform requests into commands: GraphXYLayoutEditPolicy
 Commands modify the model: NodeChangeConstraintCommand
 Change NodeEditPart/Node Pair: Observer/Observable

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
Adding the EditDomain to the Editor
public class GraphEditor extends EditorPart {
private EditDomain editDomain;

public void init(IEditorSite site, IEditorInput input)
throws PartInitException {

editDomain = new DefaultEditDomain(this);
}

public void createPartControl(Composite parent) {

editDomain.addViewer(viewer);
}

}

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
The Node Move/Resize Scenario
GraphEditPart

GraphXYLayoutEditPolicy NodeChangeConstraintCommand modifies

creates

observes
1
NodeFigure NodeEditPart Node
1
refreshes

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
Revisit GraphEditPart

public class GraphEditPart extends AbstractGraphicalEditPart


{

protected void createEditPolicies() {
installEditPolicy(
EditPolicy.LAYOUT_ROLE,
new GraphXYLayoutEditPolicy());
}
}

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
The GraphXYLayoutEditPolicy Implementation

public class GraphXYLayoutEditPolicy extends XYLayoutEditPolicy {


protected Command createChangeConstraintCommand(
EditPart child, Object constraint) {
NodeChangeConstraintCommand changeConstraintCommand =
new NodeChangeConstraintCommand();
changeConstraintCommand.setNode((Node)child.getModel());
changeConstraintCommand.setNewConstraint(
(Rectangle)constraint);
return changeConstraintCommand;
}
// We use a stub implementation for the other methods

}

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
The NodeChangeConstraintCommand
Implementation
public class NodeChangeConstraintCommand extends Command {
private Rectangle newConstraint;
private Rectangle oldConstraint;
private Node node;
public void execute() {
if (oldConstraint == null) {
oldConstraint = new Rectangle(node.getConstraint());
}
node.setConstraint(newConstraint);
}
public void undo() {
node.setConstraint(oldConstraint);
}
public void setNewConstraint(Rectangle newConstraint) {
this.newConstraint = newConstraint;
}
public void setNode(Node node) { this.node = node; }
}

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
The NodeEditPart/Node Combination
public class NodeEditPart extends AbstractGraphicalEditPart implements Observer
{

public void activate() {
if (!isActive()) ((Node)getModel()).addObserver(this);
super.activate();
}
public void deactivate() {
if (isActive()) ((Node)getModel()).deleteObserver(this);
super.deactivate();
}
public void update(Observable arg0, Object arg1) { refreshVisuals(); }
}

public class Node extends Observable {



public void setConstraint(Rectangle constraint) {
this.constraint = constraint;
setChanged();
notifyObservers();
}

}
A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
Editor with Moved and Resized Nodes

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
Creating Nodes with the Palette
 Creating the Palette
 PaletteRoot and its Tools
 Splitting the Canvas: SashForm and PaletteViewer
 Creating Nodes
 NodeCreateCommand
 Make the GraphEditPart/Graph combo Observer/Observable

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
The PaletteRoot Implementation
public class GraphPalette extends PaletteRoot {
public GraphPalette() {
PaletteGroup group = new PaletteGroup("Graph Controls");
SelectionToolEntry entry = new SelectionToolEntry();
group.add(entry);
setDefaultEntry(entry);
group.add(new PaletteSeparator());
group.add(new CreationToolEntry(
“Node”,
“Creates a new node.”,
new NodeFactory(), null, null));
}
}

public class NodeFactory implements CreationFactory {


public Object getNewObject() { return new Node(); }
public Object getObjectType() { return Node.class; }
}

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
Adding the Palette to the Editor
public class GraphEditor extends EditorPart {

public void createPartControl(Composite parent) {
SashForm form = new SashForm(parent, SWT.HORIZONTAL);
createPaletteViewer(form);
createGraphViewer(form);
form.setWeights(new int[] { 15, 85 });
}
private void createPaletteViewer(Composite parent) {
PaletteViewer viewer = new PaletteViewer();
viewer.createControl(parent);
editDomain.setPaletteViewer(viewer);
editDomain.setPaletteRoot(new GraphPalette());
}
private void createGraphViewer(Composite parent) {
ScrollingGraphicalViewer viewer = new ScrollingGraphicalViewer();
viewer.setRootEditPart(new ScalableFreeformRootEditPart());
viewer.createControl(parent);
viewer.getControl().setBackground(ColorConstants.white);
viewer.setEditPartFactory(new GraphicalEditPartFactory());
viewer.setContents(ContentProvider.INSTANCE.newSampleGraph());
editDomain.addViewer(viewer);
}
}
A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
Graph Editor with Palette

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
The NodeCreateCommand Implementation

public class NodeCreateCommand extends Command {

public Node node;


public Rectangle constraint;
public Graph parent;

public void execute() {


setNodeConstraint();
parent.addNode(node);
}

private void setNodeConstraint() {


if (constraint != null) node.setConstraint(constraint);
}
}

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
Revisit GraphXYLayoutEditPolicy
public class GraphXYLayoutEditPolicy
extends XYLayoutEditPolicy {

protected Command getCreateCommand(CreateRequest request) {
if (request.getNewObjectType().equals(Node.class)) {
NodeCreateCommand result = new NodeCreateCommand();
result.constraint = new Rectangle(
request.getLocation(),
new Dimension(65, 35));
result.node = (Node)request.getNewObject();
result.parent = (Graph)getHost().getModel();
result.node.setName(result.graph.getNextNodeName());
return result;
}
return null;
}

}

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
The GraphEditPart/Graph Combination
public class Graph extends Observable {

public void addNode(Node node) {
getNodes().add(node);
setChanged();
notifyObservers();
}
public void removeNode(Node node) {
getNodes().remove(node);
setChanged();
notifyObservers();
}

}

public class GraphEditPart


extends AbstractGraphicalEditPart implements Observer {
… // as before
}

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
Editor with Some Created Nodes

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
Next Steps and Directions

 Undo/Redo support : ActionRegistry, CommandStack and


CommandStackListener
 Add support for Connections
 Connection/ConnectionEditPart
 GraphicalNodeEditPolicy and ConnectionCreateCommand
 Delete support for Nodes and Connections
 ComponentEditPolicy/NodeDeleteCommand
 ConnectionEditPolicy/ConnectionEndpointEditPolicy
 Saving and loading the model
 Outline and thumbnail view
 Support for grid, zooming, aligning
 Make the editor extensible: node type extension point

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0
Conclusion

 Steep and long learning curve


 Starting from scratch is not easy
 No books available
 Starting small is mandatory to fully understand
 Very rich framework
 Lots of predefined functionality
 Do very complex things with almost no code
 Use code and javadocs to find details
 Overwhelming for newbie

A Gentle Introduction to GEF | © 2006 by Koen Aers; made available under the EPL v1.0

You might also like