Download:
ch08.zip
(source code)
Graph Editor Framework
Problem Domain: interactive editing of diagrams
- Graph consists of nodes and edges
- Class diagram:
nodes are rectangles
edges are arrows
- Electronic circuit diagram:
nodes are transistors, resistors
edges are wires
Framework Approach
- Traditional approach: programmer starts from scratch for every
editor type
- Framework approach: Programmer extends graph, node, edge classes
- Framework handles UI, load/save, ...
- Our framework is kept simple
- Violet uses extension of this framework
User Interface
- Toolbar on top
- Grabber button for selecting nodes/edges
- Buttons for current node/edge type
- Menu
- Drawing area
Mouse Operations
- Click on empty space: current node inserted
- Click on node or edge: select it
- Drag node when current tool an edge: connect nodes
- Drag node when current tool not an edge: move node
Division of Responsibility
- Divide code between
- Rendering is app specific (e.g. transistor)
- Hit testing is app specific (odd node shapes)
- Framework draws toolbar
- Framework does mouse listening
Adding Nodes and Edges
- Framework draws toolbar
- How does it know what nodes/edges to draw?
- App gives a list of nodes/edges to framework at startup
- How does app specify nodes/edges?
- Class names? ("Transistor")
- Class objects? (Transistor.class)
- Node, Edge objects? (new Transistor())
In our graph editor framework, a concrete graph must give the
framework prototype objects. For example, a figure might be
created by defining a node class (CircleNode), and edge class
(LineEdge), and a SimpleGraph class that defines two
node prototypes and an edge prototype. (see SimpleGraph.java)
- new CircleNode(Color.BLACK)
new CircleNode(Color.WHITE)
- When user inserts new node, the toolbar node is cloned
Node prototype = node of
currently selected toolbar button;
Node newNode = (Node) prototype.clone();
Point2D mousePoint = current mouse
position;
graph.add(newNode, mousePoint);
- Example of PROTOTYPE pattern
PROTOTYPE Pattern
Context
- A system instantiates objects of classes that are not known when
the system is built.
- You do not want to require a separate class for each kind of
object.
- You want to avoid a separate hierarchy of classes whose
responsibility it is to create the objects.
Solution
- Define a prototype interface type that is common to all created
objects.
- Supply a prototype object for each kind of object that the system
creates.
- Clone the prototype object whenever a new object of the given
kind is required.
PROTOTYPE Pattern
Name in
Design Pattern
|
Actual name
(graph editor)
|
Prototype
|
Node
|
ConcretePrototype1
|
CircleNode
|
Creator
|
The GraphPanel that handles the
mouse operation for adding new nodes
|
Framework Classes
- Framework programmer implements Node/Edge
interfaces
- draw draws node/edge
- getBounds returns
enclosing rectangle (to compute total graph size for scrolling)
- Edge.getStart, getEnd yield start/end nodes
- Node.getConnectionPoint
computes attachment point on shape boundary
- Edge.getConnectionPoints
yields start/end coordinates (for grabbers)
- clone overridden to
be public
Node Connection Points
The getConnectionPoint method in the Node
interface type computes an optimal attachment point on the boundary of
a node (see below). Since the node boundary may have an arbitrary
shape, this computation must be carried out by each concrete class.
Framework Classes: AbstractEdge
Framework Classes: Graph
- Graph collects nodes and edges
- Subclasses override methods
public abstract Node[]
getNodePrototypes()
public abstract Edge[]
getEdgePrototypes()
- Graph.java
Framework UI Classes
- GraphFrame: a frame
that manages the toolbar, the menu bar, and the graph
panel.
- ToolBar: a panel
that holds toggle buttons for the node and edge icons.
- GraphPanel: a panel
that shows the graph and handles the mouse clicks and drags for the
editing commands.
- Application programmers need not subclass these classes
A Framework Instance
- Simple application
- Draw black and white nodes
- Join nodes with straight lines
Programmer responsibilities
- For each node and edge type, define a class that implements the Node or Edge interface type
- Supply all required methods, such as drawing and containment
testing.
- Define a subclass of the Graph
class and supply getNodePrototypes, getEdgePrototypes
- Supply a class with a main
method
A Framework Instance
A Framework Instance
Generic Framework Code
- Framework frees application programmer from tedious programming
- Framework can do significant work without knowing node/edge types
- Analyze two scenarios
Add New Node
public void mousePressed(MouseEvent event)
{
Point2D mousePoint = event.getPoint();
Object tool = toolBar.getSelectedTool();
...
if (tool instanceof Node)
{
Node prototype = (Node) tool;
Node newNode = (Node)prototype.clone();
graph.add(newNode, mousePoint);
}
...
repaint();
}
Add New Node
Add New Edge
- First check if mouse was pressed inside existing node
public Node findNode(Point2D
p)
{
for (int i = 0;
i < nodes.size(); i++)
{
Node
n = (Node) nodes.get(i);
if
(n.contains(p)) return n;
}
return null;
}
Add New Edge
- mousePressed:
- Check if mouse point inside node
- Check if current tool is edge
- Mouse point is start of rubber band
- mouseDragged:
- Mouse point is end of rubber band; repaint
- mouseReleased:
Add New Edge
Maintained by John Loomis,
last updated 16 Nov 2011