]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/G2DNode.java
Support SVG generation from scenegraph
[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 cleanup() {
74         retractMapping();
75         repaint();
76     }
77
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();
84     }
85
86
87     // Bounds and transformation
88
89     public AffineTransform getTransform() {
90         return transform;
91     }
92
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;
99         else
100             this.transform = transform;
101     }
102
103     /**
104      * Return bounds transformed with local transformation
105      * 
106      * @return transformed bounds
107      */
108     public Rectangle2D getBounds() {
109         Rectangle2D local = getBoundsInLocal();
110         if (local == null)
111             return null;
112         // TODO: potential spot for CPU/memory allocation optimization
113         // by using more specialized implementations
114         return transform.createTransformedShape(local).getBounds2D();
115     }
116
117     /**
118      * Return bounds in local coordinates
119      * 
120      * @return bounds
121      */
122     abstract public Rectangle2D getBoundsInLocal();
123     
124     public Rectangle2D getBoundsInLocal(boolean ignoreNulls) {
125         return getBoundsInLocal();
126     }
127
128     // Helper methods for bounds checking
129
130     public boolean contains(Point2D point) {
131         Rectangle2D bounds = getBounds();
132         if(bounds == null) return false;
133         return bounds.contains(point);
134     }
135
136     public boolean containsLocal(Point2D localPoint) {
137         Rectangle2D bounds = getBoundsInLocal();
138         if(bounds == null) return false;
139         return bounds.contains(localPoint);
140     }
141
142     public boolean intersects(Rectangle2D b) {
143         if (b == null)
144             return true;
145         Rectangle2D a = getBounds();
146         if (a == null)
147             return true;
148         /*
149          * Compared to Rectangle2D.intersects, this
150          * intersects closed (not open) shapes.
151          */
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 &&
161                 ay + ah >= by &&
162                 ax <= bx + bw &&
163                 ay <= by + bh);
164     }
165
166     public Point2D localToParent(Point2D point) {
167         return transform.transform(point, null);
168     }
169
170     public Rectangle2D localToParent(Rectangle2D rect) {
171         return transform.createTransformedShape(rect).getBounds2D();
172     }
173
174     public Point2D parentToLocal(Point2D point) {
175         AffineTransform inverse = null;
176         try {
177             inverse = transform.createInverse();
178             return inverse.transform(point, null);
179         } catch (NoninvertibleTransformException e) {
180             e.printStackTrace(); // FIXME
181         }
182         return point;
183     }
184
185     public Point2D parentToLocal(Point2D src, Point2D dst) {
186         AffineTransform inverse = null;
187         try {
188             inverse = transform.createInverse();
189             return inverse.transform(src, dst);
190         } catch (NoninvertibleTransformException e) {
191             e.printStackTrace(); // FIXME
192         }
193         return src;
194     }
195
196     public Rectangle2D parentToLocal(Rectangle2D rect) {
197         AffineTransform inverse = null;
198         try {
199             inverse = transform.createInverse();
200             return inverse.createTransformedShape(rect).getBounds2D();
201         } catch (NoninvertibleTransformException e) {
202             e.printStackTrace(); // FIXME
203         }
204         return rect;
205     }
206
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
212         }
213         return point;
214     }
215
216     public Rectangle2D localToControl(Rectangle2D rect) {
217         Shape shape = 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
222         }
223
224         return shape.getBounds2D();
225     }
226
227     public Point2D controlToLocal(Point2D point) {
228         AffineTransform at = NodeUtil.getGlobalToLocalTransform(this, null);
229         if (at == null)
230             return point;
231         return at.transform(point, null);
232     }
233
234     public Rectangle2D controlToLocal(Rectangle2D rect) {
235         AffineTransform at = NodeUtil.getGlobalToLocalTransform(this, null);
236         if (at == null)
237             return rect;
238         return GeometryUtils.transformRectangle(at, rect);
239     }
240
241     @Override
242     public String toString() {
243         return super.toString() + " [z=" + z + ",transform=" + transform + "]";
244     }
245
246     /**
247      * @see org.simantics.scenegraph.g2d.IG2DNode#getRootNode()
248      */
249     public G2DSceneGraph getRootNode2D() {
250         ParentNode<?> root = getRootNode();
251         return (G2DSceneGraph) root;
252     }
253
254     @Override
255     public boolean hasFocus() {
256         return getFocusNode() == this;
257     }
258
259     @Override
260     public IG2DNode getFocusNode() {
261         return getRootNode2D().getFocusNode();
262     }
263
264     @Override
265     public void setFocusNode(IG2DNode node) {
266         getRootNode2D().setFocusNode(node);
267     }
268
269     protected NodeEventHandler getEventHandler() {
270         return NodeUtil.getNodeEventHandler(this);
271     }
272
273     protected void addEventHandler(IEventHandler handler) {
274         getEventHandler().add(handler);
275     }
276
277     protected void removeEventHandler(IEventHandler handler) {
278         getEventHandler().remove(handler);
279     }
280
281     @Override
282     public int getEventMask() {
283         return 0;
284     }
285
286     @Override
287     public boolean handleEvent(Event e) {
288         int eventType = EventTypes.toType(e);
289         switch (eventType) {
290             case EventTypes.Command:
291                 return handleCommand((CommandEvent) e);
292
293             case EventTypes.FocusGained:
294             case EventTypes.FocusLost:
295                 return handleFocusEvent((FocusEvent) e);
296
297             case EventTypes.KeyPressed:
298                 return keyPressed((KeyPressedEvent) e);
299             case EventTypes.KeyReleased:
300                 return keyReleased((KeyReleasedEvent) e);
301
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);
320
321             case EventTypes.Time:
322                 return handleTimeEvent((TimeEvent) e);
323         }
324         return false;
325     }
326
327     protected boolean keyReleased(KeyReleasedEvent e) {
328         return false;
329     }
330
331     protected boolean keyPressed(KeyPressedEvent e) {
332         return false;
333     }
334
335     protected boolean handleCommand(CommandEvent e) {
336         return false;
337     }
338
339     protected boolean handleFocusEvent(FocusEvent e) {
340         return false;
341     }
342
343     protected boolean handleKeyEvent(KeyEvent e) {
344         return false;
345     }
346
347     protected boolean mouseButtonPressed(MouseButtonPressedEvent e) {
348         return false;
349     }
350
351     protected boolean mouseButtonReleased(MouseButtonReleasedEvent e) {
352         return false;
353     }
354
355     protected boolean mouseClicked(MouseClickEvent e) {
356         return false;
357     }
358
359     protected boolean mouseDoubleClicked(MouseDoubleClickedEvent e) {
360         return false;
361     }
362
363     protected boolean mouseMoved(MouseMovedEvent e) {
364         return false;
365     }
366
367     protected boolean mouseDragged(MouseDragBegin e) {
368         return false;
369     }
370
371     protected boolean mouseEntered(MouseEnterEvent e) {
372         return false;
373     }
374
375     protected boolean mouseExited(MouseExitEvent e) {
376         return false;
377     }
378
379     protected boolean mouseWheelMoved(MouseWheelMovedEvent e) {
380         return false;
381     }
382
383     protected boolean handleTimeEvent(TimeEvent e) {
384         return false;
385     }
386
387     protected void setCursor(int cursorType) {
388         Component rootPane = NodeUtil.findRootPane(this);
389         if (rootPane != null)
390             rootPane.setCursor(Cursor.getPredefinedCursor(cursorType));
391     }
392
393     protected void setCursor(Cursor cursor) {
394         Component rootPane = NodeUtil.findRootPane(this);
395         if (rootPane != null)
396             rootPane.setCursor(cursor);
397     }
398     
399     public void accept(IG2DNodeVisitor visitor) {
400         visitor.enter(this);
401         visitor.leave(this);
402     }
403
404 }