1
28 package demo.view.advanced.ports;
29
30 import demo.view.hierarchy.GroupingDemo;
31 import y.base.Edge;
32 import y.base.Node;
33 import y.base.NodeCursor;
34 import y.base.NodeMap;
35 import y.geom.YRectangle;
36 import y.io.GraphMLIOHandler;
37 import y.io.graphml.graph2d.Graph2DGraphMLHandler;
38 import y.layout.LayoutOrientation;
39 import y.layout.Layouter;
40 import y.layout.NodeHalo;
41 import y.layout.hierarchic.IncrementalHierarchicLayouter;
42 import y.layout.hierarchic.incremental.RoutingStyle;
43 import y.layout.hierarchic.incremental.SimplexNodePlacer;
44 import y.layout.router.polyline.EdgeRouter;
45 import y.util.Maps;
46 import y.view.EdgeRealizer;
47 import y.view.EditMode;
48 import y.view.GenericNodeRealizer;
49 import y.view.Graph2D;
50 import y.view.Graph2DLayoutExecutor;
51 import y.view.Graph2DViewActions;
52 import y.view.NodeLabel;
53 import y.view.NodePort;
54 import y.view.NodePortLayoutConfigurator;
55 import y.view.NodeRealizer;
56 import y.view.NodeStateChangeEdgeRouter;
57 import y.view.ProxyShapeNodeRealizer;
58 import y.view.ShapeNodePainter;
59 import y.view.TooltipMode;
60 import y.view.hierarchy.DefaultGenericAutoBoundsFeature;
61 import y.view.hierarchy.DefaultHierarchyGraphFactory;
62 import y.view.hierarchy.GenericGroupNodeRealizer;
63 import y.view.hierarchy.GroupNodePainter;
64 import y.view.hierarchy.HierarchyManager;
65
66 import java.awt.BorderLayout;
67 import java.awt.Color;
68 import java.awt.EventQueue;
69 import java.awt.Graphics2D;
70 import java.awt.Shape;
71 import java.awt.event.ActionEvent;
72 import java.awt.event.ItemEvent;
73 import java.awt.event.ItemListener;
74 import java.awt.geom.Rectangle2D;
75 import java.util.Locale;
76 import java.util.Map;
77 import java.util.WeakHashMap;
78 import javax.swing.AbstractAction;
79 import javax.swing.Action;
80 import javax.swing.ActionMap;
81 import javax.swing.BorderFactory;
82 import javax.swing.ButtonGroup;
83 import javax.swing.JSplitPane;
84 import javax.swing.JToggleButton;
85 import javax.swing.JToolBar;
86
87
129 public class NodePortsDemo extends GroupingDemo {
130 private boolean fixPortsForLayout;
131 private boolean sharedPorts;
132 private boolean groupEdges;
133
134 public NodePortsDemo() {
135 this(null);
136 }
137
138 public NodePortsDemo( final String helpFilePath ) {
139 final JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new Palette(view), view);
140 splitPane.setBorder(BorderFactory.createEmptyBorder());
141 contentPane.add(splitPane, BorderLayout.CENTER);
142 addHelpPane(helpFilePath);
143 }
144
145
148 protected void initialize() {
149 super.initialize();
150
151 new HierarchyManager(view.getGraph2D());
152 }
153
154
158 protected void registerViewActions() {
159 super.registerViewActions();
160
161 final ActionMap amap = view.getCanvasComponent().getActionMap();
162
163 final Graph2DViewActions.CloseGroupsAction closeGroups =
164 new Graph2DViewActions.CloseGroupsAction(view);
165 closeGroups.setNodeStateChangeHandler(new MyNodeStateChangeEdgeRouter(
171 new InterEdgeProcessor(new NormalEdgeProcessor())));
172 amap.put(Graph2DViewActions.CLOSE_GROUPS, closeGroups);
173 final Graph2DViewActions.OpenFoldersAction openFolders =
174 new Graph2DViewActions.OpenFoldersAction(view);
175 openFolders.setNodeStateChangeHandler(new MyNodeStateChangeEdgeRouter(
179 new NormalEdgeProcessor()));
180 amap.put(Graph2DViewActions.OPEN_FOLDERS, openFolders);
181 }
182
183
188 protected EditMode createEditMode() {
189 return new PortEditMode();
190 }
191
192
201 protected TooltipMode createTooltipMode() {
202 final TooltipMode tooltipMode = new TooltipMode();
203 tooltipMode.setNodeTipEnabled(false);
204 tooltipMode.setEdgeTipEnabled(false);
205 tooltipMode.setPortTipEnabled(true);
206 return tooltipMode;
207 }
208
209
213 protected JToolBar createToolBar() {
214 final Action edgeRoutingAction = new AbstractAction("Route Edges") {
215 public void actionPerformed(final ActionEvent e) {
216 routeOrthogonally();
217 }
218 };
219 edgeRoutingAction.putValue(Action.SMALL_ICON, SHARED_LAYOUT_ICON);
220
221 final Action layoutAction = new AbstractAction("Layout") {
222 public void actionPerformed(final ActionEvent e) {
223 layoutHierarchically();
224 }
225 };
226 layoutAction.putValue(Action.SMALL_ICON, SHARED_LAYOUT_ICON);
227
228
229 final JToolBar jtb = super.createToolBar();
230 jtb.addSeparator();
231 jtb.add(createActionControl(layoutAction));
232 jtb.add(createActionControl(edgeRoutingAction));
233 jtb.addSeparator(TOOLBAR_SMALL_SEPARATOR);
234 final JToggleButton fpJb = new JToggleButton("Fixed Ports");
235 fpJb.setToolTipText("Toggle the 'Fixed Ports' feature of the layout algorithms");
236 fpJb.addItemListener(new ItemListener() {
237 public void itemStateChanged( final ItemEvent e ) {
238 fixPortsForLayout = ItemEvent.SELECTED == e.getStateChange();
239 }
240 });
241 fpJb.doClick();
242 jtb.add(fpJb);
243 final JToggleButton geJb = new JToggleButton("Edge Grouping");
244 geJb.setToolTipText("Toggle the 'Automatic Edge Grouping' feature of the layout algorithms");
245 geJb.addItemListener(new ItemListener() {
246 public void itemStateChanged( final ItemEvent e ) {
247 groupEdges = ItemEvent.SELECTED == e.getStateChange();
248 }
249 });
250 jtb.add(geJb);
251 final JToggleButton spJb = new JToggleButton("Shared Ports");
252 spJb.setToolTipText("Toggle the 'Automatic Port Grouping' feature of the layout algorithms");
253 spJb.addItemListener(new ItemListener() {
254 public void itemStateChanged( final ItemEvent e ) {
255 sharedPorts = ItemEvent.SELECTED == e.getStateChange();
256 }
257 });
258 jtb.add(spJb);
259
260 final ButtonGroup buttonGroup = new ButtonGroup();
261 buttonGroup.add(fpJb);
262 buttonGroup.add(geJb);
263 buttonGroup.add(spJb);
264
265 return jtb;
266 }
267
268
272 protected GraphMLIOHandler createGraphMLIOHandler() {
273 final SidePortLocationModel.Handler modelHandler = new SidePortLocationModel.Handler();
274 final GraphMLIOHandler graphMLIOHandler = super.createGraphMLIOHandler();
275 final Graph2DGraphMLHandler graphMLHandler = graphMLIOHandler.getGraphMLHandler();
276 graphMLHandler.addDeserializationHandler(modelHandler);
277 graphMLHandler.addSerializationHandler(modelHandler);
278 return graphMLIOHandler;
279 }
280
281
289 protected HierarchyManager createHierarchyManager( final Graph2D rootGraph ) {
290 return rootGraph.getHierarchyManager();
291 }
292
293 protected void loadInitialGraph() {
294 PortConfigurations.INSTANCE.getClass();
296
297 loadGraph("resource/NodePortsDemo.graphml");
298 }
299
300
303 protected void configureDefaultGroupNodeRealizers() {
304 Map map = GenericGroupNodeRealizer.createDefaultConfigurationMap();
306
307 GroupNodePainter gnp = new GroupNodePainter(new GroupShapeNodePainter());
308 map.put(GenericNodeRealizer.Painter.class, gnp);
309 map.put(GenericNodeRealizer.ContainsTest.class, gnp);
310 map.put(GenericNodeRealizer.GenericMouseInputEditorProvider.class, gnp);
311 map.put(GenericNodeRealizer.Initializer.class, gnp);
312
313 DefaultGenericAutoBoundsFeature abf = new DefaultGenericAutoBoundsFeature();
314 abf.setConsiderNodeLabelSize(true);
315 map.put(GenericGroupNodeRealizer.GenericAutoBoundsFeature.class, abf);
316 map.put(GenericNodeRealizer.GenericSizeConstraintProvider.class, abf);
317 map.put(GenericNodeRealizer.LabelBoundsChangedHandler.class, abf);
318
319 GenericNodeRealizer.Factory factory = GenericNodeRealizer.getFactory();
320 factory.addConfiguration(CONFIGURATION_GROUP, map);
321
322
323 GenericGroupNodeRealizer gnr = new GenericGroupNodeRealizer();
324
325 gnr.setConfiguration(CONFIGURATION_GROUP);
327
328 gnr.setFillColor(new Color(202,236,255,132));
330 gnr.setLineColor(new Color(102, 102,153,255));
331 NodeLabel label = gnr.getLabel();
332 label.setBackgroundColor(null);
333 label.setTextColor(Color.BLACK);
334 label.setFontSize(15);
335
336
337 DefaultHierarchyGraphFactory hgf = (DefaultHierarchyGraphFactory)
339 getHierarchyManager().getGraphFactory();
340
341 hgf.setProxyNodeRealizerEnabled(true);
342
343 hgf.setDefaultGroupNodeRealizer(gnr.createCopy());
344 hgf.setDefaultFolderNodeRealizer(gnr.createCopy());
345 }
346
347
348
349 private void layoutHierarchically() {
350 final SimplexNodePlacer placer = new SimplexNodePlacer();
351 placer.setBaryCenterModeEnabled(true);
352
353 final IncrementalHierarchicLayouter layouter =
354 new IncrementalHierarchicLayouter();
355 layouter.setLayoutOrientation(LayoutOrientation.LEFT_TO_RIGHT);
356 layouter.setNodePlacer(placer);
357
358 final RoutingStyle ers = new RoutingStyle(RoutingStyle.EDGE_STYLE_POLYLINE);
361 layouter.getEdgeLayoutDescriptor().setRoutingStyle(ers);
362
363 layouter.getEdgeLayoutDescriptor().setMinimumDistance(15);
367
368 layouter.getEdgeLayoutDescriptor().setMinimumFirstSegmentLength(20);
369 layouter.getEdgeLayoutDescriptor().setMinimumLastSegmentLength(40);
370 layouter.setMinimumLayerDistance(60);
371
372 doLayout(layouter, false);
373 }
374
375 private void routeOrthogonally() {
376 doLayout(new EdgeRouter(), true);
377 }
378
379 private void doLayout(
380 final Layouter layouter, final boolean edgeRouting
381 ) {
382 final Graph2DLayoutExecutor executor = new Graph2DLayoutExecutor();
383
384 final NodePortLayoutConfigurator configurator =
385 executor.getNodePortConfigurator();
386 configurator.setAutomaticPortConstraintsEnabled(fixPortsForLayout);
387 if (edgeRouting) {
388 final boolean grouped = groupEdges || sharedPorts;
391 configurator.setAutomaticEdgeGroupsEnabled(grouped);
392 } else {
393 configurator.setAutomaticEdgeGroupsEnabled(groupEdges);
394 configurator.setAutomaticPortGroupsEnabled(sharedPorts);
395 }
396 configurator.setStrongGroupConstraintsEnabled(edgeRouting);
397
398 final NodeMap haloMap = createNodeHaloMap();
399 view.getGraph2D().addDataProvider(NodeHalo.NODE_HALO_DPKEY, haloMap);
400 executor.doLayout(view, layouter);
401 view.getGraph2D().removeDataProvider(NodeHalo.NODE_HALO_DPKEY);
402 }
403
404
412 private NodeMap createNodeHaloMap() {
413 final NodeMap haloMap = Maps.createHashedNodeMap();
414 final Graph2D graph = view.getGraph2D();
415
416 final NodeHalo sharedHalo = NodeHalo.create(10, 10, 10, 10);
423
424 for (NodeCursor nc = graph.nodes(); nc.ok(); nc.next()) {
425 if (graph.getRealizer(nc.node()).portCount() > 0) {
426 haloMap.set(nc.node(), sharedHalo);
427 }
428 }
429 return haloMap;
430 }
431
432
433 public static void main( String[] args ) {
434 EventQueue.invokeLater(new Runnable() {
435 public void run() {
436 Locale.setDefault(Locale.ENGLISH);
437 initLnF();
438 (new NodePortsDemo("resource/nodeportshelp.html")).start();
439 }
440 });
441 }
442
443
444
448 private static interface Processor {
449
456 public void preEdgeStateChange( Edge edge, Node groupNode );
457
458
465 public void postEdgeStateChange( Edge edge, Node groupNode );
466
467
472 public void postNodeStateChange( Node groupNode );
473 }
474
475
482 private static final class MyNodeStateChangeEdgeRouter
483 extends NodeStateChangeEdgeRouter {
484 private final Processor impl;
485
486 MyNodeStateChangeEdgeRouter( final Processor impl ) {
487 this.impl = impl;
488 }
489
490
496 protected void preEdgeStateChange( final Edge edge, final Node groupNode ) {
497 if (impl != null) {
498 impl.preEdgeStateChange(edge, groupNode);
499 }
500
501 super.preEdgeStateChange(edge, groupNode);
502 }
503
504
510 protected void postEdgeStateChange( final Edge edge, final Node groupNode ) {
511 super.postEdgeStateChange(edge, groupNode);
512
513 if (impl != null) {
514 impl.postEdgeStateChange(edge, groupNode);
515 }
516 }
517
518
522 public void postNodeStateChange( final Node groupNode ) {
523 super.postNodeStateChange(groupNode);
524
525 if (impl != null) {
526 impl.postNodeStateChange(groupNode);
527 }
528 }
529 }
530
531
541 private static final class NormalEdgeProcessor implements Processor {
542 private final Map node2edgeState;
543
544 NormalEdgeProcessor() {
545 node2edgeState = new WeakHashMap();
546 }
547
548
549
554 public void preEdgeStateChange( final Edge edge, final Node groupNode ) {
555 if (groupNode.getGraph() instanceof Graph2D) {
556 final Graph2D graph = (Graph2D) groupNode.getGraph();
557 final HierarchyManager hm = graph.getHierarchyManager();
558 if (acceptEdge(hm, edge, groupNode)) {
559 Map edge2state = (Map) node2edgeState.get(groupNode);
560 if (edge2state == null) {
561 edge2state = new WeakHashMap();
562 node2edgeState.put(groupNode, edge2state);
563 }
564
565 if (edge.source() == groupNode) {
566 final NodePort port = NodePort.getSourcePort(graph.getRealizer(edge));
567 if (port != null &&
568 matches(port.getRealizer(), graph.getRealizer(groupNode))) {
569 EdgeState state = (EdgeState) edge2state.get(edge);
570 if (state == null) {
571 state = new EdgeState();
572 edge2state.put(edge, state);
573 }
574 state.sourcePort = port;
575 }
576 }
577 if (edge.target() == groupNode) {
578 final NodePort port = NodePort.getTargetPort(graph.getRealizer(edge));
579 if (port != null &&
580 matches(port.getRealizer(), graph.getRealizer(groupNode))) {
581 EdgeState state = (EdgeState) edge2state.get(edge);
582 if (state == null) {
583 state = new EdgeState();
584 edge2state.put(edge, state);
585 }
586 state.targetPort = port;
587 }
588 }
589 }
590 }
591 }
592
593
601 public void postEdgeStateChange( final Edge edge, final Node groupNode ) {
602 if (groupNode.getGraph() instanceof Graph2D) {
603 final Graph2D graph = (Graph2D) groupNode.getGraph();
604 final HierarchyManager hm = graph.getHierarchyManager();
605 if (acceptEdge(hm, edge, groupNode)) {
606 final Map edge2state = (Map) node2edgeState.get(groupNode);
607 final EdgeState state = (EdgeState) edge2state.get(edge);
608 if (state != null) {
609 if (edge.source() == groupNode) {
610 remapPort(
611 graph.getRealizer(groupNode), graph.getRealizer(edge),
612 state.sourcePort,
613 true);
614 }
615 if (edge.target() == groupNode) {
616 remapPort(
617 graph.getRealizer(groupNode), graph.getRealizer(edge),
618 state.targetPort,
619 false);
620 }
621 }
622 }
623 }
624 }
625
626
631 public void postNodeStateChange( final Node groupNode ) {
632 node2edgeState.remove(groupNode);
633 }
634
635
636
645 void remapPort(
646 final NodeRealizer nr,
647 final EdgeRealizer er,
648 final NodePort port,
649 final boolean source
650 ) {
651 if (port != null) {
652 final NodeRealizer oldNr = port.getRealizer();
653 if (!matches(oldNr, nr) &&
654 oldNr != null &&
655 oldNr.portCount() == nr.portCount()) {
656 bindPort(nr.getPort(indexOf(port)), er, source);
657 }
658 }
659 }
660
661
662
669 private static void bindPort(
670 final NodePort port,
671 final EdgeRealizer er,
672 final boolean source
673 ) {
674 if (source) {
675 NodePort.bindSourcePort(port, er);
676 } else {
677 NodePort.bindTargetPort(port, er);
678 }
679 }
680
681
690 private static boolean acceptEdge(
691 final HierarchyManager hm,
692 final Edge edge,
693 final Node groupNode
694 ) {
695 if (hm != null && hm.isFolderNode(groupNode) && hm.isInterEdge(edge)) {
696 return hm.getRealSource(edge) == groupNode ||
697 hm.getRealTarget(edge) == groupNode;
698 } else {
699 return edge.source() == groupNode || edge.target() == groupNode;
700 }
701 }
702
703
711 private static int indexOf( final NodePort port ) {
712 final NodeRealizer owner = port.getRealizer();
713 if (owner != null) {
714 for (int i = 0, n = owner.portCount(); i < n; ++i) {
715 if (owner.getPort(i) == port) {
716 return i;
717 }
718 }
719 }
720 return -1;
721 }
722
723
732 private static boolean matches(
733 final NodeRealizer portNr,
734 final NodeRealizer otherNr
735 ) {
736 return portNr == otherNr ||
737 (otherNr instanceof ProxyShapeNodeRealizer &&
738 portNr == ((ProxyShapeNodeRealizer) otherNr).getRealizerDelegate());
739 }
740
741
742
745 private static final class EdgeState {
746 NodePort sourcePort;
747 NodePort targetPort;
748 }
749 }
750
751
758 private static final class InterEdgeProcessor implements Processor {
759 private final Processor impl;
760
761 InterEdgeProcessor( final Processor impl ) {
762 this.impl = impl;
763 }
764
765 public void preEdgeStateChange( final Edge edge, final Node groupNode ) {
766 if (impl != null) {
767 impl.preEdgeStateChange(edge, groupNode);
768 }
769 }
770
771
779 public void postEdgeStateChange( final Edge edge, final Node groupNode ) {
780 if (impl != null) {
781 impl.postEdgeStateChange(edge, groupNode);
782 }
783
784 if (groupNode.getGraph() instanceof Graph2D) {
785 final Graph2D graph = (Graph2D) groupNode.getGraph();
786 final HierarchyManager hm = graph.getHierarchyManager();
787 if (hm != null && hm.isFolderNode(groupNode) && hm.isInterEdge(edge)) {
788 if (edge.source() == groupNode && hm.getRealSource(edge) != groupNode) {
789 remapPort(
790 graph.getRealizer(groupNode),
791 graph.getRealizer(edge),
792 true);
793 }
794 if (edge.target() == groupNode && hm.getRealTarget(edge) != groupNode) {
795 remapPort(
796 graph.getRealizer(groupNode),
797 graph.getRealizer(edge),
798 false);
799 }
800 }
801 }
802 }
803
804 public void postNodeStateChange( final Node groupNode ) {
805 if (impl != null) {
806 impl.postNodeStateChange(groupNode);
807 }
808 }
809
810
818 protected void remapPort(
819 final NodeRealizer nr,
820 final EdgeRealizer er,
821 final boolean source
822 ) {
823 if (nr.portCount() > 0) {
824 bindPort(nr.getPort(0), er, source);
825 }
826 }
827
828
829
836 private static void bindPort(
837 final NodePort port,
838 final EdgeRealizer er,
839 final boolean source
840 ) {
841 if (source) {
842 NodePort.bindSourcePort(port, er);
843 } else {
844 NodePort.bindTargetPort(port, er);
845 }
846 }
847 }
848
849
856 private static final class GroupShapeNodePainter extends ShapeNodePainter {
857 private static final Color BACKGROUND = new Color(153, 204, 255, 255);
858
859 GroupShapeNodePainter() {
860 super(ROUND_RECT);
861 }
862
863 protected void paintFilledShape(
864 final NodeRealizer context,
865 final Graphics2D graphics,
866 final Shape shape
867 ) {
868 super.paintFilledShape(context, graphics, shape);
869
870 if (context.labelCount() > 0) {
871 final Shape oldClip = graphics.getClip();
872 final Color oldColor = graphics.getColor();
873
874 final Rectangle2D cb = oldClip.getBounds2D();
875 final YRectangle r = context.getLabel().getBox();
876 graphics.clip(new Rectangle2D.Double(cb.getX(), r.getY(), cb.getWidth(), r.getHeight()));
877 graphics.setColor(BACKGROUND);
878 graphics.fill(shape);
879
880 graphics.setColor(oldColor);
881 graphics.setClip(oldClip);
882 }
883 }
884 }
885 }
886