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