1   /****************************************************************************
2    **
3    ** This file is part of yFiles-2.7. 
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-2009 by yWorks GmbH, Vor dem Kreuzberg 28, 
11   ** 72070 Tuebingen, Germany. All rights reserved.
12   **
13   ***************************************************************************/
14  package demo.view.viewmode;
15  
16  import demo.view.DemoBase;
17  import y.base.Node;
18  import y.geom.YPoint;
19  import y.util.DataProviders;
20  import y.view.CreateEdgeMode;
21  import y.view.Drawable;
22  import y.view.EditMode;
23  import y.view.HotSpotMode;
24  import y.view.MovePortMode;
25  import y.view.MoveSelectionMode;
26  import y.view.OrthogonalMoveBendsMode;
27  import y.view.SnapLine;
28  
29  import javax.swing.BorderFactory;
30  import javax.swing.JSlider;
31  import javax.swing.JToggleButton;
32  import javax.swing.JToolBar;
33  import javax.swing.SwingConstants;
34  import javax.swing.event.ChangeEvent;
35  import javax.swing.event.ChangeListener;
36  import java.awt.Color;
37  import java.awt.Dimension;
38  import java.awt.Graphics2D;
39  import java.awt.Rectangle;
40  import java.awt.EventQueue;
41  
42  /**
43   * Demonstrates {@link EditMode}'s snapping feature in conjunction with orthogonal edges. <br>
44   * This demo can be used to toggle the snapping feature on and off. It shows how a custom {@link SnapLine}
45   * (the red vertical line) can be used to snap nodes and edges to other entities.
46   * Toggling the button in the toolbar toggles snapping on and off, the sliders can be used to adjust the
47   * preferred distance between nodes and edges. This will influence the "preferred distance snap lines."
48   */
49  public class SnapLineDemo extends DemoBase {
50  
51    private EditMode editMode;
52    private JToggleButton snapLineButton;
53    /**
54     * A custom single snap line that will displayed in the view and used by the {@link MoveSelectionMode}'s
55     * {@link y.view.MoveSnapContext}.
56     */
57    private SnapLine snapLine;
58  
59    protected void initialize() {
60      super.initialize();
61      view.getGraph2D().addDataProvider(EditMode.ORTHOGONAL_ROUTING_DPKEY, DataProviders.createConstantDataProvider(Boolean.TRUE));
62      final Node n1 = view.getGraph2D().createNode(40, 30, "1");
63      final Node n2 = view.getGraph2D().createNode(40, 90, "2");
64      final Node n3 = view.getGraph2D().createNode(40, 210,"3");
65      view.getGraph2D().createEdge(n1, n2);
66      view.getGraph2D().createEdge(n2, n3);
67      view.updateWorldRect();
68  
69      snapLine = new SnapLine(SnapLine.VERTICAL, SnapLine.CENTER, new YPoint(200, 200), 0, 400, null, 1.0d);
70      view.getGraph2D().addDrawable(new Drawable() {
71        public void paint(Graphics2D g) {
72          g.setColor(Color.red);
73          snapLine.paint(g);
74        }
75  
76        public Rectangle getBounds() {
77          return snapLine.getBounds();
78        }
79      });
80    }
81  
82  
83    protected JToolBar createToolBar() {
84      JToolBar toolBar = super.createToolBar();
85      snapLineButton = new JToggleButton("Snapping");
86      toolBar.add(snapLineButton);
87  
88      final JSlider s1 = new JSlider(SwingConstants.HORIZONTAL, 0, 80, 30);
89      s1.setBorder(BorderFactory.createTitledBorder("Node To Node"));
90      s1.setMaximumSize(new Dimension(200, 100));
91      toolBar.add(s1);
92      final JSlider s2 = new JSlider(SwingConstants.HORIZONTAL, 0, 80, 30);
93      s2.setBorder(BorderFactory.createTitledBorder("Node To Edge"));
94      s2.setMaximumSize(new Dimension(200, 100));
95      toolBar.add(s2);
96      final JSlider s3 = new JSlider(SwingConstants.HORIZONTAL, 0, 80, 30);
97      s3.setBorder(BorderFactory.createTitledBorder("Edge To Edge"));
98      s3.setMaximumSize(new Dimension(200, 100));
99      toolBar.add(s3);
100     final ChangeListener listener = new ChangeListener() {
101       public void stateChanged(ChangeEvent e) {
102         configureSnapping(editMode, snapLineButton.isSelected(), snapLineButton.isSelected(), s1.getValue(),
103             s2.getValue(), s3.getValue());
104       }
105     };
106     s1.addChangeListener(listener);
107     s2.addChangeListener(listener);
108     s3.addChangeListener(listener);
109     snapLineButton.addChangeListener(listener);
110 
111     setUsingSnapping(true);
112     return toolBar;
113   }
114 
115   protected void registerViewModes() {
116     editMode = new EditMode();
117     ((MoveSelectionMode) editMode.getMoveSelectionMode()).getSnapContext().addSnapLine(snapLine);
118     ((CreateEdgeMode)editMode.getCreateEdgeMode()).setOrthogonalEdgeCreation(true);
119 
120     configureSnapping(editMode, true, true, 30, 15, 15);
121 
122     view.addViewMode(editMode);
123   }
124 
125   /**
126    * Helper method that enables and configures snapping and the automatic removal of inner bends for the ViewModes.
127    * @param editMode the mode to configure
128    * @param snapping whether to enable snapping
129    * @param removeInnerBends whether to remove inner bends after the various edit operations automatically
130    * @param nodeToNodeDistance The preferred distance between nodes. This will influence the creation of
131    * {@link y.view.SnapLine}s. Set it to <code>0.0d</code> in order to turn of preferred distance snapping.
132    * @param nodeToEdgeDistance The preferred distance between nodes and edge segments. This will influence the creation of
133    * {@link y.view.SnapLine}s. Set it to <code>0.0d</code> in order to turn of preferred distance snapping.
134    * @param edgeToEdgeDistance The preferred distance between parallel orthogonal edge segments. This will influence the creation of
135    * {@link y.view.SnapLine}s. Set it to <code>0.0d</code> in order to turn of preferred distance snapping.
136    */
137   public static void configureSnapping(final EditMode editMode, final boolean snapping, final boolean removeInnerBends,
138                                  final double nodeToNodeDistance, final double nodeToEdgeDistance,
139                                  final double edgeToEdgeDistance) {
140     editMode.setSnappingEnabled(snapping);
141     if (editMode.getHotSpotMode() instanceof HotSpotMode) {
142       ((HotSpotMode) editMode.getHotSpotMode()).setSnappingEnabled(snapping);
143       ((HotSpotMode) editMode.getHotSpotMode()).setRemovingInnerBends(removeInnerBends);
144     }
145     if (editMode.getMoveSelectionMode() instanceof MoveSelectionMode) {
146       final MoveSelectionMode msm = (MoveSelectionMode) editMode.getMoveSelectionMode();
147       msm.setSnappingEnabled(snapping);
148       msm.setRemovingInnerBends(removeInnerBends);
149       msm.getSnapContext().setNodeToNodeDistance(nodeToNodeDistance);
150       msm.getSnapContext().setNodeToEdgeDistance(nodeToEdgeDistance);
151       msm.getSnapContext().setEdgeToEdgeDistance(edgeToEdgeDistance);
152       msm.getSnapContext().setUsingSegmentSnapLines(snapping);
153       msm.getSnapContext().setUsingOrthogonalMovementConstraints(snapping);
154       msm.getSnapContext().setSnappingSegmentsToSnapLines(snapping);
155     }
156     if (editMode.getOrthogonalMoveBendsMode() instanceof OrthogonalMoveBendsMode) {
157       final OrthogonalMoveBendsMode ombm = (OrthogonalMoveBendsMode) editMode.getOrthogonalMoveBendsMode();
158       ombm.setSnappingEnabled(snapping);
159       ombm.setRemovingInnerBends(removeInnerBends);
160       ombm.getSnapContext().setNodeToEdgeDistance(nodeToEdgeDistance);
161       ombm.getSnapContext().setEdgeToEdgeDistance(edgeToEdgeDistance);
162       ombm.getSnapContext().setRenderingSnapLines(true);
163       ombm.setAutoBendInsertionEnabled(false);
164     }
165     if (editMode.getCreateEdgeMode() instanceof CreateEdgeMode) {
166       final CreateEdgeMode cem = (CreateEdgeMode) editMode.getCreateEdgeMode();
167       cem.setFuzzyTargetPortDetermination(snapping);
168       cem.setSnapToOrthogonalSegmentsDistance(snapping ? 5 : 0);
169       cem.setUsingNodeCenterSnapping(snapping);
170       cem.setSnappingOrthogonalSegments(snapping);
171     }
172     if (editMode.getMovePortMode() instanceof MovePortMode) {
173       final MovePortMode mpm = (MovePortMode) editMode.getMovePortMode();
174       mpm.setUsingRealizerPortCandidates(false);
175       mpm.setSegmentSnappingEnabled(snapping);
176       mpm.setUsingEdgeDistanceSnapLines(snapping);
177       mpm.setInvertedSnappingBehavior(true);
178       mpm.getSnapContext().setNodeToEdgeDistance(nodeToEdgeDistance);
179       mpm.getSnapContext().setEdgeToEdgeDistance(edgeToEdgeDistance);
180     }
181   }
182 
183   public boolean isUsingSnapping() {
184     return snapLineButton.isSelected();
185   }
186 
187   public void setUsingSnapping(boolean usingSnapping) {
188     this.snapLineButton.setSelected(usingSnapping);
189   }
190 
191   public static void main(String[] args) {
192     EventQueue.invokeLater(new Runnable() {
193       public void run() {
194         initLnF();
195         (new SnapLineDemo()).start("SnapLine Demo");
196       }
197     });
198   }
199 }