Additional data for graph elements can be stored using data accessors. We have already seen a basic example of dealing with data accessors in the Getting Data Into the View trail where we have used a plain NodeMap to hold additional data. In the AdditionalDataEditor application, we will still be using a NodeMap, however conveniently encapsulated to ease working with the data throughout different parts of an application.
We will use a Graph2D subclass to this end. Example 1.1, “Custom Graph2D subclass CustomGraph2D” shows our customized subclass that enables storing and retrieving custom data associated with the nodes in the graph.
Example 1.1. Custom Graph2D subclass CustomGraph2D
public class CustomGraph2D extends Graph2D {
// A data accessor with read-write semantics that holds the additional data.
NodeMap customTextMap;
/**
* Instantiates a new CustomGraph2D.
* The instance conveniently handles additional data for nodes.
*/
public CustomGraph2D() {
init();
}
/** Returns a new CustomGraph2D instance. */
public Graph createGraph() {
return new CustomGraph2D();
}
/** Initializes the data store for the additional data. */
protected void init() {
customTextMap = this.createNodeMap();
}
/** Sets the given text as the additional data for the given node. */
public void setCustomText(Node node, String text) {
customTextMap.set(node, text);
}
/** Gets the text (i.e., the additional data) stored for the given node. */
public String getCustomText(Node node) {
return (String)customTextMap.get(node);
}
}
The CustomGraph2D class holds a NodeMap that is used to store the additional data for the nodes of the graph and features convenient getter and setter methods to access that storage. Additionally, it also overrides any constructors or factory methods that need to create an instance of our type. Basically, that's all there is to it.
But why do we create a subclass of Graph2D in the first place? Why don't we just directly subclass the Node class and add another field there? Simply put, because it is not possible to subclass the Node class (or the Edge class for that matter). And it is not necessary either, since we can achieve the very same effect by means of the NodeMap in our Graph2D subclass anyway. However, subclassing the graph type instead of the graph element types maintains the notion that the graph is the single authority that is in charge of all changes to the state of the graph structure. Also, it gives us the additional benefit that the graph element types remain lean and focused.
In order to use the CustomGraph2D class in our editor application, we need to replace usages of Graph2D with our type at a few places. Most importantly, we need to set our CustomGraph2D instance as the view's graph. We do so in the createGraph2DView method using the Graph2DView constructor that takes a Graph2D as parameter. Example 1.2, “Using CustomGraph2D in the view” shows the corresponding line.
Example 1.2. Using CustomGraph2D in the view
private Graph2DView createGraph2DView() {
Graph2DView view = new Graph2DView(new CustomGraph2D());
...
The other somewhat "major" change to our code is that we also replace the getGraph getter method by the getCustomGraph method which returns our graph type, since we want to have direct access to it across the application.
Now that we have our graph type in place, let's use it. We will add a text field to the toolbar where a user can enter some text. This text shall be stored for all selected nodes as additional data. Example 1.3, “Setting additional data using the CustomGraph2D convenience methods” shows the entire setup for the text field, which is mostly common Java Swing code. In the actionPerformed callback, we store the entered text using the setter method conveniently provided by CustomGraph2D.
Example 1.3. Setting additional data using the CustomGraph2D convenience methods
toolbar.addSeparator();
// Add text field.
final JTextField textField = new JTextField(20);
textField.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ae) {
CustomGraph2D graph = getCustomGraph();
NodeCursor nc = graph.selectedNodes();
for (; nc.ok(); nc.next()) {
// Set the entered text as the node's additional data.
graph.setCustomText(nc.node(), textField.getText());
}
textField.selectAll();
graph.updateViews();
}
});
toolbar.add(textField);
The tooltip that we use for displaying the additional data is a feature of the EditMode class. To retrieve the additional data we will use the getter method conveniently provided by CustomGraph2D.
Example 1.4. Retrieving additional data using the CustomGraph2D convenience methods
editMode = new EditMode() {
protected String getNodeTip(Node node) {
// Retrieve the node's additional data.
return "<html><b>Data:</b><br>" + getCustomGraph().getCustomText(node) +
"</html>";
}
};
editMode.showNodeTips(true);
Figure 1.1, “Handling additional data for the nodes in a graph” shows the AdditionalDataEditor application in action. The text that is displayed in the tooltip has been entered in the text field.
You will find related information in the yFiles for Java Tutorial:
In the yFiles for Java API:
In the yFiles for Java source code demos:
|
Copyright ©2008-2009, yWorks GmbH. All rights reserved. |