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 refresh() {
77 public void cleanup() {
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();
91 // Bounds and transformation
93 public AffineTransform getTransform() {
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;
104 this.transform = transform;
108 * Return bounds transformed with local transformation
110 * @return transformed bounds
112 public Rectangle2D getBounds() {
113 Rectangle2D local = getBoundsInLocal();
116 // TODO: potential spot for CPU/memory allocation optimization
117 // by using more specialized implementations
118 return transform.createTransformedShape(local).getBounds2D();
122 * Return bounds in local coordinates
126 abstract public Rectangle2D getBoundsInLocal();
128 public Rectangle2D getBoundsInLocal(boolean ignoreNulls) {
129 return getBoundsInLocal();
132 // Helper methods for bounds checking
134 public boolean contains(Point2D point) {
135 Rectangle2D bounds = getBounds();
136 if(bounds == null) return false;
137 return bounds.contains(point);
140 public boolean containsLocal(Point2D localPoint) {
141 Rectangle2D bounds = getBoundsInLocal();
142 if(bounds == null) return false;
143 return bounds.contains(localPoint);
146 public boolean intersects(Rectangle2D b) {
149 Rectangle2D a = getBounds();
153 * Compared to Rectangle2D.intersects, this
154 * intersects closed (not open) shapes.
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 &&
170 public Point2D localToParent(Point2D point) {
171 return transform.transform(point, null);
174 public Rectangle2D localToParent(Rectangle2D rect) {
175 return transform.createTransformedShape(rect).getBounds2D();
178 public Point2D parentToLocal(Point2D point) {
179 AffineTransform inverse = null;
181 inverse = transform.createInverse();
182 return inverse.transform(point, null);
183 } catch (NoninvertibleTransformException e) {
184 e.printStackTrace(); // FIXME
189 public Point2D parentToLocal(Point2D src, Point2D dst) {
190 AffineTransform inverse = null;
192 inverse = transform.createInverse();
193 return inverse.transform(src, dst);
194 } catch (NoninvertibleTransformException e) {
195 e.printStackTrace(); // FIXME
200 public Rectangle2D parentToLocal(Rectangle2D rect) {
201 AffineTransform inverse = null;
203 inverse = transform.createInverse();
204 return inverse.createTransformedShape(rect).getBounds2D();
205 } catch (NoninvertibleTransformException e) {
206 e.printStackTrace(); // FIXME
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
220 public Rectangle2D localToControl(Rectangle2D 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
228 return shape.getBounds2D();
231 public Point2D controlToLocal(Point2D point) {
232 AffineTransform at = NodeUtil.getGlobalToLocalTransform(this, null);
235 return at.transform(point, null);
238 public Rectangle2D controlToLocal(Rectangle2D rect) {
239 AffineTransform at = NodeUtil.getGlobalToLocalTransform(this, null);
242 return GeometryUtils.transformRectangle(at, rect);
246 public String toString() {
247 return super.toString() + " [z=" + z + ",transform=" + transform + "]";
251 * @see org.simantics.scenegraph.g2d.IG2DNode#getRootNode()
253 public G2DSceneGraph getRootNode2D() {
254 ParentNode<?> root = getRootNode();
255 return (G2DSceneGraph) root;
259 public boolean hasFocus() {
260 return getFocusNode() == this;
264 public IG2DNode getFocusNode() {
265 return getRootNode2D().getFocusNode();
269 public void setFocusNode(IG2DNode node) {
270 getRootNode2D().setFocusNode(node);
273 protected NodeEventHandler getEventHandler() {
274 return NodeUtil.getNodeEventHandler(this);
277 protected void addEventHandler(IEventHandler handler) {
278 getEventHandler().add(handler);
281 protected void removeEventHandler(IEventHandler handler) {
282 getEventHandler().remove(handler);
286 public int getEventMask() {
291 public boolean handleEvent(Event e) {
292 int eventType = EventTypes.toType(e);
294 case EventTypes.Command:
295 return handleCommand((CommandEvent) e);
297 case EventTypes.FocusGained:
298 case EventTypes.FocusLost:
299 return handleFocusEvent((FocusEvent) e);
301 case EventTypes.KeyPressed:
302 return keyPressed((KeyPressedEvent) e);
303 case EventTypes.KeyReleased:
304 return keyReleased((KeyReleasedEvent) e);
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);
325 case EventTypes.Time:
326 return handleTimeEvent((TimeEvent) e);
331 protected boolean keyReleased(KeyReleasedEvent e) {
335 protected boolean keyPressed(KeyPressedEvent e) {
339 protected boolean handleCommand(CommandEvent e) {
343 protected boolean handleFocusEvent(FocusEvent e) {
347 protected boolean handleKeyEvent(KeyEvent e) {
351 protected boolean mouseButtonPressed(MouseButtonPressedEvent e) {
355 protected boolean mouseButtonReleased(MouseButtonReleasedEvent e) {
359 protected boolean mouseClicked(MouseClickEvent e) {
363 protected boolean mouseDoubleClicked(MouseDoubleClickedEvent e) {
367 protected boolean mouseMoved(MouseMovedEvent e) {
371 protected boolean mouseDragged(MouseDragBegin e) {
375 protected boolean mouseEntered(MouseEnterEvent e) {
379 protected boolean mouseExited(MouseExitEvent e) {
383 protected boolean mouseWheelMoved(MouseWheelMovedEvent e) {
387 protected boolean handleTimeEvent(TimeEvent e) {
391 protected void setCursor(int cursorType) {
392 Component rootPane = NodeUtil.findRootPane(this);
393 if (rootPane != null)
394 rootPane.setCursor(Cursor.getPredefinedCursor(cursorType));
397 protected void setCursor(Cursor cursor) {
398 Component rootPane = NodeUtil.findRootPane(this);
399 if (rootPane != null)
400 rootPane.setCursor(cursor);
403 public void accept(IG2DNodeVisitor visitor) {