]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/LocalDelegateNode.java
G2DParentNode handles "undefined" child bounds separately
[simantics/platform.git] / bundles / org.simantics.scenegraph / src / org / simantics / scenegraph / g2d / nodes / LocalDelegateNode.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.scenegraph.g2d.nodes;
13
14 import java.awt.Graphics2D;
15 import java.awt.geom.AffineTransform;
16 import java.awt.geom.Rectangle2D;
17
18 import org.simantics.scenegraph.ILookupService;
19 import org.simantics.scenegraph.g2d.IG2DNode;
20
21 /**
22  * A node that delegates {@link #render(Graphics2D)} and
23  * {@link #getBoundsInLocal()} methods to a delegate {@link IG2DNode} it
24  * contains.
25  * 
26  * <p>
27  * <b>CAUTION:</b> <em>this node must be used with extreme care</em>! It can be
28  * used to generate cyclic scene graphs which may cause rendering to crash due
29  * to infinite recursion and in any case rendering will not work as intended.
30  * E.g. a scene graph could have a {@link NavigationNode} under its root node
31  * and under which a <code>LinkNode</code> could link back to the navigation
32  * node, which would cause everything to be rendered twice and with double
33  * transformations. As a safety measure against cyclic cases, this node contains
34  * state that prevents it from being invoked recursively.
35  * 
36  * <p>
37  * <b>CAVEAT 1:</b> Nodes with internal state that is updated during rendering.
38  * Such nodes should not be used with this.
39  * 
40  * <p>
41  * <b>CAVEAT 2:</b> Only intended for local rendering. Will not work with remote
42  * rendering. For remote support, use {@link ILookupService} and
43  * {@link LinkNode} instead.
44  * 
45  * @author Tuukka Lehtonen
46  * 
47  * @see LinkNode
48  * @see ILookupService
49  */
50 public class LocalDelegateNode extends StateMaskNode {
51
52     private static final long   serialVersionUID = -7465071303188585400L;
53
54     /**
55      * The delegate node.
56      */
57     IG2DNode delegate;
58
59     /**
60      * @param delegate
61      */
62     public void setDelegate(IG2DNode delegate) {
63         this.delegate = delegate;
64     }
65
66     public IG2DNode getDelegate() {
67         return delegate;
68     }
69
70     @Override
71     public void render(Graphics2D g2d) {
72         // Safety against cyclic cases.
73         if (hasFlags(IN_RENDER))
74             return;
75
76         if (delegate == null)
77             return;
78
79         setFlags(IN_RENDER);
80         AffineTransform oldTransform = null;
81         if (transform != null && !transform.isIdentity()) {
82             g2d.transform(transform);
83             oldTransform = g2d.getTransform();
84         }
85         try {
86             delegate.render(g2d);
87         } finally {
88             if (oldTransform != null)
89                 g2d.setTransform(oldTransform);
90             clearFlags(IN_RENDER);
91         }
92     }
93
94     @Override
95     public Rectangle2D getBoundsInLocal() {
96         // Safety against cyclic cases.
97         if (hasFlags(IN_GET_BOUNDS))
98             return new Rectangle2D.Double();
99
100         if (delegate == null)
101             return new Rectangle2D.Double();
102
103         setFlags(IN_GET_BOUNDS);
104         try {
105             Rectangle2D bounds = delegate.getBoundsInLocal();
106             if (transform != null && !transform.isIdentity())
107                 bounds = transform.createTransformedShape(bounds).getBounds2D();
108             return bounds;
109        } finally {
110             clearFlags(IN_GET_BOUNDS);
111         }
112     }
113
114 }