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.swing;
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;
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;
34 import javax.swing.JComponent;
35 import javax.swing.UIDefaults;
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;
47 public class ComponentNode<T extends JComponent> extends G2DNode implements MouseListener, MouseMotionListener, KeyListener, FocusListener {
49 private static final long serialVersionUID = 3161843367263793336L;
50 protected ComponentContainer container = new ComponentContainer();
52 protected Rectangle2D bounds = null;
53 protected boolean focusable = true;
55 public class ComponentContainer extends Container {
59 private static final long serialVersionUID = 7233440430091475715L;
61 public ComponentContainer() {
64 protected boolean contains = false;
67 // public void setSize(int width, int height) {
68 // System.err.println("ss " + width + " " + height);
69 // super.setSize(width, height);
72 public void setContains(boolean contains) {
73 this.contains = contains;
76 // public void setBounds(int x, int y, int width, int height) {
77 // super.setBounds(x, y, width, height);
80 public void update(Graphics g) {
84 public void paint(Graphics g) {
87 public void paintAll(Graphics g) {
90 public void paintComponents(Graphics g) {
93 public void repaint() {
96 public void repaint(long tm) {
99 public void repaint(int x, int y, int width, int height) {
102 public void repaint(long tm, int x, int y, int width, int height) {
107 public boolean contains(int eventX, int eventY) {
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();
115 // ct.inverseTransform(new Point2D.Double(x,y), canvasPosition);
116 // } catch (NoninvertibleTransformException e) {
117 // e.printStackTrace();
119 //// System.err.println("pane tr=" + canvasPosition + " " + ct + " (" + x + "," + y + ")");
120 // return super.contains((int)canvasPosition.getX() , (int)canvasPosition.getY());
123 // System.err.println("pane tr=" + canvasPosition);
124 // //ElementUtils.controlToElementCoordinate(element, element.get, controlPoint, elementPoint);
125 // boolean contains = super.contains(x, y);
127 // System.err.println("pane contains!" + x + " " + y);
129 // System.err.println("pane does not contain!" + x + " " + y);
134 // int trX = 0;//getAbsoluteX(this, 0);
135 // int trY = 0;//getAbsoluteY(this, 0);
137 //// System.err.println("trX=" + trX + " trY = " + trY);
139 //// AWTChassis chassis = (AWTChassis)base.getParent().getParent();
140 // AffineTransform ct = chassis.getCanvasContext().getDefaultHintContext().getHint(Hints.KEY_CANVAS_TRANSFORM);
141 // if(ct == null) return false;
143 // int origX = eventX;
144 // int origY = eventY;
147 // eventX -= ct.getTranslateX();
148 // eventX /= ct.getScaleX();
151 // eventY -= ct.getTranslateY();
152 // eventY /= ct.getScaleY();
155 // System.err.println("ComponentNode contains? (" + origX + "," + origY + ")=>(" + eventX + "," + eventY + ")");
157 // return super.contains(eventX, eventY);
160 //// return contains;
164 public Cursor getCursor() {
165 if(contains == false || component == null || component.isCursorSet() == false) {
166 return Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR);
168 return component.getCursor();
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
176 private transient Font componentFont;
177 private transient double lastScale = 0;
179 @PropertySetter("Bounds")
181 public void setBounds(Rectangle2D bounds) {
182 assert(bounds != null);
183 this.bounds = bounds;
187 @PropertySetter("Background Color")
189 public void setBackgroundColor(Color color) {
190 if(component != null) {
191 component.setBackground(color);
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.
201 protected void setScaleDirty() {
202 // Triggers resetting of component font and size if necessary before
205 //System.out.println("scale dirty");
208 protected Font getComponentFont() {
209 if (component != null) {
210 if (componentFont == null) {
211 componentFont = component.getFont();
213 return componentFont;
218 protected void setComponentFont(Font font) {
219 this.componentFont = font;
221 component.setFont(font);
225 public void render(Graphics2D g2d) {
226 if(!visible || bounds == null) return;
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");
235 if (transform != null) {
236 g.transform(transform);
237 // sx = g.getTransform().getScaleX();
238 // sy = g.getTransform().getScaleY();
240 // g.scale(1/sx, 1/sy);
244 // Assumes proportional scaling always.
245 if (!scale && lastScale != sx) {
246 //System.out.println("NEW SCALE: " + sx + " vs. " + lastScale);
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);
257 // Handle component size scaling.
258 component.setSize((int)(bounds.getWidth()*sx), (int)(bounds.getHeight()*sy));
260 component.setSize((int)(bounds.getWidth()*sx), (int)(bounds.getHeight()*sy));
261 g.translate(bounds.getMinX()*sx, bounds.getMinY()*sy);
267 @SyncField({"visible"})
268 public void setVisible(boolean value) {
269 this.visible = value;
272 public Component getHeavyweightParent(Component component) {
273 if(component.isLightweight()) return getHeavyweightParent(component.getParent());
274 else return component;
278 public void handleEvent(AWTEvent event) {
279 if(focusable == false) return; // No event handling if not focusable
280 if(bounds == null) return; // AAARGH..
282 AWTEvent cevent = event;
285 if(event instanceof MouseEvent) {
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
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();
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());
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();
317 if(tb.contains(me.getPoint())) {
318 container.setContains(true);
320 container.setContains(false);
323 if(cevent.getID() == MouseEvent.MOUSE_DRAGGED && component != null && component.hasFocus()) {
324 cevent.setSource(component);
327 if (component != null && container.contains) { //cevent.getSource().equals(component)) {
329 cevent.setSource(component);
331 // container.dispatchEvent(cevent);
333 // Component hw = getHeavyweightParent(component);
334 // hw.dispatchEvent(cevent);
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()));
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()));
348 // c2.dispatchEvent(qe);
349 // System.err.println("qe=" + qe);
350 // System.err.println("xe=" + xe);
351 // System.err.println("cevent=" + cevent);
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);
365 cevent = new SGMouseEvent(new DummyComponent(), me.getID(), me.getWhen(), me.getModifiers(), p.getX(), p.getY(), me.getClickCount(), me.isPopupTrigger(), me.getButton(), me);
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);
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);
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);
389 if(component == null) return; // FIXME: assert maybe?
391 Container rootPane = NodeUtil.findRootPane(this);
392 if(rootPane != null) {
393 rootPane.add(container);
395 throw new AssertionError("The canvas has no rootPane!");
397 container.add(component);
398 component.setFocusable(true);
399 component.setIgnoreRepaint(true);
401 UIDefaults cDefaults = new UIDefaults();
403 component.putClientProperty("Nimbus.Overrides",cDefaults);
404 component.putClientProperty("Nimbus.Overrides.InheritDefaults",false);
406 // component.addMouseListener(new MouseListener() {
409 // public void mouseClicked(MouseEvent e) {
410 // System.err.println("aff");
414 // public void mousePressed(MouseEvent e) {
415 // System.err.println("aff2");
419 // public void mouseReleased(MouseEvent e) {
420 // System.err.println("aff3");
424 // public void mouseEntered(MouseEvent e) {
425 // // TODO Auto-generated method stub
430 // public void mouseExited(MouseEvent e) {
431 // // TODO Auto-generated method stub
437 NodeUtil.getEventDelegator(this).addMouseListener(this);
438 NodeUtil.getEventDelegator(this).addMouseMotionListener(this);
439 NodeUtil.getEventDelegator(this).addKeyListener(this);
440 NodeUtil.getEventDelegator(this).addFocusListener(this);
445 public Rectangle2D getBoundsInLocal() {
450 public void finalize() throws Throwable {
456 public void cleanup() {
459 Container rootPane = NodeUtil.findRootPane(this);
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);
471 NodeUtil.getEventDelegator(this).removeMouseListener(this);
472 NodeUtil.getEventDelegator(this).removeMouseMotionListener(this);
473 NodeUtil.getEventDelegator(this).removeKeyListener(this);
474 NodeUtil.getEventDelegator(this).removeFocusListener(this);
480 public void keyTyped(KeyEvent e) {
485 public void keyPressed(KeyEvent e) {
490 public void keyReleased(KeyEvent e) {
495 public void mouseDragged(MouseEvent e) {
500 public void mouseMoved(MouseEvent e) {
505 public void mouseClicked(MouseEvent e) {
510 public void mousePressed(MouseEvent e) {
515 public void mouseReleased(MouseEvent e) {
520 public void mouseEntered(MouseEvent e) {
525 public void mouseExited(MouseEvent e) {
530 public void focusGained(FocusEvent e) {
535 public void focusLost(FocusEvent e) {