1
28 package demo.view.isometry;
29
30 import demo.view.DemoBase;
31 import org.w3c.dom.Element;
32 import y.base.DataProvider;
33 import y.base.Edge;
34 import y.base.Node;
35 import y.base.NodeList;
36 import y.geom.YPoint;
37 import y.io.GraphMLIOHandler;
38 import y.io.IOHandler;
39 import y.io.graphml.NamespaceConstants;
40 import y.io.graphml.graph2d.EdgeLabelDeserializer;
41 import y.io.graphml.graph2d.EdgeLabelSerializer;
42 import y.io.graphml.graph2d.Graph2DGraphMLHandler;
43 import y.io.graphml.graph2d.NodeLabelDeserializer;
44 import y.io.graphml.graph2d.NodeLabelSerializer;
45 import y.io.graphml.input.DeserializationEvent;
46 import y.io.graphml.input.DeserializationHandler;
47 import y.io.graphml.input.GraphMLParseContext;
48 import y.io.graphml.input.GraphMLParseException;
49 import y.io.graphml.input.XPathUtils;
50 import y.io.graphml.output.GraphMLWriteContext;
51 import y.io.graphml.output.GraphMLWriteException;
52 import y.io.graphml.output.SerializationEvent;
53 import y.io.graphml.output.SerializationHandler;
54 import y.io.graphml.output.XmlWriter;
55 import y.layout.FixNodeLayoutStage;
56 import y.layout.LabelLayoutTranslator;
57 import y.layout.LayoutGraph;
58 import y.layout.LayoutTool;
59 import y.layout.Layouter;
60 import y.layout.NodeLayout;
61 import y.layout.hierarchic.IncrementalHierarchicLayouter;
62 import y.layout.orthogonal.OrthogonalGroupLayouter;
63 import y.util.D;
64 import y.util.DataProviderAdapter;
65 import y.view.DefaultBackgroundRenderer;
66 import y.view.DefaultOrderRenderer;
67 import y.view.EdgeLabel;
68 import y.view.EdgeRealizer;
69 import y.view.EditMode;
70 import y.view.GenericNodeRealizer;
71 import y.view.Graph2D;
72 import y.view.Graph2DLayoutExecutor;
73 import y.view.Graph2DTraversal;
74 import y.view.Graph2DView;
75 import y.view.Graph2DViewActions;
76 import y.view.HitInfo;
77 import y.view.NavigationMode;
78 import y.view.NodeLabel;
79 import y.view.NodeRealizer;
80 import y.view.NodeStateChangeHandler;
81 import y.view.ProxyShapeNodeRealizer;
82 import y.view.ViewMode;
83 import y.view.YLabel;
84 import y.view.YRenderingHints;
85 import y.view.hierarchy.GenericGroupNodeRealizer;
86 import y.view.hierarchy.HierarchyManager;
87
88 import javax.swing.AbstractAction;
89 import javax.swing.Action;
90 import javax.swing.JToolBar;
91 import java.awt.Color;
92 import java.awt.Cursor;
93 import java.awt.EventQueue;
94 import java.awt.GradientPaint;
95 import java.awt.Graphics2D;
96 import java.awt.Paint;
97 import java.awt.Rectangle;
98 import java.awt.RenderingHints;
99 import java.awt.event.ActionEvent;
100 import java.awt.geom.AffineTransform;
101 import java.beans.PropertyChangeEvent;
102 import java.beans.PropertyChangeListener;
103 import java.io.IOException;
104 import java.net.URL;
105 import java.util.HashMap;
106 import java.util.Iterator;
107 import java.util.Locale;
108 import java.util.Map;
109
110
126 public class IsometryDemo extends DemoBase {
127 private static final double PAINT_DETAIL_THRESHOLD = 0.4;
128 private static final double MAX_ZOOM = 4.0;
129 private static final double MIN_ZOOM = 0.05;
130
131 private static final int TYPE_INCREMENTAL_HIERARCHIC_LAYOUT = 0;
132 private static final int TYPE_ORTHOGONAL_GROUP_LAYOUT = 1;
133
134 private int layoutType;
135 private boolean fractionMetricsEnabled;
136
137 private IncrementalHierarchicLayouter ihl;
138 private OrthogonalGroupLayouter ogl;
139
140 public IsometryDemo() {
141 this(null);
142 }
143
144 public IsometryDemo(String helpFile) {
145 addHelpPane(helpFile);
146
147 final Graph2D graph = view.getGraph2D();
148 new HierarchyManager(graph);
149
150 graph.addDataProvider(
152 IsometryTransformationLayoutStage.TRANSFORMATION_DATA_DPKEY,
153 new TransformationDataProvider(graph));
154
155 view.getRenderingHints().put(YRenderingHints.KEY_EDGE_LABEL_PAINTING, YRenderingHints.VALUE_EDGE_LABEL_PAINTING_OFF);
158 final IsometryGraphTraversal graphTraversal = new IsometryGraphTraversal();
159 view.setGraph2DRenderer(new DefaultOrderRenderer(graphTraversal, graphTraversal) {
160 public void paint(Graphics2D gfx, Graph2D graph) {
161 Rectangle clip = gfx.getClipBounds();
162 if (clip == null) {
163 clip = graph.getBoundingBox();
164 }
165
166 final Graph2DTraversal paintOrder = getPaintOrder();
167 final int types = Graph2DTraversal.NODES | Graph2DTraversal.EDGES | Graph2DTraversal.EDGE_LABELS;
168 for (Iterator it = paintOrder.firstToLast(graph, types); it.hasNext();) {
169 final Object element = it.next();
170 if (element instanceof Edge) {
171 final EdgeRealizer er = graph.getRealizer((Edge) element);
172 if (intersects(er, clip)) {
173 er.paint(gfx);
174 }
175 } else if (element instanceof Node) {
176 final NodeRealizer nr = graph.getRealizer((Node) element);
177 if (intersects(nr, clip)) {
178 nr.paint(gfx);
179 }
180 } else if (element instanceof EdgeLabel) {
181 final EdgeLabel label = (EdgeLabel) element;
182 if (label.intersects(clip.getX(), clip.getY(), clip.getWidth(), clip.getHeight())) {
183 label.paint(gfx);
184 }
185 }
186 }
187 }
188 });
189
190 IsometryRealizerFactory.initializeConfigurations();
191 configureLayouter();
192 configureBackgroundRenderer();
193 configureLabelRendering();
194 configureZoomThreshold();
195
196 loadGraph("resource/iso_sample_1.graphml");
197 }
198
199
203 private void configureLayouter() {
204 ihl = new IncrementalHierarchicLayouter();
205 ihl.setOrthogonallyRouted(true);
206 ihl.setNodeToEdgeDistance(50);
207 ihl.setMinimumLayerDistance(40);
208 ihl.setLabelLayouterEnabled(false);
209 ihl.setIntegratedEdgeLabelingEnabled(true);
210 ihl.setConsiderNodeLabelsEnabled(true);
211
212 final LabelLayoutTranslator llt = new LabelLayoutTranslator() {
215 public void doLayout(LayoutGraph graph) {
216 final Layouter coreLayouter = getCoreLayouter();
217 if (coreLayouter != null) {
218 coreLayouter.doLayout(graph);
219 }
220 }
221 };
222
223 ogl = new OrthogonalGroupLayouter();
224 ogl.setIntegratedEdgeLabelingEnabled(true);
225 ogl.setConsiderNodeLabelsEnabled(true);
226 ogl.setLabelLayouter(llt);
227 }
228
229
232 private void configureBackgroundRenderer() {
233 final DefaultBackgroundRenderer bgRenderer = new FoggyFrameBackgroundRenderer(view);
234 bgRenderer.setImageResource(getResource("resource/grid.png"));
235 bgRenderer.setMode(DefaultBackgroundRenderer.CENTERED);
236 bgRenderer.setColor(Color.WHITE);
237 view.setBackgroundRenderer(bgRenderer);
238 }
239
240
244 private void configureLabelRendering() {
245 fractionMetricsEnabled = YLabel.isFractionMetricsForSizeCalculationEnabled();
246 YLabel.setFractionMetricsForSizeCalculationEnabled(true);
247 view.getRenderingHints().put(
248 RenderingHints.KEY_FRACTIONALMETRICS,
249 RenderingHints.VALUE_FRACTIONALMETRICS_ON);
250 }
251
252
256 public void dispose() {
257 YLabel.setFractionMetricsForSizeCalculationEnabled(fractionMetricsEnabled);
258 }
259
260
263 private void configureZoomThreshold() {
264 view.setPaintDetailThreshold(PAINT_DETAIL_THRESHOLD);
266 view.getCanvasComponent().addPropertyChangeListener(
268 new PropertyChangeListener() {
269 public void propertyChange(PropertyChangeEvent evt) {
270 if ("Zoom".equals(evt.getPropertyName())) {
271 final double zoom = ((Double) evt.getNewValue()).doubleValue();
272 if (zoom > MAX_ZOOM) {
273 view.setZoom(MAX_ZOOM);
274 } else if (zoom < MIN_ZOOM) {
275 view.setZoom(MIN_ZOOM);
276 }
277 }
278 }
279 });
280 }
281
282
285 protected void loadGraph(URL resource) {
286 if (resource == null) {
287 String message = "Resource \"" + resource + "\" not found in classpath";
288 D.showError(message);
289 throw new RuntimeException(message);
290 }
291
292 try {
293 IOHandler ioh = createGraphMLIOHandler();
294 view.getGraph2D().clear();
295 ioh.read(view.getGraph2D(), resource);
296 } catch (IOException e) {
297 String message = "Unexpected error while loading resource \"" + resource + "\" due to " + e.getMessage();
298 D.bug(message);
299 throw new RuntimeException(message, e);
300 }
301
302 view.getGraph2D().setURL(resource);
303
304 IsometryRealizerFactory.applyIsometryRealizerDefaults(view.getGraph2D());
306
307 runLayout(false);
309 }
310
311
315 protected JToolBar createToolBar() {
316 final JToolBar toolBar = super.createToolBar();
317 toolBar.addSeparator();
318 toolBar.add(createActionControl(createLayoutAction("Hierarchic", TYPE_INCREMENTAL_HIERARCHIC_LAYOUT), true));
319 toolBar.add(createActionControl(createLayoutAction("Orthogonal", TYPE_ORTHOGONAL_GROUP_LAYOUT), true));
320 return toolBar;
321 }
322
323
326 protected boolean isDeletionEnabled() {
327 return false;
328 }
329
330
333 protected boolean isUndoRedoEnabled() {
334 return false;
335 }
336
337
340 protected boolean isClipboardEnabled() {
341 return false;
342 }
343
344
347 private Action createLayoutAction(final String text, final int type) {
348 final AbstractAction action = new AbstractAction(text) {
349 public void actionPerformed(ActionEvent e) {
350 layoutType = type;
351 runLayout(false);
352 }
353 };
354 action.putValue(Action.SMALL_ICON, SHARED_LAYOUT_ICON);
355 return action;
356 }
357
358
362 protected GraphMLIOHandler createGraphMLIOHandler() {
363 final GraphMLIOHandler graphMLIOHandler = super.createGraphMLIOHandler();
364 final Graph2DGraphMLHandler graphMLHandler = graphMLIOHandler.getGraphMLHandler();
365 final IsometryDataIOHandler dataHandler = new IsometryDataIOHandler();
366 graphMLHandler.addSerializationHandler(dataHandler);
367 graphMLHandler.addDeserializationHandler(dataHandler);
368 graphMLHandler.addSerializationHandler(new IsometryEdgeLabelSerializer());
369 graphMLHandler.addDeserializationHandler(new IsometryEdgeLabelDeserializer());
370 graphMLHandler.addSerializationHandler(new IsometryNodeLabelSerializer());
371 graphMLHandler.addDeserializationHandler(new IsometryNodeLabelDeserializer());
372 return graphMLIOHandler;
373 }
374
375
380 protected EditMode createEditMode() {
381 return null;
382 }
383
384
387 protected void registerViewModes() {
388 super.registerViewModes();
389 view.addViewMode(new NavigationMode());
390 view.addViewMode(new GroupingViewMode());
391 }
392
393
397 private void runLayout(final boolean fromSketch) {
398 final Graph2DLayoutExecutor executor = new Graph2DLayoutExecutor();
399 executor.getLayoutMorpher().setKeepZoomFactor(fromSketch);
400
401 if (layoutType == TYPE_INCREMENTAL_HIERARCHIC_LAYOUT) {
402 if (fromSketch) {
403 ihl.setLayoutMode(IncrementalHierarchicLayouter.LAYOUT_MODE_INCREMENTAL);
404 executor.doLayout(view, new FixGroupStateIconLayoutStage(new IsometryTransformationLayoutStage(ihl, fromSketch)));
405 } else {
406 ihl.setLayoutMode(IncrementalHierarchicLayouter.LAYOUT_MODE_FROM_SCRATCH);
407 executor.doLayout(view, new IsometryTransformationLayoutStage(ihl, fromSketch));
408 }
409 } else if (layoutType == TYPE_ORTHOGONAL_GROUP_LAYOUT) {
410 if (fromSketch) {
411 executor.doLayout(view, new FixGroupStateIconLayoutStage(new IsometryTransformationLayoutStage(ogl, fromSketch)));
412 } else {
413 executor.doLayout(view, new IsometryTransformationLayoutStage(ogl, fromSketch));
414 }
415 }
416 }
417
418
421 public static void main(final String[] args) {
422 EventQueue.invokeLater(new Runnable() {
423 public void run() {
424 Locale.setDefault(Locale.ENGLISH);
425 initLnF();
426 (new IsometryDemo("resource/iso_help.html")).start("Isometry Demo");
427 }
428 });
429 }
430
431
435 private static class FoggyFrameBackgroundRenderer extends DefaultBackgroundRenderer {
436
437 private static final Color COLOR_BLANK = new Color(255, 255, 255, 0);
438
439 private Color bgColor;
440
441 public FoggyFrameBackgroundRenderer(Graph2DView view) {
442 super(view);
443 bgColor = Color.WHITE;
444 }
445
446 public void paint(Graphics2D graphics, int x, int y, int w, int h) {
447 super.paint(graphics, x, y, w, h);
448
449 paintFoggyFrame(graphics);
450 }
451
452 private void paintFoggyFrame(Graphics2D graphics) {
453 final Paint oldPaint = graphics.getPaint();
454 final AffineTransform oldTransform = graphics.getTransform();
455
456 undoWorldTransform(graphics);
457
458 final float viewX = (float) 0;
459 final float viewY = (float) 0;
460 final float viewW = (float) view.getWidth();
461 final float viewH = (float) view.getHeight();
462
463 final float halfWidth = viewW * 0.5f;
464 final float halfHeight = viewH * 0.5f;
465 graphics.setPaint(
466 new GradientPaint(viewX + halfWidth, viewY, bgColor, viewX + halfWidth, viewY + viewH * 0.2f, COLOR_BLANK));
467 graphics.fillRect((int) viewX, (int) viewY, (int) viewW, (int) (viewH * 0.2));
468 graphics.setPaint(
469 new GradientPaint(viewX + halfWidth, viewY + viewH * 0.8f, COLOR_BLANK, viewX + halfWidth, viewY + viewH,
470 bgColor));
471 graphics.fillRect((int) viewX, (int) (viewY + viewH * 0.8), (int) viewW, (int) (viewH * 0.2));
472 graphics.setPaint(
473 new GradientPaint(viewX, viewY + halfHeight, bgColor, viewX + viewW * 0.2f, viewY + halfHeight, COLOR_BLANK));
474 graphics.fillRect((int) viewX, (int) viewY, (int) (viewW * 0.2), (int) viewH);
475 graphics.setPaint(
476 new GradientPaint(viewX + viewW * 0.8f, viewY + halfHeight, COLOR_BLANK, viewX + viewW, viewY + halfHeight,
477 bgColor));
478 graphics.fillRect((int) (viewX + viewW * 0.8), (int) viewY, (int) (viewW * 0.2), (int) viewH);
479
480 graphics.setPaint(oldPaint);
481 graphics.setTransform(oldTransform);
482 }
483 }
484
485
488 private static class TransformationDataProvider extends DataProviderAdapter {
489 private final Graph2D graph;
490
491 public TransformationDataProvider(final Graph2D graph) {
492 this.graph = graph;
493 }
494
495 public Object get(final Object dataHolder) {
496 if (dataHolder instanceof Node) {
497 NodeRealizer realizer = graph.getRealizer((Node) dataHolder);
498 if (realizer instanceof ProxyShapeNodeRealizer) {
499 realizer = ((ProxyShapeNodeRealizer) realizer).getRealizerDelegate();
500 }
501 if (realizer instanceof GenericNodeRealizer) {
502 return ((GenericNodeRealizer) realizer).getUserData();
503 }
504 } else if (dataHolder instanceof EdgeLabel) {
505 return ((EdgeLabel) dataHolder).getUserData();
506 }
507 return null;
508 }
509 }
510
511
514 private class IsometryDataIOHandler implements SerializationHandler, DeserializationHandler {
515 private static final String ELEMENT_NAME = "IsometryData";
516 private static final String ELEMENT_WIDTH = "width";
517 private static final String ELEMENT_HEIGHT = "height";
518 private static final String ELEMENT_DEPTH = "depth";
519 private static final String ELEMENT_HORIZONTAL = "horizontal";
520
521 public void onHandleSerialization(SerializationEvent event) throws GraphMLWriteException {
522 final Object item = event.getItem();
523 if (item instanceof IsometryData) {
524 IsometryData isometryData = (IsometryData) item;
525 final XmlWriter writer = event.getWriter();
526 writer.writeStartElement(ELEMENT_NAME, NamespaceConstants.YFILES_JAVA_NS);
527 writer.writeAttribute(ELEMENT_WIDTH, isometryData.getWidth());
528 writer.writeAttribute(ELEMENT_HEIGHT, isometryData.getHeight());
529 writer.writeAttribute(ELEMENT_DEPTH, isometryData.getDepth());
530 writer.writeAttribute(ELEMENT_HORIZONTAL, isometryData.isHorizontal());
531 writer.writeEndElement();
532 event.setHandled(true);
533 }
534 }
535
536 public void onHandleDeserialization(DeserializationEvent event) throws GraphMLParseException {
537 final org.w3c.dom.Node xmlNode = event.getXmlNode();
538 if (xmlNode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE
539 && NamespaceConstants.YFILES_JAVA_NS.equals(xmlNode.getNamespaceURI())
540 && ELEMENT_NAME.equals(xmlNode.getLocalName())) {
541 final Element element = (Element) xmlNode;
542 String attribute = element.getAttribute(ELEMENT_WIDTH);
543 final double width = Double.parseDouble(attribute);
544 attribute = element.getAttribute(ELEMENT_HEIGHT);
545 final double height = Double.parseDouble(attribute);
546 attribute = element.getAttribute(ELEMENT_DEPTH);
547 final double depth = Double.parseDouble(attribute);
548 attribute = element.getAttribute(ELEMENT_HORIZONTAL);
549 final boolean horizontal = Boolean.getBoolean(attribute);
550 event.setResult(new IsometryData(width, depth, height, horizontal));
551 }
552 }
553 }
554
555
559 private static class IsometryEdgeLabelSerializer extends EdgeLabelSerializer {
560 protected void serializeContent(EdgeLabel label, XmlWriter writer, GraphMLWriteContext context) throws
561 GraphMLWriteException {
562 super.serializeContent(label, writer, context);
563 if (label.getUserData() != null) {
564 writer.writeStartElement("UserData", NamespaceConstants.YFILES_JAVA_NS);
565 context.serialize(label.getUserData());
566 writer.writeEndElement();
567 }
568 }
569 }
570
571
575 private static class IsometryEdgeLabelDeserializer extends EdgeLabelDeserializer {
576 protected void parseEdgeLabel(GraphMLParseContext context, org.w3c.dom.Node root, EdgeLabel label) throws
577 GraphMLParseException {
578 super.parseEdgeLabel(context, root, label);
579 final org.w3c.dom.Node userDataNode = XPathUtils.selectFirstChildElement(root, "UserData",
580 NamespaceConstants.YFILES_JAVA_NS);
581 Object userData = null;
582 if (userDataNode != null) {
583 final org.w3c.dom.Node isoData = XPathUtils.selectFirstChildElement(userDataNode, "IsometryData",
584 NamespaceConstants.YFILES_JAVA_NS);
585 if (isoData != null) {
586 userData = context.deserialize(isoData);
587 }
588 }
589 if (userData != null) {
590 label.setUserData(userData);
591 }
592 }
593 }
594
595
599 private static class IsometryNodeLabelSerializer extends NodeLabelSerializer {
600 protected void serializeContent(NodeLabel label, XmlWriter writer, GraphMLWriteContext context) throws
601 GraphMLWriteException {
602 super.serializeContent(label, writer, context);
603 if (label.getUserData() != null) {
604 writer.writeStartElement("UserData", NamespaceConstants.YFILES_JAVA_NS);
605 context.serialize(label.getUserData());
606 writer.writeEndElement();
607 }
608 }
609 }
610
611
615 private static class IsometryNodeLabelDeserializer extends NodeLabelDeserializer {
616 protected void parseNodeLabel(GraphMLParseContext context, org.w3c.dom.Node root, NodeLabel label) throws
617 GraphMLParseException {
618 super.parseNodeLabel(context, root, label);
619 final org.w3c.dom.Node userDataNode = XPathUtils.selectFirstChildElement(root, "UserData",
620 NamespaceConstants.YFILES_JAVA_NS);
621 Object userData = null;
622 if (userDataNode != null) {
623 final org.w3c.dom.Node isoData = XPathUtils.selectFirstChildElement(userDataNode, "IsometryData",
624 NamespaceConstants.YFILES_JAVA_NS);
625 if (isoData != null) {
626 userData = context.deserialize(isoData);
627 }
628 }
629 if (userData != null) {
630 label.setUserData(userData);
631 }
632 }
633 }
634
635
638 private class GroupingViewMode extends ViewMode {
639 public void mouseMoved(double x, double y) {
640 if (hitsGroupStateIcon(x, y)) {
641 view.setViewCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
642 } else {
643 view.setViewCursor(Cursor.getDefaultCursor());
644 }
645 }
646
647 public void mouseClicked(double x, double y) {
648 if (hitsGroupStateIcon(x, y)) {
649 final HitInfo hit = getHitInfo(x, y);
650 final Node hitNode = hit.getHitNode();
651 final HierarchyManager hm = view.getGraph2D().getHierarchyManager();
652 view.getGraph2D().addDataProvider(FixNodeLayoutStage.FIXED_NODE_DPKEY, new DataProviderAdapter() {
653 public boolean getBool(Object dataHolder) {
654 return dataHolder instanceof Node && hitNode == dataHolder;
655 }
656 });
657 if (hm.isFolderNode(hitNode)) {
658 openFolder(hitNode);
659 } else {
660 closeGroup(hitNode);
661 }
662
663 runLayout(true);
665
666 view.getGraph2D().removeDataProvider(FixNodeLayoutStage.FIXED_NODE_DPKEY);
667 }
668 }
669
670
673 private boolean hitsGroupStateIcon(double x, double y) {
674 final HitInfo hit = getHitInfo(x, y);
675 if (hit.hasHitNodes()) {
676 final Node hitNode = hit.getHitNode();
677 NodeRealizer realizer = view.getGraph2D().getRealizer(hitNode);
678 if (realizer instanceof ProxyShapeNodeRealizer) {
679 ProxyShapeNodeRealizer proxy = (ProxyShapeNodeRealizer) realizer;
680 realizer = proxy.getRealizerDelegate();
681 }
682 if (realizer instanceof GenericGroupNodeRealizer
683 && IsometryGroupPainter.hitsGroupStateIcon(realizer, x, y)) {
684 return true;
685 }
686 }
687 return false;
688 }
689
690
694 protected void closeGroup(final Node node) {
695 Graph2DViewActions.CloseGroupsAction helper = new Graph2DViewActions.CloseGroupsAction(view) {
696 protected boolean acceptNode(final Graph2D graph, final Node groupNode) {
697 return groupNode == node;
698 }
699 };
700 helper.setNodeStateChangeHandler(new GroupNodeStateChangeHandler(helper.getNodeStateChangeHandler()));
701 helper.closeGroups(view);
702 }
703
704
708 protected void openFolder(final Node node) {
709 Graph2DViewActions.OpenFoldersAction helper = new Graph2DViewActions.OpenFoldersAction(view) {
710 protected boolean acceptNode(final Graph2D graph, final Node groupNode) {
711 return groupNode == node;
712 }
713 };
714 helper.setNodeStateChangeHandler(new GroupNodeStateChangeHandler(helper.getNodeStateChangeHandler()));
715 helper.openFolders(view);
716 }
717 }
718
719
725 private static final class GroupNodeStateChangeHandler implements NodeStateChangeHandler {
726 private final NodeStateChangeHandler handler;
727 private final Map state;
728
729 GroupNodeStateChangeHandler(final NodeStateChangeHandler handler) {
730 this.handler = handler;
731 state = new HashMap();
732 }
733
734
737 public void preNodeStateChange(final Node node) {
738 state.put(node, calculateFixPoint(node));
739
740 if (handler != null) {
741 handler.postNodeStateChange(node);
742 }
743 }
744
745 private Object calculateFixPoint(final Node node) {
746 final NodeRealizer realizer = ((Graph2D) node.getGraph()).getRealizer(node);
747 final double[] corners = new double[16];
748 final IsometryData isometryData = IsometryRealizerFactory.getIsometryData(realizer);
749 isometryData.calculateCorners(corners);
750 IsometryData.moveTo(realizer.getX(), realizer.getY(), corners);
751
752 return new YPoint(corners[IsometryData.C3_X], corners[IsometryData.C3_Y]);
753 }
754
755
758 public void postNodeStateChange(final Node node) {
759 final YPoint fixPoint = (YPoint) state.remove(node);
760 if (fixPoint != null) {
761 restoreFixPoint(node, fixPoint);
762 }
763
764 if (handler != null) {
765 handler.postNodeStateChange(node);
766 }
767 }
768
769 private void restoreFixPoint(final Node node, final YPoint fixPoint) {
770 final Graph2D graph = (Graph2D) node.getGraph();
771 NodeRealizer realizer = graph.getRealizer(node);
772 if (realizer instanceof ProxyShapeNodeRealizer) {
773 realizer = ((ProxyShapeNodeRealizer) realizer).getRealizerDelegate();
774 }
775 final double[] corners = new double[16];
776 final IsometryData isometryData = IsometryRealizerFactory.getIsometryData(realizer);
777 isometryData.calculateCorners(corners);
778 IsometryData.moveTo(realizer.getX(), realizer.getY(), corners);
779
780 final double newCornerX = corners[IsometryData.C3_X];
781 final double newCornerY = corners[IsometryData.C3_Y];
782
783 final double dx = fixPoint.getX() - newCornerX;
784 final double dy = fixPoint.getY() - newCornerY;
785
786 final HierarchyManager hm = graph.getHierarchyManager();
787 if (hm.isGroupNode(node)) {
788 final NodeList subGraph = new NodeList(hm.getChildren(node));
789 subGraph.add(node);
790 LayoutTool.moveSubgraph(graph, subGraph.nodes(), dx, dy);
791 } else {
792 LayoutTool.moveNode(graph, node, dx, dy);
793 }
794 }
795 }
796
797
802 private class FixGroupStateIconLayoutStage extends FixNodeLayoutStage {
803 private FixGroupStateIconLayoutStage(final Layouter core) {
804 super(core);
805 }
806
807
811 protected YPoint calculateFixPoint(final LayoutGraph graph, final NodeList fixed) {
812 final Node node = fixed.firstNode();
813 final DataProvider provider = graph.getDataProvider(IsometryTransformationLayoutStage.TRANSFORMATION_DATA_DPKEY);
814 final IsometryData isometryData = (IsometryData) provider.get(node);
815
816 final double[] corners = new double[16];
817 isometryData.calculateCorners(corners);
818 final NodeLayout nodeLayout = graph.getNodeLayout(node);
819 IsometryData.moveTo(nodeLayout.getX(), nodeLayout.getY(), corners);
820 return new YPoint(corners[IsometryData.C3_X], corners[IsometryData.C3_Y]);
821 }
822 }
823 }
824