--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
+ * in Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.scenegraph.g2d;\r
+\r
+import java.awt.Component;\r
+import java.awt.Cursor;\r
+import java.awt.Shape;\r
+import java.awt.geom.AffineTransform;\r
+import java.awt.geom.NoninvertibleTransformException;\r
+import java.awt.geom.Point2D;\r
+import java.awt.geom.Rectangle2D;\r
+\r
+import org.simantics.scenegraph.INode;\r
+import org.simantics.scenegraph.Node;\r
+import org.simantics.scenegraph.ParentNode;\r
+import org.simantics.scenegraph.g2d.events.Event;\r
+import org.simantics.scenegraph.g2d.events.EventTypes;\r
+import org.simantics.scenegraph.g2d.events.FocusEvent;\r
+import org.simantics.scenegraph.g2d.events.IEventHandler;\r
+import org.simantics.scenegraph.g2d.events.KeyEvent;\r
+import org.simantics.scenegraph.g2d.events.KeyEvent.KeyPressedEvent;\r
+import org.simantics.scenegraph.g2d.events.KeyEvent.KeyReleasedEvent;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonReleasedEvent;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseClickEvent;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDoubleClickedEvent;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseDragBegin;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseEnterEvent;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseExitEvent;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;\r
+import org.simantics.scenegraph.g2d.events.MouseEvent.MouseWheelMovedEvent;\r
+import org.simantics.scenegraph.g2d.events.NodeEventHandler;\r
+import org.simantics.scenegraph.g2d.events.TimeEvent;\r
+import org.simantics.scenegraph.g2d.events.command.CommandEvent;\r
+import org.simantics.scenegraph.utils.GeometryUtils;\r
+import org.simantics.scenegraph.utils.NodeUtil;\r
+\r
+public abstract class G2DNode extends Node implements IG2DNode {\r
+\r
+ private static final long serialVersionUID = 8283264115992894707L;\r
+\r
+ protected int z = 0;\r
+ protected AffineTransform transform = IdentityAffineTransform.INSTANCE;\r
+\r
+ @SyncField("z")\r
+ public void setZIndex(int z) {\r
+ if (z != this.z) {\r
+ G2DParentNode parent = (G2DParentNode) getParent();\r
+ if (parent != null)\r
+ parent.invalidateChildOrder();\r
+ this.z = z;\r
+ }\r
+ }\r
+\r
+ public int getZIndex() {\r
+ return z;\r
+ }\r
+ \r
+ public boolean validate() {\r
+ return true;\r
+ }\r
+ \r
+ @Override\r
+ public void cleanup() {\r
+ retractMapping();\r
+ repaint();\r
+ }\r
+\r
+ public void repaint() {\r
+ INode parent = getParent();\r
+ while(parent != null && !(parent instanceof G2DSceneGraph))\r
+ parent = parent.getParent();\r
+ if(parent == null || ((G2DSceneGraph)parent).getRootPane() == null) return;\r
+ ((G2DSceneGraph)parent).getRootPane().repaint();\r
+ }\r
+\r
+\r
+ // Bounds and transformation\r
+\r
+ public AffineTransform getTransform() {\r
+ return transform;\r
+ }\r
+\r
+ @PropertySetter("Transform")\r
+ @SyncField("transform")\r
+ public void setTransform(AffineTransform transform) {\r
+ assert(transform != null);\r
+ if (transform.isIdentity())\r
+ this.transform = IdentityAffineTransform.INSTANCE;\r
+ else\r
+ this.transform = transform;\r
+ }\r
+\r
+ /**\r
+ * Return bounds transformed with local transformation\r
+ * \r
+ * @return transformed bounds\r
+ */\r
+ public Rectangle2D getBounds() {\r
+ Rectangle2D local = getBoundsInLocal();\r
+ if (local == null)\r
+ return null;\r
+ // TODO: potential spot for CPU/memory allocation optimization\r
+ // by using more specialized implementations\r
+ return transform.createTransformedShape(local).getBounds2D();\r
+ }\r
+\r
+ /**\r
+ * Return bounds in local coordinates\r
+ * \r
+ * @return bounds\r
+ */\r
+ abstract public Rectangle2D getBoundsInLocal();\r
+ \r
+ public Rectangle2D getBoundsInLocal(boolean ignoreNulls) {\r
+ return getBoundsInLocal();\r
+ }\r
+\r
+ // Helper methods for bounds checking\r
+\r
+ public boolean contains(Point2D point) {\r
+ Rectangle2D bounds = getBounds();\r
+ if(bounds == null) return false;\r
+ return bounds.contains(point);\r
+ }\r
+\r
+ public boolean containsLocal(Point2D localPoint) {\r
+ Rectangle2D bounds = getBoundsInLocal();\r
+ if(bounds == null) return false;\r
+ return bounds.contains(localPoint);\r
+ }\r
+\r
+ public boolean intersects(Rectangle2D b) {\r
+ if (b == null)\r
+ return true;\r
+ Rectangle2D a = getBounds();\r
+ if (a == null)\r
+ return true;\r
+ /*\r
+ * Compared to Rectangle2D.intersects, this\r
+ * intersects closed (not open) shapes.\r
+ */\r
+ double ax = a.getX();\r
+ double ay = a.getY();\r
+ double aw = a.getWidth();\r
+ double ah = a.getHeight();\r
+ double bx = b.getX();\r
+ double by = b.getY();\r
+ double bw = b.getWidth();\r
+ double bh = b.getHeight();\r
+ return (ax + aw >= bx &&\r
+ ay + ah >= by &&\r
+ ax <= bx + bw &&\r
+ ay <= by + bh);\r
+ }\r
+\r
+ public Point2D localToParent(Point2D point) {\r
+ return transform.transform(point, null);\r
+ }\r
+\r
+ public Rectangle2D localToParent(Rectangle2D rect) {\r
+ return transform.createTransformedShape(rect).getBounds2D();\r
+ }\r
+\r
+ public Point2D parentToLocal(Point2D point) {\r
+ AffineTransform inverse = null;\r
+ try {\r
+ inverse = transform.createInverse();\r
+ return inverse.transform(point, null);\r
+ } catch (NoninvertibleTransformException e) {\r
+ e.printStackTrace(); // FIXME\r
+ }\r
+ return point;\r
+ }\r
+\r
+ public Point2D parentToLocal(Point2D src, Point2D dst) {\r
+ AffineTransform inverse = null;\r
+ try {\r
+ inverse = transform.createInverse();\r
+ return inverse.transform(src, dst);\r
+ } catch (NoninvertibleTransformException e) {\r
+ e.printStackTrace(); // FIXME\r
+ }\r
+ return src;\r
+ }\r
+\r
+ public Rectangle2D parentToLocal(Rectangle2D rect) {\r
+ AffineTransform inverse = null;\r
+ try {\r
+ inverse = transform.createInverse();\r
+ return inverse.createTransformedShape(rect).getBounds2D();\r
+ } catch (NoninvertibleTransformException e) {\r
+ e.printStackTrace(); // FIXME\r
+ }\r
+ return rect;\r
+ }\r
+\r
+ public Point2D localToControl(Point2D point) {\r
+ IG2DNode node = this;\r
+ while(node != null) {\r
+ point = node.getTransform().transform(point, null);\r
+ node = (G2DParentNode)node.getParent(); // FIXME: it should be G2DParentNode but you can never be sure\r
+ }\r
+ return point;\r
+ }\r
+\r
+ public Rectangle2D localToControl(Rectangle2D rect) {\r
+ Shape shape = rect;\r
+ IG2DNode node = this;\r
+ while(node != null) {\r
+ shape = node.getTransform().createTransformedShape(shape);\r
+ node = (G2DParentNode)node.getParent(); // FIXME: it should be G2DParentNode but you can never be sure\r
+ }\r
+\r
+ return shape.getBounds2D();\r
+ }\r
+\r
+ public Point2D controlToLocal(Point2D point) {\r
+ AffineTransform at = NodeUtil.getGlobalToLocalTransform(this, null);\r
+ if (at == null)\r
+ return point;\r
+ return at.transform(point, null);\r
+ }\r
+\r
+ public Rectangle2D controlToLocal(Rectangle2D rect) {\r
+ AffineTransform at = NodeUtil.getGlobalToLocalTransform(this, null);\r
+ if (at == null)\r
+ return rect;\r
+ return GeometryUtils.transformRectangle(at, rect);\r
+ }\r
+\r
+ @Override\r
+ public String toString() {\r
+ return super.toString() + " [z=" + z + ",transform=" + transform + "]";\r
+ }\r
+\r
+ /**\r
+ * @see org.simantics.scenegraph.g2d.IG2DNode#getRootNode()\r
+ */\r
+ public G2DSceneGraph getRootNode2D() {\r
+ ParentNode<?> root = getRootNode();\r
+ return (G2DSceneGraph) root;\r
+ }\r
+\r
+ @Override\r
+ public boolean hasFocus() {\r
+ return getFocusNode() == this;\r
+ }\r
+\r
+ @Override\r
+ public IG2DNode getFocusNode() {\r
+ return getRootNode2D().getFocusNode();\r
+ }\r
+\r
+ @Override\r
+ public void setFocusNode(IG2DNode node) {\r
+ getRootNode2D().setFocusNode(node);\r
+ }\r
+\r
+ protected NodeEventHandler getEventHandler() {\r
+ return NodeUtil.getNodeEventHandler(this);\r
+ }\r
+\r
+ protected void addEventHandler(IEventHandler handler) {\r
+ getEventHandler().add(handler);\r
+ }\r
+\r
+ protected void removeEventHandler(IEventHandler handler) {\r
+ getEventHandler().remove(handler);\r
+ }\r
+\r
+ @Override\r
+ public int getEventMask() {\r
+ return 0;\r
+ }\r
+\r
+ @Override\r
+ public boolean handleEvent(Event e) {\r
+ int eventType = EventTypes.toType(e);\r
+ switch (eventType) {\r
+ case EventTypes.Command:\r
+ return handleCommand((CommandEvent) e);\r
+\r
+ case EventTypes.FocusGained:\r
+ case EventTypes.FocusLost:\r
+ return handleFocusEvent((FocusEvent) e);\r
+\r
+ case EventTypes.KeyPressed:\r
+ return keyPressed((KeyPressedEvent) e);\r
+ case EventTypes.KeyReleased:\r
+ return keyReleased((KeyReleasedEvent) e);\r
+\r
+ case EventTypes.MouseButtonPressed:\r
+ return mouseButtonPressed((MouseButtonPressedEvent) e);\r
+ case EventTypes.MouseButtonReleased:\r
+ return mouseButtonReleased((MouseButtonReleasedEvent) e);\r
+ case EventTypes.MouseClick:\r
+ return mouseClicked((MouseClickEvent) e);\r
+ case EventTypes.MouseDoubleClick:\r
+ return mouseDoubleClicked((MouseDoubleClickedEvent) e);\r
+ case EventTypes.MouseMoved:\r
+ return mouseMoved((MouseMovedEvent) e);\r
+ case EventTypes.MouseDragBegin:\r
+ return mouseDragged((MouseDragBegin) e);\r
+ case EventTypes.MouseEnter:\r
+ return mouseEntered((MouseEnterEvent) e);\r
+ case EventTypes.MouseExit:\r
+ return mouseExited((MouseExitEvent) e);\r
+ case EventTypes.MouseWheel:\r
+ return mouseWheelMoved((MouseWheelMovedEvent) e);\r
+\r
+ case EventTypes.Time:\r
+ return handleTimeEvent((TimeEvent) e);\r
+ }\r
+ return false;\r
+ }\r
+\r
+ protected boolean keyReleased(KeyReleasedEvent e) {\r
+ return false;\r
+ }\r
+\r
+ protected boolean keyPressed(KeyPressedEvent e) {\r
+ return false;\r
+ }\r
+\r
+ protected boolean handleCommand(CommandEvent e) {\r
+ return false;\r
+ }\r
+\r
+ protected boolean handleFocusEvent(FocusEvent e) {\r
+ return false;\r
+ }\r
+\r
+ protected boolean handleKeyEvent(KeyEvent e) {\r
+ return false;\r
+ }\r
+\r
+ protected boolean mouseButtonPressed(MouseButtonPressedEvent e) {\r
+ return false;\r
+ }\r
+\r
+ protected boolean mouseButtonReleased(MouseButtonReleasedEvent e) {\r
+ return false;\r
+ }\r
+\r
+ protected boolean mouseClicked(MouseClickEvent e) {\r
+ return false;\r
+ }\r
+\r
+ protected boolean mouseDoubleClicked(MouseDoubleClickedEvent e) {\r
+ return false;\r
+ }\r
+\r
+ protected boolean mouseMoved(MouseMovedEvent e) {\r
+ return false;\r
+ }\r
+\r
+ protected boolean mouseDragged(MouseDragBegin e) {\r
+ return false;\r
+ }\r
+\r
+ protected boolean mouseEntered(MouseEnterEvent e) {\r
+ return false;\r
+ }\r
+\r
+ protected boolean mouseExited(MouseExitEvent e) {\r
+ return false;\r
+ }\r
+\r
+ protected boolean mouseWheelMoved(MouseWheelMovedEvent e) {\r
+ return false;\r
+ }\r
+\r
+ protected boolean handleTimeEvent(TimeEvent e) {\r
+ return false;\r
+ }\r
+\r
+ protected void setCursor(int cursorType) {\r
+ Component rootPane = NodeUtil.findRootPane(this);\r
+ if (rootPane != null)\r
+ rootPane.setCursor(Cursor.getPredefinedCursor(cursorType));\r
+ }\r
+\r
+ protected void setCursor(Cursor cursor) {\r
+ Component rootPane = NodeUtil.findRootPane(this);\r
+ if (rootPane != null)\r
+ rootPane.setCursor(cursor);\r
+ }\r
+\r
+}\r