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