1
14 package demo.layout.tree;
15
16 import demo.view.DemoBase;
17 import y.algo.Bfs;
18 import y.base.Edge;
19 import y.base.EdgeMap;
20 import y.base.Node;
21 import y.base.NodeCursor;
22 import y.base.NodeList;
23 import y.base.NodeMap;
24 import y.geom.YPoint;
25 import y.layout.PortConstraint;
26 import y.layout.PortConstraintKeys;
27 import y.layout.tree.DefaultNodePlacer;
28 import y.layout.tree.GenericTreeLayouter;
29 import y.layout.tree.NodePlacer;
30 import y.util.DataProviderAdapter;
31 import y.view.Arrow;
32 import y.view.CreateChildEdgeMode;
33 import y.view.EdgeRealizer;
34 import y.view.EditMode;
35 import y.view.Graph2D;
36 import y.view.Graph2DSelectionEvent;
37 import y.view.Graph2DSelectionListener;
38 import y.view.HotSpotMode;
39 import y.view.LineType;
40 import y.view.NodeRealizer;
41 import y.view.PolyLineEdgeRealizer;
42 import y.view.PopupMode;
43 import y.view.PortAssignmentMoveSelectionMode;
44
45 import javax.swing.AbstractAction;
46 import javax.swing.JButton;
47 import javax.swing.JLabel;
48 import javax.swing.JMenu;
49 import javax.swing.JPanel;
50 import javax.swing.JPopupMenu;
51 import javax.swing.JSpinner;
52 import javax.swing.JSplitPane;
53 import javax.swing.SpinnerNumberModel;
54 import javax.swing.event.ChangeEvent;
55 import javax.swing.event.ChangeListener;
56 import java.awt.BorderLayout;
57 import java.awt.Color;
58 import java.awt.Cursor;
59 import java.awt.FlowLayout;
60 import java.awt.EventQueue;
61 import java.awt.event.ActionEvent;
62 import java.util.ArrayList;
63 import java.util.List;
64 import java.util.Locale;
65
66
82 public class IncrementalTreeLayouterDemo extends DemoBase {
83 private static final Color[] layerColors = {Color.red, Color.orange, Color.yellow, Color.cyan, Color.green,
84 Color.blue};
85
86 private EdgeMap targetPortMap;
87 private NodeMap nodePlacerMap;
88
89 private PortAssignmentMoveSelectionMode paMode;
90 private double hDistance = 40.0;
91
92 private double vDistance = 40.0;
93
94 private GenericTreeLayouter treeLayouter;
95
96 private DefaultNodePlacerConfigPanel configPanel;
97
98 private List layerStyles = new ArrayList();
99
100 {
101 layerStyles.add(new DefaultNodePlacer(DefaultNodePlacer.PLACEMENT_HORIZONTAL_DOWNWARD,
102 DefaultNodePlacer.ALIGNMENT_MEDIAN, 40.0, 40.0));
103 layerStyles.add(new DefaultNodePlacer(DefaultNodePlacer.PLACEMENT_VERTICAL_TO_RIGHT,
104 DefaultNodePlacer.ALIGNMENT_LEADING_OFFSET, 20.0, 40.0));
105 layerStyles.add(new DefaultNodePlacer(DefaultNodePlacer.PLACEMENT_HORIZONTAL_DOWNWARD,
106 DefaultNodePlacer.ALIGNMENT_LEADING_OFFSET, DefaultNodePlacer.ROUTING_FORK_AT_ROOT, 10.0, 20.0));
107 layerStyles.add(new DefaultNodePlacer(DefaultNodePlacer.PLACEMENT_HORIZONTAL_DOWNWARD,
108 DefaultNodePlacer.ALIGNMENT_MEDIAN, 40.0, 40.0));
109 }
110
111 public IncrementalTreeLayouterDemo() {
112 final Graph2D graph = view.getGraph2D();
113 EdgeRealizer defaultER = graph.getDefaultEdgeRealizer();
114 defaultER.setArrow(Arrow.STANDARD);
115 ((PolyLineEdgeRealizer) defaultER).setSmoothedBends(true);
116 defaultER.setLineType(LineType.LINE_2);
117
118 EdgeMap sourcePortMap = graph.createEdgeMap();
119 targetPortMap = graph.createEdgeMap();
120 NodeMap portAssignmentMap = graph.createNodeMap();
121 nodePlacerMap = graph.createNodeMap();
122 graph.addDataProvider(GenericTreeLayouter.NODE_PLACER_DPKEY, nodePlacerMap);
123 graph.addDataProvider(GenericTreeLayouter.PORT_ASSIGNMENT_DPKEY, portAssignmentMap);
124 graph.addDataProvider(GenericTreeLayouter.CHILD_COMPARATOR_DPKEY, new ChildEdgeComparatorProvider());
125 graph.addDataProvider(PortConstraintKeys.SOURCE_PORT_CONSTRAINT_KEY, sourcePortMap);
126 graph.addDataProvider(PortConstraintKeys.TARGET_PORT_CONSTRAINT_KEY, targetPortMap);
127
128
129 paMode.setSpc(sourcePortMap);
130 paMode.setTpc(targetPortMap);
131
132 treeLayouter = new GenericTreeLayouter();
133
134 configPanel = new DefaultNodePlacerConfigPanel();
135 configPanel.adoptPlacerValues((NodePlacer) layerStyles.get(0));
136
137 JPanel layerChooserPanel = new JPanel(new FlowLayout(FlowLayout.LEADING));
138 layerChooserPanel.add(new JLabel("Layer: "));
139 final JSpinner spinner = new JSpinner(new SpinnerNumberModel(1, 1, 100, 1));
140 spinner.addChangeListener(new ChangeListener() {
141 public void stateChanged(ChangeEvent ce) {
142 final int layer = ((Number) spinner.getValue()).intValue() - 1;
143 while (layer >= layerStyles.size()) {
144 layerStyles.add(new DefaultNodePlacer(DefaultNodePlacer.PLACEMENT_HORIZONTAL_DOWNWARD, 40.0, 40.0));
145 }
146 NodePlacer placer = (NodePlacer) layerStyles.get(layer);
147 configPanel.adoptPlacerValues(placer);
148 }
149 }
150 );
151 layerChooserPanel.add(spinner);
152
153 configPanel.addChangeListener(new ChangeListener() {
154 public void stateChanged(ChangeEvent ce) {
155 final int layer = ((Number) spinner.getValue()).intValue() - 1;
156 while (layer >= layerStyles.size()) {
157 layerStyles.add(new DefaultNodePlacer(DefaultNodePlacer.PLACEMENT_HORIZONTAL_DOWNWARD, 40.0, 40.0));
158 }
159 layerStyles.set(layer, configPanel.createPlacerCopy());
160 }
161 }
162 );
163
164 JButton button = new JButton(new AbstractAction("Apply") {
165 public void actionPerformed(ActionEvent ae) {
166 final int layer = ((Number) spinner.getValue()).intValue() - 1;
167 NodePlacer placer = (NodePlacer) layerStyles.get(layer);
168 NodeList[] layers = Bfs.getLayers(graph, new NodeList(graph.firstNode()));
169 if (layer < layers.length) {
170 for (NodeCursor nc = layers[layer].nodes(); nc.ok(); nc.next()) {
171 nodePlacerMap.set(nc.node(), placer);
172 }
173 calcLayout();
174 }
175 }
176 });
177
178 graph.addGraph2DSelectionListener(new Graph2DSelectionListener() {
179 public void onGraph2DSelectionEvent(Graph2DSelectionEvent ev) {
180 if (ev.isNodeSelection() && graph.isSelectionSingleton()) {
181 Node n = (Node) ev.getSubject();
182 int depth = 1;
183 while (n.inDegree() > 0) {
184 n = n.firstInEdge().source();
185 depth++;
186 }
187 spinner.setValue(new Integer(depth));
188 }
189 }
190 }
191 );
192 layerChooserPanel.add(button);
193
194 JPanel rightPanel = new JPanel(new BorderLayout());
195 rightPanel.add(configPanel, BorderLayout.CENTER);
196 rightPanel.add(layerChooserPanel, BorderLayout.NORTH);
197
198 JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, rightPanel, view);
199 sp.setOneTouchExpandable(true);
200 sp.setContinuousLayout(false);
201 contentPane.add(sp, BorderLayout.CENTER);
202 createSampleGraph(graph);
203 }
204
205 final class ChildEdgeComparatorProvider extends DataProviderAdapter {
206 public Object get(Object forRootNode) {
207 NodePlacer placer = (NodePlacer) nodePlacerMap.get(forRootNode);
208 if (placer instanceof DefaultNodePlacer) {
209 return ((DefaultNodePlacer) placer).createComparator();
210 }
211 return null;
212 }
213 }
214
215 private void createSampleGraph(Graph2D graph) {
216 graph.clear();
217 Node root = graph.createNode();
218 graph.getRealizer(root).setFillColor(layerColors[0]);
219 nodePlacerMap.set(root, layerStyles.get(0));
220 createChildren(graph, root, 4, 1, 2);
221 calcLayout();
222 }
223
224 private void createChildren(Graph2D graph, Node root, int children, int layer, int layers) {
225 for (int i = 0; i < children; i++) {
226 Node child = graph.createNode();
227 graph.createEdge(root, child);
228 graph.getRealizer(child).setFillColor(layerColors[layer % layerColors.length]);
229 if (layerStyles.size() > layer) {
230 nodePlacerMap.set(child, layerStyles.get(layer));
231 }
232 if (layers > 0) {
233 createChildren(graph, child, children, layer + 1, layers - 1);
234 }
235 }
236 }
237
238 protected boolean isDeletionEnabled() {
239 return false;
240 }
241
242 protected void registerViewModes() {
243 EditMode editMode = new TreeCreateEditMode();
244 view.addViewMode(editMode);
245 }
246
247
248 public void calcLayout() {
249 if (!view.getGraph2D().isEmpty()) {
250 Cursor oldCursor = view.getViewCursor();
251 try {
252 view.setViewCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
253 view.applyLayoutAnimated(treeLayouter);
254 } finally {
255 view.setViewCursor(oldCursor);
256 }
257 }
258 }
259
260 final class TreeCreateChildEdgeMode extends CreateChildEdgeMode {
261
262 NodeRealizer activeDummyTargetRealizer;
263
264 protected boolean acceptSourceNode(Node source, double x, double y) {
265 final boolean accept = super.acceptSourceNode(source, x, y);
266 activeDummyTargetRealizer = createChildNodeRealizer();
267 int depth = 1;
268 for (Node n = source; n.inDegree() > 0; n = n.firstInEdge().source()){
269 depth++;
270 }
271 activeDummyTargetRealizer.setFillColor(layerColors[depth % layerColors.length]);
272 return accept;
273 }
274
275 protected NodeRealizer createDummyTargetNodeRealizer(double x, double y) {
276 return activeDummyTargetRealizer;
277 }
278
279 protected void edgeCreated(Edge e) {
280 int depth = 1;
281 for (Node n = e.source(); n.inDegree() > 0; n = n.firstInEdge().source()) {
282 depth++;
283 }
284 Graph2D g = getGraph2D();
285 g.getRealizer(e.target()).setFillColor(layerColors[depth % layerColors.length]);
286 EdgeRealizer er = g.getRealizer(e);
287 if (nodePlacerMap.get(e.source()) == null) {
288 parseNodePlacement(g, e, er);
289 }
290 if (layerStyles.size() > depth) {
291 nodePlacerMap.set(e.target(), layerStyles.get(depth));
292 }
293 parseTargetPort(g, e, er);
294 g.unselectAll();
295 calcLayout();
296 }
297
298 private void parseNodePlacement(Graph2D g, Edge e, EdgeRealizer er) {
299 YPoint firstPoint = er.bendCount() > 0 ? new YPoint(er.firstBend().getX(), er.firstBend().getY()) :
300 g.getTargetPointAbs(e);
301 NodeRealizer source = g.getRealizer(e.source());
302 double dx = firstPoint.x - source.getCenterX();
303 double dy = firstPoint.y - source.getCenterY();
304 final byte placement;
305 final byte alignment = DefaultNodePlacer.ALIGNMENT_MEDIAN;
306 final byte routing = DefaultNodePlacer.ROUTING_FORK;
307 if (Math.abs(dx) > Math.abs(dy)) {
308 if (dx > 0.0) {
309 placement = DefaultNodePlacer.PLACEMENT_VERTICAL_TO_RIGHT;
310 } else {
311 placement = DefaultNodePlacer.PLACEMENT_VERTICAL_TO_LEFT;
312 }
313 } else {
314 if (dy > 0.0) {
315 placement = DefaultNodePlacer.PLACEMENT_HORIZONTAL_DOWNWARD;
316 } else {
317 placement = DefaultNodePlacer.PLACEMENT_HORIZONTAL_UPWARD;
318 }
319 }
320 nodePlacerMap.set(e.source(), new DefaultNodePlacer(placement, alignment, routing, hDistance, vDistance));
321 }
322
323 private void parseTargetPort(Graph2D g, Edge e, EdgeRealizer er) {
324 if (er.bendCount() > 0) {
325 YPoint lastPoint = new YPoint(er.lastBend().getX(), er.lastBend().getY());
326 NodeRealizer target = g.getRealizer(e.target());
327 double dx = lastPoint.x - target.getCenterX();
328 double dy = lastPoint.y - target.getCenterY();
329 byte side = PortConstraint.ANY_SIDE;
330 if (Math.abs(dx) > Math.abs(dy)) {
331 if (dx > 0.0) {
332 side = PortConstraint.EAST;
333 } else {
334 side = PortConstraint.WEST;
335 }
336 } else {
337 if (dy > 0.0) {
338 side = PortConstraint.SOUTH;
339 } else {
340 side = PortConstraint.NORTH;
341 }
342 }
343 targetPortMap.set(e, PortConstraint.create(side));
344 }
345 }
346
347 protected NodeRealizer createChildNodeRealizer() {
348 NodeRealizer retValue;
349 retValue = super.createChildNodeRealizer();
350 retValue.setLabelText("");
351 return retValue;
352 }
353
354 }
355
356
357 final class TreeLayouterPopupMode extends PopupMode {
358 private JPopupMenu nodePlacementMenu;
359
360 TreeLayouterPopupMode() {
361 nodePlacementMenu = new JPopupMenu();
362 JMenu alignment = new JMenu("Root node Alignment");
363 JMenu placement = new JMenu("Child Placement");
364 JMenu routing = new JMenu("Routing Style");
365
366 nodePlacementMenu.add(placement);
367 nodePlacementMenu.add(alignment);
368 nodePlacementMenu.add(routing);
369
370 placement.add(new PlacementAction("Horizontally Downwards", DefaultNodePlacer.PLACEMENT_HORIZONTAL_DOWNWARD));
371 placement.add(new PlacementAction("Horizontally Upwards", DefaultNodePlacer.PLACEMENT_HORIZONTAL_UPWARD));
372 placement.add(new PlacementAction("Vertically to Left", DefaultNodePlacer.PLACEMENT_VERTICAL_TO_LEFT));
373 placement.add(new PlacementAction("Vertically to right", DefaultNodePlacer.PLACEMENT_VERTICAL_TO_RIGHT));
374
375 alignment.add(new AlignmentAction("Offset Leading", DefaultNodePlacer.ALIGNMENT_LEADING_OFFSET));
376 alignment.add(new AlignmentAction("Leading", DefaultNodePlacer.ALIGNMENT_LEADING));
377 alignment.add(new AlignmentAction("Centered", DefaultNodePlacer.ALIGNMENT_CENTER));
378 alignment.add(new AlignmentAction("Median", DefaultNodePlacer.ALIGNMENT_MEDIAN));
379 alignment.add(new AlignmentAction("Trailing", DefaultNodePlacer.ALIGNMENT_TRAILING));
380 alignment.add(new AlignmentAction("Offset Trailing", DefaultNodePlacer.ALIGNMENT_TRAILING_OFFSET));
381
382 routing.add(new RoutingAction("Fork", DefaultNodePlacer.ROUTING_FORK));
383 routing.add(new RoutingAction("Fork at Root", DefaultNodePlacer.ROUTING_FORK_AT_ROOT));
384 routing.add(new RoutingAction("Poly Line", DefaultNodePlacer.ROUTING_POLY_LINE));
385 routing.add(new RoutingAction("Straight", DefaultNodePlacer.ROUTING_STRAIGHT));
386 }
387
388 public JPopupMenu getNodePopup(final Node v) {
389 return nodePlacementMenu;
390 }
391
392 public JPopupMenu getSelectionPopup(double x, double y) {
393 if (getGraph2D().selectedNodes().ok()) {
394 return nodePlacementMenu;
395 } else {
396 return null;
397 }
398 }
399
400 }
401
402 abstract class AssignLayouterAction extends AbstractAction {
403
404 protected AssignLayouterAction(String name) {
405 super(name);
406 }
407
408 public void actionPerformed(ActionEvent e) {
409 NodeList selectedNodes = new NodeList(IncrementalTreeLayouterDemo.this.view.getGraph2D().selectedNodes());
410
411 NodePlacer placer = (NodePlacer) nodePlacerMap.get(selectedNodes.firstNode());
412 placer = getPlacer(placer);
413
414 DefaultNodePlacer dnp = (DefaultNodePlacer) placer;
415 for (NodeCursor nc = selectedNodes.nodes(); nc.ok(); nc.next()) {
416 nodePlacerMap.set(nc.node(), new DefaultNodePlacer(
417 dnp.getChildPlacement(),
418 dnp.getRootAlignment(),
419 dnp.getRoutingStyle(),
420 dnp.getHorizontalDistance(),
421 dnp.getVerticalDistance()));
422 }
423 calcLayout();
424 }
425
426 protected abstract NodePlacer getPlacer(NodePlacer placer);
427 }
428
429 final class PlacementAction extends AssignLayouterAction {
430
431 private byte newPlacement;
432
433 public PlacementAction(String name, byte newPlacement) {
434 super(name);
435 this.newPlacement = newPlacement;
436 }
437
438 protected NodePlacer getPlacer(NodePlacer placer) {
439 if (placer instanceof DefaultNodePlacer) {
440 placer = (DefaultNodePlacer) ((DefaultNodePlacer) placer).clone();
441 ((DefaultNodePlacer) placer).setChildPlacement(newPlacement);
442 } else {
443 placer = new DefaultNodePlacer(newPlacement, DefaultNodePlacer.ALIGNMENT_MEDIAN, hDistance, vDistance);
444 }
445 return placer;
446 }
447 }
448
449 final class AlignmentAction extends AssignLayouterAction {
450 private byte newAlignment;
451
452 public AlignmentAction(String name, byte newAlignment) {
453 super(name);
454 this.newAlignment = newAlignment;
455 }
456
457 protected NodePlacer getPlacer(NodePlacer placer) {
458 if (placer instanceof DefaultNodePlacer) {
459 placer = (DefaultNodePlacer) ((DefaultNodePlacer) placer).clone();
460 ((DefaultNodePlacer) placer).setRootAlignment(newAlignment);
461 } else {
462 placer = new DefaultNodePlacer(DefaultNodePlacer.PLACEMENT_HORIZONTAL_DOWNWARD, newAlignment, hDistance,
463 vDistance);
464 }
465 return placer;
466 }
467 }
468
469 final class RoutingAction extends AssignLayouterAction {
470 private byte newRouting;
471
472 public RoutingAction(String name, byte newRouting) {
473 super(name);
474 this.newRouting = newRouting;
475 }
476
477 protected NodePlacer getPlacer(NodePlacer placer) {
478 if (placer instanceof DefaultNodePlacer) {
479 placer = (DefaultNodePlacer) ((DefaultNodePlacer) placer).clone();
480 ((DefaultNodePlacer) placer).setRoutingStyle(newRouting);
481 } else {
482 placer = new DefaultNodePlacer(DefaultNodePlacer.PLACEMENT_HORIZONTAL_DOWNWARD,
483 DefaultNodePlacer.ALIGNMENT_CENTER, newRouting, hDistance, vDistance);
484 }
485 return placer;
486 }
487 }
488
489 final class TreeHotSpotMode extends HotSpotMode {
490 public void mouseReleasedLeft(double x, double y) {
491 super.mouseReleasedLeft(x, y);
492 calcLayout();
493 }
494 }
495
496 final class TreeCreateEditMode extends EditMode {
497 TreeCreateEditMode() {
498 super();
499 setMoveSelectionMode(paMode = new TreePortAssignmentMode());
500 setCreateEdgeMode(new TreeCreateChildEdgeMode());
501 setHotSpotMode(new TreeHotSpotMode());
502 setPopupMode(new TreeLayouterPopupMode());
503 }
504
505 public boolean doAllowNodeCreation() {
506 return getGraph2D().N() == 0;
507 }
508
509 protected void nodeCreated(Node v) {
510 super.nodeCreated(v);
511 nodePlacerMap.set(v, configPanel.createPlacerCopy());
512 }
513
514 }
515
516 final class TreePortAssignmentMode extends PortAssignmentMoveSelectionMode {
517 TreePortAssignmentMode() {
518 super(null, null);
519 }
520
521 protected boolean isPortReassignmentAllowed(Edge edge, boolean source) {
522 return !source;
523 }
524
525
530 protected void selectionMovedAction(double dx, double dy, double x, double y) {
531 super.selectionMovedAction(dx, dy, x, y);
532 calcLayout();
533 }
534
535 }
536
537
540 public static void main(String[] args) {
541 EventQueue.invokeLater(new Runnable() {
542 public void run() {
543 Locale.setDefault(Locale.ENGLISH);
544 initLnF();
545 (new IncrementalTreeLayouterDemo()).start("Incremental Tree Layouter Demo");
546 }
547 });
548 }
549 }
550