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.view.flowchart;
29  
30  import y.geom.YPoint;
31  import y.option.RealizerCellRenderer;
32  import y.view.DropSupport;
33  import y.view.EdgeRealizer;
34  import y.view.Graph2DView;
35  import y.view.NodeRealizer;
36  import y.view.Graph2D;
37  import y.view.PolyLineEdgeRealizer;
38  import y.view.GenericNodeRealizer;
39  import y.view.Arrow;
40  
41  import javax.swing.DefaultListModel;
42  import javax.swing.Icon;
43  import javax.swing.JComponent;
44  import javax.swing.JList;
45  import javax.swing.JScrollPane;
46  import javax.swing.ListSelectionModel;
47  import javax.swing.event.ListSelectionListener;
48  import javax.swing.event.ListSelectionEvent;
49  import java.awt.Dimension;
50  import java.awt.BorderLayout;
51  import java.awt.dnd.DnDConstants;
52  import java.awt.dnd.DragGestureEvent;
53  import java.awt.dnd.DragGestureListener;
54  import java.awt.dnd.DragSource;
55  import java.util.ArrayList;
56  import java.util.Collection;
57  import java.util.HashMap;
58  import java.util.Iterator;
59  import java.util.List;
60  import java.util.Map;
61  
62  import demo.view.flowchart.painters.FlowchartAnnotationPainter;
63  import demo.view.flowchart.painters.FlowchartRealizerFactory;
64  
65  /**
66   * This is a component, which represents a palette of flowchart nodes and edges and allows to drag them into a Graph2DView.
67   */
68  public class FlowchartPalette extends JComponent {
69    private DragAndDropSupport dropSupport;
70    boolean snapMode;
71  
72    /**
73     * Creates a new FlowchartPalette with a pre-configured list of node and edge realizers.
74     */
75    public FlowchartPalette(final Graph2DView view) {
76      final BorderLayout borderLayout = new BorderLayout();
77      borderLayout.setVgap(10);
78      this.setLayout(borderLayout);
79      this.add(createDefaultPalette(view),BorderLayout.CENTER);
80      initializeDefaultRealizers(view);
81    }
82  
83    /**
84     * Returns whether or not snapping is enabled.
85     * @return true if snap mode enabled.
86     * @see #setSnapMode(boolean)
87     */
88    public boolean isSnapMode() {
89      return snapMode;
90    }
91  
92    /**
93     * Activates/deactivates snapping between graph elements, while dragging of a Flowchart-Node into the view.
94     * @param snapMode Whether to enable snapping.
95     */
96    public void setSnapMode(boolean snapMode) {
97      this.snapMode = snapMode;
98      dropSupport.configureSnapping(snapMode, 30, 15, true);
99    }
100 
101   /**
102    * Initializes default realizers
103    * @param view The respective Graph2DView.
104    */
105   protected void initializeDefaultRealizers(Graph2DView view) {
106     Graph2D graph = view.getGraph2D();
107     final EdgeRealizer der = graph.getDefaultEdgeRealizer();
108     if (der instanceof PolyLineEdgeRealizer){
109       ((PolyLineEdgeRealizer)der).setSmoothedBends(true);
110       der.setTargetArrow(Arrow.STANDARD);
111     }
112   }
113 
114   /**
115    * Creates a default flowchart realizers palette
116    * @param view The respective Graph2DView that is the target of the drag&drop action from the realizer palette.
117    */
118   private JComponent createDefaultPalette(final Graph2DView view) {
119 
120     final ArrayList realizers = new ArrayList();
121     addDefaultTemplates(realizers);
122 
123     //add the realizer list to the panel
124     //create the drag and drop list filled with the available realizer configurations
125     dropSupport = new DragAndDropSupport(realizers, view);
126     final JList realizerList = dropSupport.getList();
127     realizerList.setCellRenderer(new RealizerCellRenderer(60, 45) {
128       private Map nodeTips = new HashMap();
129       private Map edgeTips = new HashMap();
130 
131       protected Icon createEdgeRealizerIcon(EdgeRealizer realizer, int iconWidth, int iconHeight) {
132         final EdgeRealizerIcon icon = createIcon(realizer, iconWidth, iconHeight);
133         icon.setDrawingBends(false);
134         return icon;
135       }
136 
137       private EdgeRealizerIcon createIcon(EdgeRealizer realizer, int iconWidth, int iconHeight) {
138         if (realizer.labelCount() > 0) {
139           final String text = realizer.getLabelText();
140           if ("No".equalsIgnoreCase(text) || "Yes".equalsIgnoreCase(text)) {
141             return new EdgeRealizerIcon(realizer, iconWidth, iconHeight) {
142               protected YPoint calculateSourceBend(EdgeRealizer realizer, int iconWidth, int iconHeight) {
143                 return new YPoint(0.5 * iconWidth, iconHeight - realizer.getLabel().getHeight() - 2);
144               }
145             };
146           }
147         }
148         return new EdgeRealizerIcon(realizer, iconWidth, iconHeight);
149       }
150 
151       protected String createEdgeToolTipText(EdgeRealizer realizer) {
152         if(edgeTips.containsKey(realizer)){
153           return (String) edgeTips.get(realizer);
154         }else{
155           final String text = FlowchartPalette.this.createEdgeToolTipText(realizer);
156           edgeTips.put(realizer, text);
157           return text;
158         }
159       }
160 
161       protected String createNodeToolTipText( final NodeRealizer realizer ) {
162         if (nodeTips.containsKey(realizer)) {
163           return (String) nodeTips.get(realizer);
164         } else {
165           final String text = FlowchartPalette.this.createNodeToolTipText(realizer);
166           nodeTips.put(realizer, text);
167           return text;
168         }
169       }
170     });
171     realizerList.setLayoutOrientation(JList.HORIZONTAL_WRAP);
172     realizerList.setVisibleRowCount(-1);
173     JScrollPane palette = new JScrollPane(realizerList);
174     palette.setPreferredSize(new Dimension(220, 300));
175     return palette;
176   }
177 
178   protected String createEdgeToolTipText( final EdgeRealizer realizer ) {
179     return null;
180   }
181 
182   protected String createNodeToolTipText( final NodeRealizer realizer ) {
183     if (realizer instanceof GenericNodeRealizer) {
184       String s = ((GenericNodeRealizer) realizer).getConfiguration();
185       if (s != null) {
186         s = s.trim();
187         final String prefix = "com.yworks.flowchart.";
188         if (s.startsWith(prefix)) {
189           s = s.substring(prefix.length());
190           final int l = s.length();
191           final char[] chars = s.toCharArray();
192           final StringBuffer sb = new StringBuffer(s.length() + 4);
193           int last = 0;
194           String del = "";
195           for (int i = 1; i < l; ++i) {
196             if (Character.isUpperCase(chars[i])) {
197               sb.append(del).append(Character.toUpperCase(chars[last]));
198               sb.append(chars, last + 1, i - last - 1);
199               last = i;
200               del = " ";
201             }
202           }
203           if (last < l) {
204             sb.append(del).append(Character.toUpperCase(chars[last]));
205             sb.append(chars, last + 1, l - last - 1);
206           }
207           return sb.toString();
208         }
209       }
210     }
211     return null;
212   }
213 
214   /**
215    * Adds default flowchart templates to the palette list.
216    *
217    * @param realizers The list of all template realizers
218    */
219   protected void addDefaultTemplates(final List realizers) {
220     realizers.add(FlowchartRealizerFactory.createData());
221     realizers.add(FlowchartRealizerFactory.createDirectData());
222     realizers.add(FlowchartRealizerFactory.createDataBase());
223     realizers.add(FlowchartRealizerFactory.createProcess());
224     realizers.add(FlowchartRealizerFactory.createDecision());
225     realizers.add(FlowchartRealizerFactory.createDocument());
226     realizers.add(FlowchartRealizerFactory.createStart1());
227     realizers.add(FlowchartRealizerFactory.createStart2());
228     realizers.add(FlowchartRealizerFactory.createPredefinedProcess());
229     realizers.add(FlowchartRealizerFactory.createStoredData());
230     realizers.add(FlowchartRealizerFactory.createInternalStorage());
231     realizers.add(FlowchartRealizerFactory.createSequentialData());
232     realizers.add(FlowchartRealizerFactory.createManualInput());
233     realizers.add(FlowchartRealizerFactory.createCard());
234     realizers.add(FlowchartRealizerFactory.createPaperTape());
235     realizers.add(FlowchartRealizerFactory.createCloud());
236     realizers.add(FlowchartRealizerFactory.createDelay());
237     realizers.add(FlowchartRealizerFactory.createDisplay());
238     realizers.add(FlowchartRealizerFactory.createManualOperation());
239     realizers.add(FlowchartRealizerFactory.createPreparation());
240     realizers.add(FlowchartRealizerFactory.createLoopLimit());
241     realizers.add(FlowchartRealizerFactory.createLoopLimitEnd());
242     realizers.add(FlowchartRealizerFactory.createTerminator());
243     realizers.add(FlowchartRealizerFactory.createOnPageReference());
244     realizers.add(FlowchartRealizerFactory.createOffPageReference());
245     realizers.add(FlowchartRealizerFactory.createAnnotation(FlowchartAnnotationPainter.PROPERTY_ORIENTATION_VALUE_AUTO));
246     realizers.add(FlowchartRealizerFactory.createUserMessage());
247     realizers.add(FlowchartRealizerFactory.createNetworkMessage());
248     realizers.add(FlowchartRealizerFactory.createDefaultConnection());
249     realizers.add(FlowchartRealizerFactory.createNoConnection());
250     realizers.add(FlowchartRealizerFactory.createYesConnection());
251   }
252 
253 
254   private static final class DragAndDropSupport {
255     private final JList realizerList;
256     private DropSupport dropSupport;
257 
258     public DragAndDropSupport(Collection realizers, final Graph2DView view) {
259       // create the drop support class that can be used for dropping realizers
260       // onto the Graph2DView
261       dropSupport = new DropSupport(view);
262 
263       dropSupport.setPreviewEnabled(true);
264 
265       // when a node is dropped on an edge, split the edge
266       dropSupport.setEdgeSplittingEnabled(true);
267       // when an edge is split, remove bends that lie inside the dropped node
268       dropSupport.getEdgeSplitSupport().setRemovingInnerBends(true);
269 
270       // create a nice GUI for displaying NodeRealizers
271       DefaultListModel model = new DefaultListModel();
272       for (Iterator it = realizers.iterator(); it.hasNext();) {
273         model.addElement(it.next());
274       }
275       realizerList = new JList(model);
276       realizerList.setCellRenderer(new RealizerCellRenderer(120, 45));
277 
278       // set the currently selected NodeRealizer as default nodeRealizer
279       realizerList.addListSelectionListener(new ListSelectionListener() {
280         public void valueChanged(ListSelectionEvent e) {
281           if (realizerList.getSelectedValue() instanceof NodeRealizer) {
282             nodeRealizerSelected(view, (NodeRealizer) realizerList.getSelectedValue());
283           } else if (realizerList.getSelectedValue() instanceof EdgeRealizer) {
284             edgeRealizerSelected(view, (EdgeRealizer) realizerList.getSelectedValue());
285           }
286         }
287       });
288 
289       realizerList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
290       realizerList.setSelectedIndex(0);
291 
292       // define the realizer list to be the drag source
293       // use the string-valued name of the realizer as transferable
294       final DragSource dragSource = new DragSource();
295       dragSource.createDefaultDragGestureRecognizer(realizerList, DnDConstants.ACTION_MOVE,
296           new DragGestureListener() {
297             public void dragGestureRecognized(DragGestureEvent event) {
298               final Object value = realizerList.getSelectedValue();
299               if (value instanceof NodeRealizer) {
300                 NodeRealizer nr = (NodeRealizer) value;
301                 // use the drop support class to initialize the drag and drop operation.
302                 dropSupport.startDrag(dragSource, nr, event, DragSource.DefaultMoveDrop);
303               } else if (value instanceof EdgeRealizer) {
304                 EdgeRealizer nr = (EdgeRealizer) value;
305                 // use the drop support class to initialize the drag and drop operation.
306                 dropSupport.startDrag(dragSource, nr, event, DragSource.DefaultMoveDrop);
307               }
308             }
309           });
310     }
311 
312     public void configureSnapping(final boolean snapping, final int nodeToNodeDistance, final int nodeToEdgeDistance,
313                                   final boolean previewEnabled) {
314       configureDropSupport(dropSupport, snapping, previewEnabled, nodeToNodeDistance, nodeToEdgeDistance);
315     }
316 
317     protected static void configureDropSupport(final DropSupport dropSupport, final boolean snapping,
318                                                final boolean previewEnabled, final double nodeToNodeDistance,
319                                                final double nodeToEdgeDistance) {
320       dropSupport.setSnappingEnabled(snapping);
321       dropSupport.getSnapContext().setNodeToNodeDistance(nodeToNodeDistance);
322       dropSupport.getSnapContext().setNodeToEdgeDistance(nodeToEdgeDistance);
323       dropSupport.getSnapContext().setUsingSegmentSnapLines(snapping);
324       dropSupport.setPreviewEnabled(previewEnabled);
325     }
326 
327     /**
328      * Callback method that is triggered whenever the selection changes in the JList. This method sets the given
329      * NodeRealizer as the view's graph default node realizer.
330      */
331     protected void nodeRealizerSelected(Graph2DView view, NodeRealizer realizer) {
332       view.getGraph2D().setDefaultNodeRealizer(realizer);
333     }
334 
335     /**
336      * Callback method that is triggered whenever the selection changes in the JList. This method sets the given
337      * EdgeRealizer as the view's graph default node realizer.
338      */
339     protected void edgeRealizerSelected(Graph2DView view, EdgeRealizer realizer) {
340       view.getGraph2D().setDefaultEdgeRealizer(realizer);
341     }
342 
343     /** Return the JList that has been configured by this support class. */
344     public JList getList() {
345       return realizerList;
346     }
347   }
348 }