]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/G2DNode.java
G2DParentNode handles "undefined" child bounds separately
[simantics/platform.git] / bundles / org.simantics.scenegraph / src / org / simantics / scenegraph / g2d / G2DNode.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;
13
14 import java.awt.Component;
15 import java.awt.Cursor;
16 import java.awt.Shape;
17 import java.awt.geom.AffineTransform;
18 import java.awt.geom.NoninvertibleTransformException;
19 import java.awt.geom.Point2D;
20 import java.awt.geom.Rectangle2D;
21
22 import org.simantics.scenegraph.INode;
23 import org.simantics.scenegraph.Node;
24 import org.simantics.scenegraph.ParentNode;
25 import org.simantics.scenegraph.g2d.events.Event;
26 import org.simantics.scenegraph.g2d.events.EventTypes;
27 import org.simantics.scenegraph.g2d.events.FocusEvent;
28 import org.simantics.scenegraph.g2d.events.IEventHandler;
29 import org.simantics.scenegraph.g2d.events.KeyEvent;
30 import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;
31 import org.simantics.scenegraph.g2d.events.KeyEvent.KeyReleasedEvent;
32 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent;
33 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonReleasedEvent;
34 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;
35 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDoubleClickedEvent;
36 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin;
37 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseEnterEvent;
38 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseExitEvent;
39 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;
40 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseWheelMovedEvent;
41 import org.simantics.scenegraph.g2d.events.NodeEventHandler;
42 import org.simantics.scenegraph.g2d.events.TimeEvent;
43 import org.simantics.scenegraph.g2d.events.command.CommandEvent;
44 import org.simantics.scenegraph.utils.GeometryUtils;
45 import org.simantics.scenegraph.utils.NodeUtil;
46
47 public abstract class G2DNode extends Node implements IG2DNode {
48
49     private static final long serialVersionUID = 8283264115992894707L;
50
51     protected int z = 0;
52     protected AffineTransform transform = IdentityAffineTransform.INSTANCE;
53
54     @SyncField("z")
55     public void setZIndex(int z) {
56         if (z != this.z) {
57             G2DParentNode parent = (G2DParentNode) getParent();
58             if (parent != null)
59                 parent.invalidateChildOrder();
60             this.z = z;
61         }
62     }
63
64     public int getZIndex() {
65         return z;
66     }
67     
68     public boolean validate() {
69         return true;
70     }
71     
72     @Override
73     public void refresh() {
74     }
75
76     @Override
77     public void cleanup() {
78         retractMapping();
79         repaint();
80     }
81
82     public void repaint() {
83         INode parent = getParent();
84         while(parent != null && !(parent instanceof G2DSceneGraph))
85             parent = parent.getParent();
86         if(parent == null || ((G2DSceneGraph)parent).getRootPane() == null) return;
87         ((G2DSceneGraph)parent).getRootPane().repaint();
88     }
89
90
91     // Bounds and transformation
92
93     public AffineTransform getTransform() {
94         return transform;
95     }
96
97     @PropertySetter("Transform")
98     @SyncField("transform")
99     public void setTransform(AffineTransform transform) {
100         assert(transform != null);
101         if (transform.isIdentity())
102             this.transform = IdentityAffineTransform.INSTANCE;
103         else
104             this.transform = transform;
105     }
106
107     /**
108      * Return bounds transformed with local transformation
109      * 
110      * @return transformed bounds
111      */
112     public Rectangle2D getBounds() {
113         Rectangle2D local = getBoundsInLocal();
114         if (local == null)
115             return null;
116         // TODO: potential spot for CPU/memory allocation optimization
117         // by using more specialized implementations
118         return transform.createTransformedShape(local).getBounds2D();
119     }
120
121     /**
122      * Return bounds in local coordinates
123      * 
124      * @return bounds
125      */
126     abstract public Rectangle2D getBoundsInLocal();
127     
128     public Rectangle2D getBoundsInLocal(boolean ignoreNulls) {
129         return getBoundsInLocal();
130     }
131
132     // Helper methods for bounds checking
133
134     public boolean contains(Point2D point) {
135         Rectangle2D bounds = getBounds();
136         if(bounds == null) return false;
137         return bounds.contains(point);
138     }
139
140     public boolean containsLocal(Point2D localPoint) {
141         Rectangle2D bounds = getBoundsInLocal();
142         if(bounds == null) return false;
143         return bounds.contains(localPoint);
144     }
145
146     public boolean intersects(Rectangle2D b) {
147         if (b == null)
148             return true;
149         Rectangle2D a = getBounds();
150         if (a == null)
151             return true;
152         /*
153          * Compared to Rectangle2D.intersects, this
154          * intersects closed (not open) shapes.
155          */
156         double ax = a.getX();
157         double ay = a.getY();
158         double aw = a.getWidth();
159         double ah = a.getHeight();
160         double bx = b.getX();
161         double by = b.getY();
162         double bw = b.getWidth();
163         double bh = b.getHeight();
164         return (ax + aw >= bx &&
165                 ay + ah >= by &&
166                 ax <= bx + bw &&
167                 ay <= by + bh);
168     }
169
170     public Point2D localToParent(Point2D point) {
171         return transform.transform(point, null);
172     }
173
174     public Rectangle2D localToParent(Rectangle2D rect) {
175         return transform.createTransformedShape(rect).getBounds2D();
176     }
177
178     public Point2D parentToLocal(Point2D point) {
179         AffineTransform inverse = null;
180         try {
181             inverse = transform.createInverse();
182             return inverse.transform(point, null);
183         } catch (NoninvertibleTransformException e) {
184             e.printStackTrace(); // FIXME
185         }
186         return point;
187     }
188
189     public Point2D parentToLocal(Point2D src, Point2D dst) {
190         AffineTransform inverse = null;
191         try {
192             inverse = transform.createInverse();
193             return inverse.transform(src, dst);
194         } catch (NoninvertibleTransformException e) {
195             e.printStackTrace(); // FIXME
196         }
197         return src;
198     }
199
200     public Rectangle2D parentToLocal(Rectangle2D rect) {
201         AffineTransform inverse = null;
202         try {
203             inverse = transform.createInverse();
204             return inverse.createTransformedShape(rect).getBounds2D();
205         } catch (NoninvertibleTransformException e) {
206             e.printStackTrace(); // FIXME
207         }
208         return rect;
209     }
210
211     public Point2D localToControl(Point2D point) {
212         IG2DNode node = this;
213         while(node != null) {
214             point = node.getTransform().transform(point, null);
215             node = (G2DParentNode)node.getParent(); // FIXME: it should be G2DParentNode but you can never be sure
216         }
217         return point;
218     }
219
220     public Rectangle2D localToControl(Rectangle2D rect) {
221         Shape shape = rect;
222         IG2DNode node = this;
223         while(node != null) {
224             shape = node.getTransform().createTransformedShape(shape);
225             node = (G2DParentNode)node.getParent(); // FIXME: it should be G2DParentNode but you can never be sure
226         }
227
228         return shape.getBounds2D();
229     }
230
231     public Point2D controlToLocal(Point2D point) {
232         AffineTransform at = NodeUtil.getGlobalToLocalTransform(this, null);
233         if (at == null)
234             return point;
235         return at.transform(point, null);
236     }
237
238     public Rectangle2D controlToLocal(Rectangle2D rect) {
239         AffineTransform at = NodeUtil.getGlobalToLocalTransform(this, null);
240         if (at == null)
241             return rect;
242         return GeometryUtils.transformRectangle(at, rect);
243     }
244
245     @Override
246     public String toString() {
247         return super.toString() + " [z=" + z + ",transform=" + transform + "]";
248     }
249
250     /**
251      * @see org.simantics.scenegraph.g2d.IG2DNode#getRootNode()
252      */
253     public G2DSceneGraph getRootNode2D() {
254         ParentNode<?> root = getRootNode();
255         return (G2DSceneGraph) root;
256     }
257
258     @Override
259     public boolean hasFocus() {
260         return getFocusNode() == this;
261     }
262
263     @Override
264     public IG2DNode getFocusNode() {
265         return getRootNode2D().getFocusNode();
266     }
267
268     @Override
269     public void setFocusNode(IG2DNode node) {
270         getRootNode2D().setFocusNode(node);
271     }
272
273     protected NodeEventHandler getEventHandler() {
274         return NodeUtil.getNodeEventHandler(this);
275     }
276
277     protected void addEventHandler(IEventHandler handler) {
278         getEventHandler().add(handler);
279     }
280
281     protected void removeEventHandler(IEventHandler handler) {
282         getEventHandler().remove(handler);
283     }
284
285     @Override
286     public int getEventMask() {
287         return 0;
288     }
289
290     @Override
291     public boolean handleEvent(Event e) {
292         int eventType = EventTypes.toType(e);
293         switch (eventType) {
294             case EventTypes.Command:
295                 return handleCommand((CommandEvent) e);
296
297             case EventTypes.FocusGained:
298             case EventTypes.FocusLost:
299                 return handleFocusEvent((FocusEvent) e);
300
301             case EventTypes.KeyPressed:
302                 return keyPressed((KeyPressedEvent) e);
303             case EventTypes.KeyReleased:
304                 return keyReleased((KeyReleasedEvent) e);
305
306             case EventTypes.MouseButtonPressed:
307                 return mouseButtonPressed((MouseButtonPressedEvent) e);
308             case EventTypes.MouseButtonReleased:
309                 return mouseButtonReleased((MouseButtonReleasedEvent) e);
310             case EventTypes.MouseClick:
311                 return mouseClicked((MouseClickEvent) e);
312             case EventTypes.MouseDoubleClick:
313                 return mouseDoubleClicked((MouseDoubleClickedEvent) e);
314             case EventTypes.MouseMoved:
315                 return mouseMoved((MouseMovedEvent) e);
316             case EventTypes.MouseDragBegin:
317                 return mouseDragged((MouseDragBegin) e);
318             case EventTypes.MouseEnter:
319                 return mouseEntered((MouseEnterEvent) e);
320             case EventTypes.MouseExit:
321                 return mouseExited((MouseExitEvent) e);
322             case EventTypes.MouseWheel:
323                 return mouseWheelMoved((MouseWheelMovedEvent) e);
324
325             case EventTypes.Time:
326                 return handleTimeEvent((TimeEvent) e);
327         }
328         return false;
329     }
330
331     protected boolean keyReleased(KeyReleasedEvent e) {
332         return false;
333     }
334
335     protected boolean keyPressed(KeyPressedEvent e) {
336         return false;
337     }
338
339     protected boolean handleCommand(CommandEvent e) {
340         return false;
341     }
342
343     protected boolean handleFocusEvent(FocusEvent e) {
344         return false;
345     }
346
347     protected boolean handleKeyEvent(KeyEvent e) {
348         return false;
349     }
350
351     protected boolean mouseButtonPressed(MouseButtonPressedEvent e) {
352         return false;
353     }
354
355     protected boolean mouseButtonReleased(MouseButtonReleasedEvent e) {
356         return false;
357     }
358
359     protected boolean mouseClicked(MouseClickEvent e) {
360         return false;
361     }
362
363     protected boolean mouseDoubleClicked(MouseDoubleClickedEvent e) {
364         return false;
365     }
366
367     protected boolean mouseMoved(MouseMovedEvent e) {
368         return false;
369     }
370
371     protected boolean mouseDragged(MouseDragBegin e) {
372         return false;
373     }
374
375     protected boolean mouseEntered(MouseEnterEvent e) {
376         return false;
377     }
378
379     protected boolean mouseExited(MouseExitEvent e) {
380         return false;
381     }
382
383     protected boolean mouseWheelMoved(MouseWheelMovedEvent e) {
384         return false;
385     }
386
387     protected boolean handleTimeEvent(TimeEvent e) {
388         return false;
389     }
390
391     protected void setCursor(int cursorType) {
392         Component rootPane = NodeUtil.findRootPane(this);
393         if (rootPane != null)
394             rootPane.setCursor(Cursor.getPredefinedCursor(cursorType));
395     }
396
397     protected void setCursor(Cursor cursor) {
398         Component rootPane = NodeUtil.findRootPane(this);
399         if (rootPane != null)
400             rootPane.setCursor(cursor);
401     }
402     
403     public void accept(IG2DNodeVisitor visitor) {
404         visitor.enter(this);
405         visitor.leave(this);
406     }
407
408 }