1   /****************************************************************************
2    **
3    ** This file is part of yFiles-2.9. 
4    ** 
5    ** yWorks proprietary/confidential. Use is subject to license terms.
6    **
7    ** Redistribution of this file or of an unauthorized byte-code version
8    ** of this file is strictly forbidden.
9    **
10   ** Copyright (c) 2000-2011 by yWorks GmbH, Vor dem Kreuzberg 28, 
11   ** 72070 Tuebingen, Germany. All rights reserved.
12   **
13   ***************************************************************************/
14  package demo.io.graphml;
15  
16  import y.util.AbstractStringConverter;
17  import y.util.ObjectStringConversion;
18  import y.view.AbstractCustomHotSpotPainter;
19  import y.view.AbstractCustomNodePainter;
20  import y.view.GenericNodeRealizer;
21  import y.view.NodeLabel;
22  import y.view.NodeRealizer;
23  import y.view.SimpleUserDataHandler;
24  import y.view.ShinyPlateNodePainter;
25  import y.view.Graph2D;
26  import y.view.PolyLineEdgeRealizer;
27  import y.view.Arrow;
28  import y.base.Node;
29  
30  import javax.swing.JComboBox;
31  import javax.swing.JLabel;
32  import javax.swing.JToolBar;
33  import java.awt.Color;
34  import java.awt.Graphics2D;
35  import java.awt.Shape;
36  import java.awt.EventQueue;
37  import java.awt.event.ActionEvent;
38  import java.awt.event.ActionListener;
39  import java.awt.geom.AffineTransform;
40  import java.awt.geom.Ellipse2D;
41  import java.awt.geom.GeneralPath;
42  import java.awt.geom.RectangularShape;
43  import java.util.HashMap;
44  import java.util.Locale;
45  import java.util.Map;
46  
47  import demo.view.DemoDefaults;
48  import y.view.SmartNodeLabelModel;
49  
50  /**
51   * This class demonstrates how to create customized node realizers 
52   * of type {@link GenericNodeRealizer}, how to add user data and how
53   * to read and write these customized types in GraphML format.
54   */
55  public class GenericNodeRealizerDemo extends GraphMLDemo {
56  
57    /** Create the demo */
58    public GenericNodeRealizerDemo() {
59      loadInitialGraph();
60    }
61  
62    /** Create a toolbar to switch the default node realizer type */
63    protected JToolBar createToolBar() {
64      final JComboBox cb = new JComboBox(new Object[]{"Circle", "Round Rectangle", "Butterfly"});
65      cb.setMaximumSize(cb.getPreferredSize());
66      cb.addActionListener(new ActionListener() {
67        public void actionPerformed(ActionEvent e) {
68          configureDefaultNodeRealizer(cb.getSelectedItem().toString(), cb.getSelectedIndex() + 1);
69        }
70      });
71  
72      final JToolBar toolBar = super.createToolBar();
73      toolBar.addSeparator();
74      toolBar.add(new JLabel("Default Style: "));
75      toolBar.add(cb);
76      return toolBar;
77    }
78  
79  
80    protected String[] getExampleResources() {
81      return null;
82    }
83  
84    protected void loadInitialGraph() {
85      //Since we use some custom data type for our UserData, make sure there is some conversion to/from strings available.
86      ObjectStringConversion.getInstance().registerObjectStringConverter(UserData.class,
87          new AbstractStringConverter(UserData.class) {
88            protected Object convertToObject(String o) throws IllegalArgumentException {
89              return new UserData(Integer.parseInt(o));
90            }
91  
92            protected String convertToString(Object o) throws IllegalArgumentException {
93              return String.valueOf(((UserData) o).value);
94            }
95          });
96  
97      // get the factory to register our own styles
98      GenericNodeRealizer.Factory factory = GenericNodeRealizer.getFactory();
99  
100     // use a map to pass in our implementations
101     Map implementationsMap = new HashMap();
102     // create custom implementations for ...
103 
104     // the painter and contains test
105     CustomPainter painter = new CustomPainter(new Ellipse2D.Double());
106     // register the painter
107     implementationsMap.put(GenericNodeRealizer.Painter.class, painter);
108     // and the contains test
109     implementationsMap.put(GenericNodeRealizer.ContainsTest.class, painter);
110 
111     // create a custom hotspot painter and hot spot hit test
112     CustomHotSpotPainter chsp = new CustomHotSpotPainter(165, new Ellipse2D.Double(), null);
113     // register the painter
114     implementationsMap.put(GenericNodeRealizer.HotSpotPainter.class, chsp);
115     // and the hit test
116     implementationsMap.put(GenericNodeRealizer.HotSpotHitTest.class, chsp);
117 
118     // a simple default implementation that can deal with cloneable and serializable userdata....
119     implementationsMap.put(GenericNodeRealizer.UserDataHandler.class,
120         new SimpleUserDataHandler(SimpleUserDataHandler.REFERENCE_ON_FAILURE));
121 
122     // finally add the configuration to the factory
123     factory.addConfiguration("Circle", implementationsMap);
124 
125     // do the same with two different styles...
126     ShinyPlateNodePainter shinyPainter = new ShinyPlateNodePainter() {
127       protected void paintNode(NodeRealizer context, Graphics2D graphics, boolean sloppy) {
128         super.paintNode(context, graphics, sloppy);
129         paintUserData(context, graphics);
130       }
131     };
132     shinyPainter.setDrawShadow(true);
133     implementationsMap.put(GenericNodeRealizer.Painter.class, shinyPainter);
134     implementationsMap.put(GenericNodeRealizer.ContainsTest.class, shinyPainter);
135     factory.addConfiguration("Round Rectangle", implementationsMap);
136 
137     GeneralPath gp = new GeneralPath();
138     gp.moveTo(1.0f, 0.5f);
139     gp.lineTo(0.0f, 1.0f);
140     gp.quadTo(0.0f, 0.5f, 0.3f, 0.5f);
141     gp.quadTo(0.0f, 0.5f, 0.0f, 0.0f);
142     gp.closePath();
143 
144     PolygonPainter pp = new PolygonPainter(gp);
145     implementationsMap.put(GenericNodeRealizer.Painter.class, pp);
146     implementationsMap.put(GenericNodeRealizer.ContainsTest.class, pp);
147     factory.addConfiguration("Butterfly", implementationsMap);
148 
149     // Create the default node realizer.
150     Graph2D graph = view.getGraph2D();
151     graph.setDefaultNodeRealizer(new GenericNodeRealizer());
152 
153     // Create an initial graph.
154     configureDefaultNodeRealizer("Circle", 1);
155     Node circle = graph.createNode(60, 60, "3");
156     configureDefaultNodeRealizer("Round Rectangle", 2);
157     Node rectangle = graph.createNode(300, 60, "2");
158     configureDefaultNodeRealizer("Butterfly", 3);
159     Node butterfly = graph.createNode(180, 220, "1");
160 
161     graph.createEdge(circle, rectangle);
162     PolyLineEdgeRealizer edgeRealizer1 = new PolyLineEdgeRealizer();
163     edgeRealizer1.setTargetArrow(Arrow.STANDARD);
164     graph.createEdge(rectangle, butterfly, edgeRealizer1);
165     edgeRealizer1.insertBend(300, 220);
166     PolyLineEdgeRealizer edgeRealizer2 = new PolyLineEdgeRealizer();
167     edgeRealizer2.setTargetArrow(Arrow.STANDARD);
168     graph.createEdge(butterfly, circle, edgeRealizer2);
169     edgeRealizer2.insertBend(60, 220);
170 
171     graphMLPane.updateGraphMLText(view.getGraph2D());
172 
173     // Set the default for new nodes to the initial value of the combo box for choosing
174     // the default node realizer (see below).
175     configureDefaultNodeRealizer("Circle", 1);
176   }
177 
178   protected static void paintUserData(NodeRealizer context, Graphics2D graphics) {
179     UserData data = (UserData) ((GenericNodeRealizer) context).getUserData();
180     graphics.setColor(Color.black);
181     graphics.drawString("data=" + data.value,
182         (float) context.getX(),
183         (float) (context.getY() - 1));
184   }
185 
186   private void configureDefaultNodeRealizer(String configuration, int userInt) {
187     GenericNodeRealizer gnr = (GenericNodeRealizer) view.getGraph2D().getDefaultNodeRealizer();
188     gnr.setConfiguration(configuration);
189     gnr.setUserData(new UserData(userInt));
190     gnr.setFillColor(DemoDefaults.DEFAULT_NODE_COLOR);
191     NodeLabel label = gnr.getLabel();
192     SmartNodeLabelModel model = new SmartNodeLabelModel();
193     label.setLabelModel(model);
194     label.setModelParameter(model.getDefaultParameter());
195   }
196 
197   /**
198    * A custom HotSpotPainter implementation
199    */
200   static final class CustomHotSpotPainter extends AbstractCustomHotSpotPainter {
201 
202     private RectangularShape shape;
203     private Color color;
204 
205     CustomHotSpotPainter(int mask, RectangularShape shape, Color color) {
206       super(mask);
207       this.shape = shape;
208       this.color = color;
209     }
210 
211     protected void initGraphics(NodeRealizer context, Graphics2D g) {
212       super.initGraphics(context, g);
213       if (color == null) {
214         Color fc = context.getFillColor();
215         if (fc != null) {
216           g.setColor(fc);
217         }
218       } else {
219         g.setColor(color);
220       }
221     }
222 
223 
224     protected void paint(byte hotSpot, double centerX, double centerY, Graphics2D graphics) {
225       shape.setFrame(centerX - 2, centerY - 2, 5, 5);
226       graphics.fill(shape);
227     }
228 
229     protected boolean isHit(byte hotSpot, double centerX, double centerY, double testX, double testY) {
230       return Math.abs(testX - centerX) < 3 && Math.abs(testY - centerY) < 3;
231     }
232 
233   }
234 
235   /**
236    * A custom Painter and ContainsTest implementation.
237    * This one works with any kind of RectangularShape
238    */
239   static final class CustomPainter extends AbstractCustomNodePainter implements GenericNodeRealizer.ContainsTest {
240     RectangularShape shape;
241 
242     CustomPainter(RectangularShape shape) {
243       this.shape = shape;
244     }
245 
246     /** Override default fill color */
247     protected Color getFillColor(NodeRealizer context, boolean selected) {
248       if (selected) {
249         return Color.red;
250       } else {
251         return super.getFillColor(context, selected);
252       }
253     }
254 
255     protected void paintNode(NodeRealizer context, Graphics2D graphics, boolean sloppy) {
256       shape.setFrame(context.getX(), context.getY(), context.getWidth(), context.getHeight());
257       if (initializeFill(context, graphics)) {
258         graphics.fill(shape);
259       }
260       if (initializeLine(context, graphics)) {
261         graphics.draw(shape);
262       }
263       paintUserData(context, graphics);
264     }
265 
266     public boolean contains(NodeRealizer context, double x, double y) {
267       shape.setFrame(context.getX(), context.getY(), context.getWidth(), context.getHeight());
268       return shape.contains(x, y);
269     }
270   }
271 
272   /**
273    * Another custom Painter and ContainsTest implementation.
274    * This one works with any kind of GeneralPath
275    */
276   static final class PolygonPainter extends AbstractCustomNodePainter implements GenericNodeRealizer.ContainsTest {
277     GeneralPath path;
278     AffineTransform aft;
279 
280     PolygonPainter(GeneralPath path) {
281       this.path = path;
282       this.aft = AffineTransform.getScaleInstance(1.0d, 1.0d);
283     }
284 
285     protected void paintNode(NodeRealizer context, Graphics2D graphics, boolean sloppy) {
286       aft.setToIdentity();
287       aft.translate(context.getX(), context.getY());
288       aft.scale(context.getWidth(), context.getHeight());
289       Shape shape = path.createTransformedShape(aft);
290       if (initializeFill(context, graphics)) {
291         graphics.fill(shape);
292       }
293       if (initializeLine(context, graphics)) {
294         graphics.draw(shape);
295       }
296       paintUserData(context, graphics);
297     }
298 
299     /** Override default fill color to be the same as the unselected fill color */
300     protected Color getFillColor(NodeRealizer context, boolean selected) {
301       return super.getFillColor(context, false);
302     }
303 
304     public boolean contains(NodeRealizer context, double x, double y) {
305       return path.contains((x - context.getX()) / context.getWidth(), (y - context.getY()) / context.getHeight());
306     }
307   }
308 
309   /**
310    * The type for the user data that is associated with GenericNodeRealizer.
311    */
312   static class UserData {
313     int value;
314 
315     UserData(int value) {
316       this.value = value;
317     }
318   }
319 
320   /**
321    * Launcher method. Execute this class to see sample instantiations of
322    * the CustomNodeRealizer in action.
323    */
324   public static void main(String[] args) {
325     EventQueue.invokeLater(new Runnable() {
326       public void run() {
327         Locale.setDefault(Locale.ENGLISH);
328         initLnF();
329         (new GenericNodeRealizerDemo()).start();
330       }
331     });
332   }
333 }
334