1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
\r
3 * in Industry THTH ry.
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.scenegraph.g2d;
\r
14 import java.awt.Component;
\r
15 import java.awt.Cursor;
\r
16 import java.awt.Shape;
\r
17 import java.awt.geom.AffineTransform;
\r
18 import java.awt.geom.NoninvertibleTransformException;
\r
19 import java.awt.geom.Point2D;
\r
20 import java.awt.geom.Rectangle2D;
\r
22 import org.simantics.scenegraph.INode;
\r
23 import org.simantics.scenegraph.Node;
\r
24 import org.simantics.scenegraph.ParentNode;
\r
25 import org.simantics.scenegraph.g2d.events.Event;
\r
26 import org.simantics.scenegraph.g2d.events.EventTypes;
\r
27 import org.simantics.scenegraph.g2d.events.FocusEvent;
\r
28 import org.simantics.scenegraph.g2d.events.IEventHandler;
\r
29 import org.simantics.scenegraph.g2d.events.KeyEvent;
\r
30 import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;
\r
31 import org.simantics.scenegraph.g2d.events.KeyEvent.KeyReleasedEvent;
\r
32 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent;
\r
33 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonReleasedEvent;
\r
34 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;
\r
35 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDoubleClickedEvent;
\r
36 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin;
\r
37 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseEnterEvent;
\r
38 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseExitEvent;
\r
39 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;
\r
40 import org.simantics.scenegraph.g2d.events.MouseEvent.MouseWheelMovedEvent;
\r
41 import org.simantics.scenegraph.g2d.events.NodeEventHandler;
\r
42 import org.simantics.scenegraph.g2d.events.TimeEvent;
\r
43 import org.simantics.scenegraph.g2d.events.command.CommandEvent;
\r
44 import org.simantics.scenegraph.utils.GeometryUtils;
\r
45 import org.simantics.scenegraph.utils.NodeUtil;
\r
47 public abstract class G2DNode extends Node implements IG2DNode {
\r
49 private static final long serialVersionUID = 8283264115992894707L;
\r
51 protected int z = 0;
\r
52 protected AffineTransform transform = IdentityAffineTransform.INSTANCE;
\r
55 public void setZIndex(int z) {
\r
57 G2DParentNode parent = (G2DParentNode) getParent();
\r
59 parent.invalidateChildOrder();
\r
64 public int getZIndex() {
\r
68 public boolean validate() {
\r
73 public void cleanup() {
\r
78 public void repaint() {
\r
79 INode parent = getParent();
\r
80 while(parent != null && !(parent instanceof G2DSceneGraph))
\r
81 parent = parent.getParent();
\r
82 if(parent == null || ((G2DSceneGraph)parent).getRootPane() == null) return;
\r
83 ((G2DSceneGraph)parent).getRootPane().repaint();
\r
87 // Bounds and transformation
\r
89 public AffineTransform getTransform() {
\r
93 @PropertySetter("Transform")
\r
94 @SyncField("transform")
\r
95 public void setTransform(AffineTransform transform) {
\r
96 assert(transform != null);
\r
97 if (transform.isIdentity())
\r
98 this.transform = IdentityAffineTransform.INSTANCE;
\r
100 this.transform = transform;
\r
104 * Return bounds transformed with local transformation
\r
106 * @return transformed bounds
\r
108 public Rectangle2D getBounds() {
\r
109 Rectangle2D local = getBoundsInLocal();
\r
112 // TODO: potential spot for CPU/memory allocation optimization
\r
113 // by using more specialized implementations
\r
114 return transform.createTransformedShape(local).getBounds2D();
\r
118 * Return bounds in local coordinates
\r
122 abstract public Rectangle2D getBoundsInLocal();
\r
124 public Rectangle2D getBoundsInLocal(boolean ignoreNulls) {
\r
125 return getBoundsInLocal();
\r
128 // Helper methods for bounds checking
\r
130 public boolean contains(Point2D point) {
\r
131 Rectangle2D bounds = getBounds();
\r
132 if(bounds == null) return false;
\r
133 return bounds.contains(point);
\r
136 public boolean containsLocal(Point2D localPoint) {
\r
137 Rectangle2D bounds = getBoundsInLocal();
\r
138 if(bounds == null) return false;
\r
139 return bounds.contains(localPoint);
\r
142 public boolean intersects(Rectangle2D b) {
\r
145 Rectangle2D a = getBounds();
\r
149 * Compared to Rectangle2D.intersects, this
\r
150 * intersects closed (not open) shapes.
\r
152 double ax = a.getX();
\r
153 double ay = a.getY();
\r
154 double aw = a.getWidth();
\r
155 double ah = a.getHeight();
\r
156 double bx = b.getX();
\r
157 double by = b.getY();
\r
158 double bw = b.getWidth();
\r
159 double bh = b.getHeight();
\r
160 return (ax + aw >= bx &&
\r
166 public Point2D localToParent(Point2D point) {
\r
167 return transform.transform(point, null);
\r
170 public Rectangle2D localToParent(Rectangle2D rect) {
\r
171 return transform.createTransformedShape(rect).getBounds2D();
\r
174 public Point2D parentToLocal(Point2D point) {
\r
175 AffineTransform inverse = null;
\r
177 inverse = transform.createInverse();
\r
178 return inverse.transform(point, null);
\r
179 } catch (NoninvertibleTransformException e) {
\r
180 e.printStackTrace(); // FIXME
\r
185 public Point2D parentToLocal(Point2D src, Point2D dst) {
\r
186 AffineTransform inverse = null;
\r
188 inverse = transform.createInverse();
\r
189 return inverse.transform(src, dst);
\r
190 } catch (NoninvertibleTransformException e) {
\r
191 e.printStackTrace(); // FIXME
\r
196 public Rectangle2D parentToLocal(Rectangle2D rect) {
\r
197 AffineTransform inverse = null;
\r
199 inverse = transform.createInverse();
\r
200 return inverse.createTransformedShape(rect).getBounds2D();
\r
201 } catch (NoninvertibleTransformException e) {
\r
202 e.printStackTrace(); // FIXME
\r
207 public Point2D localToControl(Point2D point) {
\r
208 IG2DNode node = this;
\r
209 while(node != null) {
\r
210 point = node.getTransform().transform(point, null);
\r
211 node = (G2DParentNode)node.getParent(); // FIXME: it should be G2DParentNode but you can never be sure
\r
216 public Rectangle2D localToControl(Rectangle2D rect) {
\r
217 Shape shape = rect;
\r
218 IG2DNode node = this;
\r
219 while(node != null) {
\r
220 shape = node.getTransform().createTransformedShape(shape);
\r
221 node = (G2DParentNode)node.getParent(); // FIXME: it should be G2DParentNode but you can never be sure
\r
224 return shape.getBounds2D();
\r
227 public Point2D controlToLocal(Point2D point) {
\r
228 AffineTransform at = NodeUtil.getGlobalToLocalTransform(this, null);
\r
231 return at.transform(point, null);
\r
234 public Rectangle2D controlToLocal(Rectangle2D rect) {
\r
235 AffineTransform at = NodeUtil.getGlobalToLocalTransform(this, null);
\r
238 return GeometryUtils.transformRectangle(at, rect);
\r
242 public String toString() {
\r
243 return super.toString() + " [z=" + z + ",transform=" + transform + "]";
\r
247 * @see org.simantics.scenegraph.g2d.IG2DNode#getRootNode()
\r
249 public G2DSceneGraph getRootNode2D() {
\r
250 ParentNode<?> root = getRootNode();
\r
251 return (G2DSceneGraph) root;
\r
255 public boolean hasFocus() {
\r
256 return getFocusNode() == this;
\r
260 public IG2DNode getFocusNode() {
\r
261 return getRootNode2D().getFocusNode();
\r
265 public void setFocusNode(IG2DNode node) {
\r
266 getRootNode2D().setFocusNode(node);
\r
269 protected NodeEventHandler getEventHandler() {
\r
270 return NodeUtil.getNodeEventHandler(this);
\r
273 protected void addEventHandler(IEventHandler handler) {
\r
274 getEventHandler().add(handler);
\r
277 protected void removeEventHandler(IEventHandler handler) {
\r
278 getEventHandler().remove(handler);
\r
282 public int getEventMask() {
\r
287 public boolean handleEvent(Event e) {
\r
288 int eventType = EventTypes.toType(e);
\r
289 switch (eventType) {
\r
290 case EventTypes.Command:
\r
291 return handleCommand((CommandEvent) e);
\r
293 case EventTypes.FocusGained:
\r
294 case EventTypes.FocusLost:
\r
295 return handleFocusEvent((FocusEvent) e);
\r
297 case EventTypes.KeyPressed:
\r
298 return keyPressed((KeyPressedEvent) e);
\r
299 case EventTypes.KeyReleased:
\r
300 return keyReleased((KeyReleasedEvent) e);
\r
302 case EventTypes.MouseButtonPressed:
\r
303 return mouseButtonPressed((MouseButtonPressedEvent) e);
\r
304 case EventTypes.MouseButtonReleased:
\r
305 return mouseButtonReleased((MouseButtonReleasedEvent) e);
\r
306 case EventTypes.MouseClick:
\r
307 return mouseClicked((MouseClickEvent) e);
\r
308 case EventTypes.MouseDoubleClick:
\r
309 return mouseDoubleClicked((MouseDoubleClickedEvent) e);
\r
310 case EventTypes.MouseMoved:
\r
311 return mouseMoved((MouseMovedEvent) e);
\r
312 case EventTypes.MouseDragBegin:
\r
313 return mouseDragged((MouseDragBegin) e);
\r
314 case EventTypes.MouseEnter:
\r
315 return mouseEntered((MouseEnterEvent) e);
\r
316 case EventTypes.MouseExit:
\r
317 return mouseExited((MouseExitEvent) e);
\r
318 case EventTypes.MouseWheel:
\r
319 return mouseWheelMoved((MouseWheelMovedEvent) e);
\r
321 case EventTypes.Time:
\r
322 return handleTimeEvent((TimeEvent) e);
\r
327 protected boolean keyReleased(KeyReleasedEvent e) {
\r
331 protected boolean keyPressed(KeyPressedEvent e) {
\r
335 protected boolean handleCommand(CommandEvent e) {
\r
339 protected boolean handleFocusEvent(FocusEvent e) {
\r
343 protected boolean handleKeyEvent(KeyEvent e) {
\r
347 protected boolean mouseButtonPressed(MouseButtonPressedEvent e) {
\r
351 protected boolean mouseButtonReleased(MouseButtonReleasedEvent e) {
\r
355 protected boolean mouseClicked(MouseClickEvent e) {
\r
359 protected boolean mouseDoubleClicked(MouseDoubleClickedEvent e) {
\r
363 protected boolean mouseMoved(MouseMovedEvent e) {
\r
367 protected boolean mouseDragged(MouseDragBegin e) {
\r
371 protected boolean mouseEntered(MouseEnterEvent e) {
\r
375 protected boolean mouseExited(MouseExitEvent e) {
\r
379 protected boolean mouseWheelMoved(MouseWheelMovedEvent e) {
\r
383 protected boolean handleTimeEvent(TimeEvent e) {
\r
387 protected void setCursor(int cursorType) {
\r
388 Component rootPane = NodeUtil.findRootPane(this);
\r
389 if (rootPane != null)
\r
390 rootPane.setCursor(Cursor.getPredefinedCursor(cursorType));
\r
393 protected void setCursor(Cursor cursor) {
\r
394 Component rootPane = NodeUtil.findRootPane(this);
\r
395 if (rootPane != null)
\r
396 rootPane.setCursor(cursor);
\r