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