]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.scenegraph.swing/src/org/simantics/scenegraph/swing/ComponentNode.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.scenegraph.swing / src / org / simantics / scenegraph / swing / ComponentNode.java
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
8  *\r
9  * Contributors:\r
10  *     VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.scenegraph.swing;
13
14 import java.awt.AWTEvent;\r
15 import java.awt.Color;\r
16 import java.awt.Component;\r
17 import java.awt.Container;\r
18 import java.awt.Cursor;\r
19 import java.awt.Font;\r
20 import java.awt.Graphics;\r
21 import java.awt.Graphics2D;\r
22 import java.awt.event.FocusEvent;\r
23 import java.awt.event.FocusListener;\r
24 import java.awt.event.KeyEvent;\r
25 import java.awt.event.KeyListener;\r
26 import java.awt.event.MouseEvent;\r
27 import java.awt.event.MouseListener;\r
28 import java.awt.event.MouseMotionListener;\r
29 import java.awt.event.MouseWheelEvent;\r
30 import java.awt.geom.AffineTransform;\r
31 import java.awt.geom.Point2D;\r
32 import java.awt.geom.Rectangle2D;\r
33 \r
34 import javax.swing.JComponent;\r
35 import javax.swing.UIDefaults;\r
36 \r
37 import org.simantics.scenegraph.g2d.G2DFocusManager;\r
38 import org.simantics.scenegraph.g2d.G2DNode;\r
39 import org.simantics.scenegraph.g2d.G2DParentNode;\r
40 import org.simantics.scenegraph.g2d.IG2DNode;\r
41 import org.simantics.scenegraph.g2d.events.ISGMouseEvent;\r
42 import org.simantics.scenegraph.g2d.events.SGMouseEvent;\r
43 import org.simantics.scenegraph.g2d.events.SGMouseWheelEvent;\r
44 import org.simantics.scenegraph.utils.DummyComponent;\r
45 import org.simantics.scenegraph.utils.NodeUtil;\r
46
47 public class ComponentNode<T extends JComponent> extends G2DNode implements MouseListener, MouseMotionListener, KeyListener, FocusListener {
48
49     private static final long serialVersionUID = 3161843367263793336L;
50     protected ComponentContainer container = new ComponentContainer();\r
51     
52         protected Rectangle2D bounds = null;\r
53         protected boolean focusable = true;\r
54
55     public class ComponentContainer extends Container {
56         /**
57          * 
58          */
59         private static final long serialVersionUID = 7233440430091475715L;
60
61         public ComponentContainer() {
62             super();
63         }
64         protected boolean contains = false;
65 \r
66 //        @Override\r
67 //        public void setSize(int width, int height) {\r
68 //              System.err.println("ss " + width + " " + height);\r
69 //              super.setSize(width, height);\r
70 //        }\r
71         
72         public void setContains(boolean contains) {
73             this.contains = contains;\r
74         }\r
75 //        @Override\r
76 //        public void setBounds(int x, int y, int width, int height) {\r
77 //              super.setBounds(x, y, width, height);\r
78 //        }
79         @Override
80         public void update(Graphics g) {\r
81                 component.update(g);
82         }
83         @Override
84         public void paint(Graphics g) {
85         }
86         @Override
87         public void paintAll(Graphics g) {
88         }
89         @Override
90         public void paintComponents(Graphics g) {\r
91         }
92         @Override\r
93         public void repaint() {\r
94         }
95         @Override\r
96         public void repaint(long tm) {\r
97         }\r
98         @Override\r
99         public void repaint(int x, int y, int width, int height) {\r
100         }\r
101         @Override\r
102         public void repaint(long tm, int x, int y, int width, int height) {\r
103         }\r
104 \r
105         \r
106         @Override
107         public boolean contains(int eventX, int eventY) {\r
108                 \r
109 //                      G2DSceneGraph sg = (G2DSceneGraph)ComponentNode.this.getRootNode();\r
110                         //AWTChassis chassis = (AWTChassis)(sg.getRootPane().getParent());\r
111 //                      AffineTransform ct = chassis.getCanvasContext().getDefaultHintContext().getHint(Hints.KEY_CANVAS_TRANSFORM);\r
112 //                      if(ct == null) return false;\r
113 //                      Point2D canvasPosition = new Point2D.Double(); \r
114 //                      try {\r
115 //                              ct.inverseTransform(new Point2D.Double(x,y), canvasPosition);\r
116 //                      } catch (NoninvertibleTransformException e) {\r
117 //                              e.printStackTrace();\r
118 //                      }\r
119 ////                    System.err.println("pane tr=" + canvasPosition + " " + ct + " (" + x + "," + y + ")");\r
120 //                      return super.contains((int)canvasPosition.getX() , (int)canvasPosition.getY());\r
121                         \r
122                         \r
123 //                      System.err.println("pane tr=" + canvasPosition);\r
124 //                      //ElementUtils.controlToElementCoordinate(element, element.get, controlPoint, elementPoint);\r
125 //                      boolean contains = super.contains(x, y);\r
126 //                      if(contains) {\r
127 //                              System.err.println("pane contains!" + x + " " + y);\r
128 //                      } else {\r
129 //                              System.err.println("pane does not contain!" + x + " " + y);\r
130 //                      }\r
131 //                      return contains;\r
132                         \r
133                         \r
134 //                      int trX = 0;//getAbsoluteX(this, 0);\r
135 //                      int trY = 0;//getAbsoluteY(this, 0);\r
136 //                      \r
137 ////                    System.err.println("trX=" + trX + " trY = " + trY);\r
138 //                      \r
139 ////                    AWTChassis chassis = (AWTChassis)base.getParent().getParent();\r
140 //                      AffineTransform ct = chassis.getCanvasContext().getDefaultHintContext().getHint(Hints.KEY_CANVAS_TRANSFORM);\r
141 //                      if(ct == null) return false;\r
142 //\r
143 //                      int origX = eventX;\r
144 //                      int origY = eventY;\r
145 //                      \r
146 //                      eventX += trX;\r
147 //                      eventX -= ct.getTranslateX();\r
148 //                      eventX /= ct.getScaleX();\r
149 //                      eventX -= trX;\r
150 //                      eventY += trY;\r
151 //                      eventY -= ct.getTranslateY();\r
152 //                      eventY /= ct.getScaleY();\r
153 //                      eventY -= trY;\r
154 //\r
155 //                      System.err.println("ComponentNode contains? (" + origX + "," + origY + ")=>(" + eventX + "," + eventY + ")");\r
156 \r
157 //                      return super.contains(eventX, eventY);\r
158                 \r
159                 return true;
160 ////            return contains;
161         }
162
163         @Override
164         public Cursor getCursor() {
165             if(contains == false || component == null || component.isCursorSet() == false) {\r
166                 return Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);\r
167             }\r
168             return component.getCursor();
169         }
170     };
171
172     protected T      component  = null;
173     protected boolean visible = true;
174     protected boolean scale = false; // If true, component is scaled. If false, component is resized\r
175 \r
176     private transient Font componentFont;\r
177     private transient double lastScale = 0;\r
178     \r
179     @PropertySetter("Bounds")\r
180     @SyncField("bounds")\r
181     public void setBounds(Rectangle2D bounds) {\r
182         assert(bounds != null);\r
183         this.bounds = bounds;\r
184         setScaleDirty();\r
185     }\r
186     \r
187     @PropertySetter("Background Color")\r
188     @ClientSide\r
189         public void setBackgroundColor(Color color) {\r
190                 if(component != null) {\r
191                         component.setBackground(color);\r
192                 }\r
193         }\r
194 \r
195     /**\r
196      * If the size, font or any other scalable visual property of the component\r
197      * changes, this method should be invoked to make sure that\r
198      * {@link #render(Graphics2D)} will update the visual properties for the\r
199      * component with proper scaling.\r
200      */\r
201     protected void setScaleDirty() {\r
202         // Triggers resetting of component font and size if necessary before\r
203         // rendering.\r
204         lastScale = 0;\r
205         //System.out.println("scale dirty");\r
206     }\r
207 \r
208     protected Font getComponentFont() {\r
209         if (component != null) {\r
210             if (componentFont == null) {\r
211                 componentFont = component.getFont();\r
212             }\r
213             return componentFont;\r
214         }\r
215         return null;\r
216     }\r
217 \r
218     protected void setComponentFont(Font font) {\r
219         this.componentFont = font;\r
220         setScaleDirty();\r
221         component.setFont(font);\r
222     }\r
223 \r
224     @Override
225     public void render(Graphics2D g2d) {
226         if(!visible || bounds == null) return;
227
228         Graphics2D g = (Graphics2D)g2d.create();
229         if (component != null) {\r
230             // see http://java.sun.com/docs/books/tutorial/uiswing/lookandfeel/size.html\r
231             component.putClientProperty("JComponent.sizeVariant", "large");
232
233             double sx = 1;\r
234             double sy = 1;\r
235             if (transform != null) {\r
236                 g.transform(transform);\r
237 //                sx = g.getTransform().getScaleX();\r
238 //                sy = g.getTransform().getScaleY();\r
239 //                if(!scale) {\r
240 //                      g.scale(1/sx, 1/sy);\r
241 //                }\r
242             }\r
243 \r
244             // Assumes proportional scaling always.\r
245             if (!scale && lastScale != sx) {\r
246                 //System.out.println("NEW SCALE: " + sx + " vs. " + lastScale);\r
247                 lastScale = sx;\r
248 \r
249                 // Handle font scaling\r
250                 if (componentFont == null)\r
251                     componentFont = component.getFont();\r
252                 if (componentFont != null) {\r
253                     Font newFont = component.getFont().deriveFont(componentFont.getSize2D()*(float)sx);\r
254                     component.setFont(newFont);\r
255                 }\r
256 \r
257                 // Handle component size scaling.\r
258                 component.setSize((int)(bounds.getWidth()*sx), (int)(bounds.getHeight()*sy));\r
259             }\r
260             component.setSize((int)(bounds.getWidth()*sx), (int)(bounds.getHeight()*sy));\r
261             g.translate(bounds.getMinX()*sx, bounds.getMinY()*sy);\r
262             component.paint(g);\r
263         }
264         g.dispose();
265     }
266
267     @SyncField({"visible"})
268     public void setVisible(boolean value) {
269         this.visible = value;
270     }
271 \r
272     public Component getHeavyweightParent(Component component) {\r
273         if(component.isLightweight()) return getHeavyweightParent(component.getParent());\r
274         else return component;\r
275     }\r
276     \r
277     // FIXME
278     public void handleEvent(AWTEvent event) {\r
279         if(focusable == false) return; // No event handling if not focusable\r
280         if(bounds == null) return; // AAARGH..\r
281         
282         AWTEvent cevent = event;\r
283         double sx = 1.0;\r
284         double sy = 1.0;\r
285         if(event instanceof MouseEvent) {\r
286                 if(!scale) {\r
287                     IG2DNode node = (IG2DNode) this.getParent();\r
288                     while(node != null) {\r
289                         sx *= node.getTransform().getScaleX();\r
290                         sy *= node.getTransform().getScaleY();\r
291                         node = (G2DParentNode)node.getParent(); // FIXME: it should be G2DParentNode but you can never be sure\r
292                     }\r
293                 }\r
294 \r
295             MouseEvent me = (MouseEvent)event;\r
296                 // Use double coordinates if available                  \r
297                 double mx = me.getX();\r
298                 double my = me.getY();\r
299                 if(event instanceof ISGMouseEvent) {\r
300                         mx = ((ISGMouseEvent)event).getDoubleX();\r
301                         my = ((ISGMouseEvent)event).getDoubleY();\r
302                 }\r
303                 AffineTransform i = AffineTransform.getTranslateInstance(-transform.getTranslateX()*sx-getBoundsInLocal().getMinX()*sx, -transform.getTranslateY()*sy-getBoundsInLocal().getMinY()*sy);\r
304             Point2D p = i.transform(new Point2D.Double(sx*mx, sy*my), null);\r
305             cevent = relocateEvent(me, p);\r
306             Rectangle2D tb = new Rectangle2D.Double(transform.getTranslateX()+getBoundsInLocal().getMinX(), transform.getTranslateY()+getBoundsInLocal().getMinY(), getBoundsInLocal().getWidth(), getBoundsInLocal().getHeight());\r
307             \r
308             if(cevent.getID() == MouseEvent.MOUSE_PRESSED || cevent.getID() == MouseWheelEvent.MOUSE_WHEEL) {\r
309                 if(tb.contains(me.getPoint())) {\r
310                     G2DFocusManager.INSTANCE.markFocus(component);\r
311                         if(component != null && component.hasFocus() == false) {\r
312                         component.requestFocusInWindow();\r
313                     }\r
314                 }\r
315             }\r
316 \r
317             if(tb.contains(me.getPoint())) {\r
318                 container.setContains(true);\r
319             } else {\r
320                 container.setContains(false);\r
321             }\r
322 \r
323             if(cevent.getID() == MouseEvent.MOUSE_DRAGGED && component != null && component.hasFocus()) {\r
324                 cevent.setSource(component);\r
325             }\r
326         }
327         if (component != null && container.contains) { //cevent.getSource().equals(component)) {\r
328 //              new Lightwe\r
329                 cevent.setSource(component);\r
330                 \r
331 //            container.dispatchEvent(cevent);\r
332                 \r
333 //              Component hw = getHeavyweightParent(component);\r
334 //              hw.dispatchEvent(cevent);\r
335                 \r
336 //              container.getParent().dispatchEvent(cevent);\r
337 //            System.err.println("cevent=" + cevent);\r
338 //            // FIXME: terrible kludge to dispatch event correctly to every child\r
339 //            for(Component c : component.getComponents()) {\r
340 //              AWTEvent xe = translateEvent(cevent, new Point2D.Double(-c.getLocation().getX(), -c.getLocation().getY()));\r
341 //              xe.setSource(c);\r
342 //                c.dispatchEvent(xe);\r
343 //                if(c instanceof JComponent) {\r
344 //                      for(Component c2 : ((JComponent)c).getComponents()) {\r
345 //                              AWTEvent qe = translateEvent(xe, new Point2D.Double(-c.getLocation().getX(), -c.getLocation().getY()));\r
346 //                              qe.setSource(c);\r
347 //                              qe.setSource(c2);\r
348 //                              c2.dispatchEvent(qe);\r
349 //                              System.err.println("qe=" + qe);\r
350 //                              System.err.println("xe=" + xe);\r
351 //                              System.err.println("cevent=" + cevent);\r
352 //                      }\r
353 //                }\r
354 //            }
355         }
356     }
357 \r
358     protected AWTEvent relocateEvent(AWTEvent e, Point2D p) {\r
359         if(!(e instanceof MouseEvent)) return e; // Only for mouse events\r
360         MouseEvent me = (MouseEvent)e;\r
361         MouseEvent cevent = null;\r
362         if(me.getID() == MouseWheelEvent.MOUSE_WHEEL) {\r
363             cevent = new SGMouseWheelEvent(new DummyComponent(), me.getID(), me.getWhen(), me.getModifiers(), p.getX(), p.getY(), me.getClickCount(), me.isPopupTrigger(), ((MouseWheelEvent)me).getScrollType(), ((MouseWheelEvent)me).getScrollAmount(), ((MouseWheelEvent)me).getWheelRotation(), me);\r
364         } else {\r
365             cevent = new SGMouseEvent(new DummyComponent(), me.getID(), me.getWhen(), me.getModifiers(), p.getX(), p.getY(), me.getClickCount(), me.isPopupTrigger(), me.getButton(), me);\r
366         }\r
367         return cevent;\r
368     }\r
369     \r
370     protected AWTEvent translateEvent(AWTEvent e, Point2D p) {\r
371         if(!(e instanceof MouseEvent)) return e; // Only for mouse events\r
372         MouseEvent me = (MouseEvent)e;\r
373         MouseEvent cevent = null;\r
374         if(me.getID() == MouseWheelEvent.MOUSE_WHEEL) {\r
375             cevent = new SGMouseWheelEvent(new DummyComponent(), me.getID(), me.getWhen(), me.getModifiers(), me.getX()+p.getX(), me.getY()+p.getY(), me.getClickCount(), me.isPopupTrigger(), ((MouseWheelEvent)me).getScrollType(), ((MouseWheelEvent)me).getScrollAmount(), ((MouseWheelEvent)me).getWheelRotation(), me);\r
376         } else {\r
377             cevent = new SGMouseEvent(new DummyComponent(), me.getID(), me.getWhen(), me.getModifiers(), me.getX()+p.getX(), me.getY()+p.getY(), me.getClickCount(), me.isPopupTrigger(), me.getButton(), me);\r
378         }\r
379         return cevent;\r
380     }\r
381     public void setFocusable(boolean focusable) {\r
382         this.focusable = focusable;\r
383         container.setContains(false); // Always false when focusable property is changed\r
384         component.setFocusable(focusable);\r
385     }\r
386     \r
387     @Override\r
388     public void init() {\r
389         if(component == null) return; // FIXME: assert maybe?\r
390 \r
391         Container rootPane = NodeUtil.findRootPane(this);\r
392         if(rootPane != null) {\r
393             rootPane.add(container);\r
394         } else {\r
395                 throw new AssertionError("The canvas has no rootPane!");\r
396         }\r
397         container.add(component);\r
398         component.setFocusable(true);\r
399         component.setIgnoreRepaint(true);\r
400 \r
401         UIDefaults cDefaults = new UIDefaults();\r
402 \r
403         component.putClientProperty("Nimbus.Overrides",cDefaults);\r
404         component.putClientProperty("Nimbus.Overrides.InheritDefaults",false);\r
405 \r
406 //        component.addMouseListener(new MouseListener() {\r
407 //\r
408 //                      @Override\r
409 //                      public void mouseClicked(MouseEvent e) {\r
410 //                              System.err.println("aff");\r
411 //                      }\r
412 //\r
413 //                      @Override\r
414 //                      public void mousePressed(MouseEvent e) {\r
415 //                              System.err.println("aff2");\r
416 //                      }\r
417 //\r
418 //                      @Override\r
419 //                      public void mouseReleased(MouseEvent e) {\r
420 //                              System.err.println("aff3");\r
421 //                      }\r
422 //\r
423 //                      @Override\r
424 //                      public void mouseEntered(MouseEvent e) {\r
425 //                              // TODO Auto-generated method stub\r
426 //                              \r
427 //                      }\r
428 //\r
429 //                      @Override\r
430 //                      public void mouseExited(MouseEvent e) {\r
431 //                              // TODO Auto-generated method stub\r
432 //                              \r
433 //                      }\r
434 //              \r
435 //        });\r
436         \r
437         NodeUtil.getEventDelegator(this).addMouseListener(this);\r
438         NodeUtil.getEventDelegator(this).addMouseMotionListener(this);\r
439         NodeUtil.getEventDelegator(this).addKeyListener(this);\r
440         NodeUtil.getEventDelegator(this).addFocusListener(this);\r
441         \r
442     }
443
444     @Override
445     public Rectangle2D getBoundsInLocal() {\r
446         return bounds;\r
447     }\r
448     
449     @Override
450     public void finalize() throws Throwable {
451         cleanup();\r
452         super.finalize();
453     }
454
455     @Override
456     public void cleanup() {\r
457         retractMapping();\r
458 \r
459         Container rootPane = NodeUtil.findRootPane(this);\r
460         
461         if(container != null) {\r
462                 rootPane.remove(container);\r
463                 container.setContains(false);
464             if(container.getParent() != null) container.getParent().remove(container); // eh...\r
465             if (component != null)
466                 container.remove(component);\r
467         }
468         container = null;
469         component = null;\r
470         \r
471         NodeUtil.getEventDelegator(this).removeMouseListener(this);\r
472         NodeUtil.getEventDelegator(this).removeMouseMotionListener(this);\r
473         NodeUtil.getEventDelegator(this).removeKeyListener(this);\r
474         NodeUtil.getEventDelegator(this).removeFocusListener(this);\r
475 \r
476         super.cleanup();
477     }\r
478 \r
479         @Override\r
480         public void keyTyped(KeyEvent e) {\r
481                 handleEvent(e);\r
482         }\r
483 \r
484         @Override\r
485         public void keyPressed(KeyEvent e) {\r
486                 handleEvent(e);\r
487         }\r
488 \r
489         @Override\r
490         public void keyReleased(KeyEvent e) {\r
491                 handleEvent(e);\r
492         }\r
493 \r
494         @Override\r
495         public void mouseDragged(MouseEvent e) {\r
496                 handleEvent(e);\r
497         }\r
498 \r
499         @Override\r
500         public void mouseMoved(MouseEvent e) {\r
501                 handleEvent(e);\r
502         }\r
503 \r
504         @Override\r
505         public void mouseClicked(MouseEvent e) {\r
506                 handleEvent(e);\r
507         }\r
508 \r
509         @Override\r
510         public void mousePressed(MouseEvent e) {\r
511                 handleEvent(e);\r
512         }\r
513 \r
514         @Override\r
515         public void mouseReleased(MouseEvent e) {\r
516                 handleEvent(e);\r
517         }\r
518 \r
519         @Override\r
520         public void mouseEntered(MouseEvent e) {\r
521                 handleEvent(e);\r
522         }\r
523 \r
524         @Override\r
525         public void mouseExited(MouseEvent e) {\r
526                 handleEvent(e);\r
527         }\r
528 \r
529         @Override\r
530         public void focusGained(FocusEvent e) {\r
531                 handleEvent(e);\r
532         }\r
533 \r
534         @Override\r
535         public void focusLost(FocusEvent e) {\r
536                 handleEvent(e);\r
537         }
538 }