/******************************************************************************* * Copyright (c) 2007, 2010 Association for Decentralized Information Management * in Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.scenegraph.swing; import java.awt.AWTEvent; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Cursor; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.MouseWheelEvent; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import javax.swing.JComponent; import javax.swing.UIDefaults; import org.simantics.scenegraph.g2d.G2DFocusManager; import org.simantics.scenegraph.g2d.G2DNode; import org.simantics.scenegraph.g2d.G2DParentNode; import org.simantics.scenegraph.g2d.IG2DNode; import org.simantics.scenegraph.g2d.events.ISGMouseEvent; import org.simantics.scenegraph.g2d.events.SGMouseEvent; import org.simantics.scenegraph.g2d.events.SGMouseWheelEvent; import org.simantics.scenegraph.utils.DummyComponent; import org.simantics.scenegraph.utils.NodeUtil; public class ComponentNode extends G2DNode implements MouseListener, MouseMotionListener, KeyListener, FocusListener { private static final long serialVersionUID = 3161843367263793336L; protected ComponentContainer container = new ComponentContainer(); protected Rectangle2D bounds = null; protected boolean focusable = true; public class ComponentContainer extends Container { /** * */ private static final long serialVersionUID = 7233440430091475715L; public ComponentContainer() { super(); } protected boolean contains = false; // @Override // public void setSize(int width, int height) { // System.err.println("ss " + width + " " + height); // super.setSize(width, height); // } public void setContains(boolean contains) { this.contains = contains; } // @Override // public void setBounds(int x, int y, int width, int height) { // super.setBounds(x, y, width, height); // } @Override public void update(Graphics g) { component.update(g); } @Override public void paint(Graphics g) { } @Override public void paintAll(Graphics g) { } @Override public void paintComponents(Graphics g) { } @Override public void repaint() { } @Override public void repaint(long tm) { } @Override public void repaint(int x, int y, int width, int height) { } @Override public void repaint(long tm, int x, int y, int width, int height) { } @Override public boolean contains(int eventX, int eventY) { // G2DSceneGraph sg = (G2DSceneGraph)ComponentNode.this.getRootNode(); //AWTChassis chassis = (AWTChassis)(sg.getRootPane().getParent()); // AffineTransform ct = chassis.getCanvasContext().getDefaultHintContext().getHint(Hints.KEY_CANVAS_TRANSFORM); // if(ct == null) return false; // Point2D canvasPosition = new Point2D.Double(); // try { // ct.inverseTransform(new Point2D.Double(x,y), canvasPosition); // } catch (NoninvertibleTransformException e) { // e.printStackTrace(); // } //// System.err.println("pane tr=" + canvasPosition + " " + ct + " (" + x + "," + y + ")"); // return super.contains((int)canvasPosition.getX() , (int)canvasPosition.getY()); // System.err.println("pane tr=" + canvasPosition); // //ElementUtils.controlToElementCoordinate(element, element.get, controlPoint, elementPoint); // boolean contains = super.contains(x, y); // if(contains) { // System.err.println("pane contains!" + x + " " + y); // } else { // System.err.println("pane does not contain!" + x + " " + y); // } // return contains; // int trX = 0;//getAbsoluteX(this, 0); // int trY = 0;//getAbsoluteY(this, 0); // //// System.err.println("trX=" + trX + " trY = " + trY); // //// AWTChassis chassis = (AWTChassis)base.getParent().getParent(); // AffineTransform ct = chassis.getCanvasContext().getDefaultHintContext().getHint(Hints.KEY_CANVAS_TRANSFORM); // if(ct == null) return false; // // int origX = eventX; // int origY = eventY; // // eventX += trX; // eventX -= ct.getTranslateX(); // eventX /= ct.getScaleX(); // eventX -= trX; // eventY += trY; // eventY -= ct.getTranslateY(); // eventY /= ct.getScaleY(); // eventY -= trY; // // System.err.println("ComponentNode contains? (" + origX + "," + origY + ")=>(" + eventX + "," + eventY + ")"); // return super.contains(eventX, eventY); return true; //// return contains; } @Override public Cursor getCursor() { if(contains == false || component == null || component.isCursorSet() == false) { return Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR); } return component.getCursor(); } }; protected T component = null; protected boolean visible = true; protected boolean scale = false; // If true, component is scaled. If false, component is resized private transient Font componentFont; private transient double lastScale = 0; @PropertySetter("Bounds") @SyncField("bounds") public void setBounds(Rectangle2D bounds) { assert(bounds != null); this.bounds = bounds; setScaleDirty(); } @PropertySetter("Background Color") @ClientSide public void setBackgroundColor(Color color) { if(component != null) { component.setBackground(color); } } /** * If the size, font or any other scalable visual property of the component * changes, this method should be invoked to make sure that * {@link #render(Graphics2D)} will update the visual properties for the * component with proper scaling. */ protected void setScaleDirty() { // Triggers resetting of component font and size if necessary before // rendering. lastScale = 0; //System.out.println("scale dirty"); } protected Font getComponentFont() { if (component != null) { if (componentFont == null) { componentFont = component.getFont(); } return componentFont; } return null; } protected void setComponentFont(Font font) { this.componentFont = font; setScaleDirty(); component.setFont(font); } @Override public void render(Graphics2D g2d) { if(!visible || bounds == null) return; Graphics2D g = (Graphics2D)g2d.create(); if (component != null) { // see http://java.sun.com/docs/books/tutorial/uiswing/lookandfeel/size.html component.putClientProperty("JComponent.sizeVariant", "large"); double sx = 1; double sy = 1; if (transform != null) { g.transform(transform); // sx = g.getTransform().getScaleX(); // sy = g.getTransform().getScaleY(); // if(!scale) { // g.scale(1/sx, 1/sy); // } } // Assumes proportional scaling always. if (!scale && lastScale != sx) { //System.out.println("NEW SCALE: " + sx + " vs. " + lastScale); lastScale = sx; // Handle font scaling if (componentFont == null) componentFont = component.getFont(); if (componentFont != null) { Font newFont = component.getFont().deriveFont(componentFont.getSize2D()*(float)sx); component.setFont(newFont); } // Handle component size scaling. component.setSize((int)(bounds.getWidth()*sx), (int)(bounds.getHeight()*sy)); } component.setSize((int)(bounds.getWidth()*sx), (int)(bounds.getHeight()*sy)); g.translate(bounds.getMinX()*sx, bounds.getMinY()*sy); component.paint(g); } g.dispose(); } @SyncField({"visible"}) public void setVisible(boolean value) { this.visible = value; } public Component getHeavyweightParent(Component component) { if(component.isLightweight()) return getHeavyweightParent(component.getParent()); else return component; } // FIXME public void handleEvent(AWTEvent event) { if(focusable == false) return; // No event handling if not focusable if(bounds == null) return; // AAARGH.. AWTEvent cevent = event; double sx = 1.0; double sy = 1.0; if(event instanceof MouseEvent) { if(!scale) { IG2DNode node = (IG2DNode) this.getParent(); while(node != null) { sx *= node.getTransform().getScaleX(); sy *= node.getTransform().getScaleY(); node = (G2DParentNode)node.getParent(); // FIXME: it should be G2DParentNode but you can never be sure } } MouseEvent me = (MouseEvent)event; // Use double coordinates if available double mx = me.getX(); double my = me.getY(); if(event instanceof ISGMouseEvent) { mx = ((ISGMouseEvent)event).getDoubleX(); my = ((ISGMouseEvent)event).getDoubleY(); } AffineTransform i = AffineTransform.getTranslateInstance(-transform.getTranslateX()*sx-getBoundsInLocal().getMinX()*sx, -transform.getTranslateY()*sy-getBoundsInLocal().getMinY()*sy); Point2D p = i.transform(new Point2D.Double(sx*mx, sy*my), null); cevent = relocateEvent(me, p); Rectangle2D tb = new Rectangle2D.Double(transform.getTranslateX()+getBoundsInLocal().getMinX(), transform.getTranslateY()+getBoundsInLocal().getMinY(), getBoundsInLocal().getWidth(), getBoundsInLocal().getHeight()); if(cevent.getID() == MouseEvent.MOUSE_PRESSED || cevent.getID() == MouseWheelEvent.MOUSE_WHEEL) { if(tb.contains(me.getPoint())) { G2DFocusManager.INSTANCE.markFocus(component); if(component != null && component.hasFocus() == false) { component.requestFocusInWindow(); } } } if(tb.contains(me.getPoint())) { container.setContains(true); } else { container.setContains(false); } if(cevent.getID() == MouseEvent.MOUSE_DRAGGED && component != null && component.hasFocus()) { cevent.setSource(component); } } if (component != null && container.contains) { //cevent.getSource().equals(component)) { // new Lightwe cevent.setSource(component); // container.dispatchEvent(cevent); // Component hw = getHeavyweightParent(component); // hw.dispatchEvent(cevent); // container.getParent().dispatchEvent(cevent); // System.err.println("cevent=" + cevent); // // FIXME: terrible kludge to dispatch event correctly to every child // for(Component c : component.getComponents()) { // AWTEvent xe = translateEvent(cevent, new Point2D.Double(-c.getLocation().getX(), -c.getLocation().getY())); // xe.setSource(c); // c.dispatchEvent(xe); // if(c instanceof JComponent) { // for(Component c2 : ((JComponent)c).getComponents()) { // AWTEvent qe = translateEvent(xe, new Point2D.Double(-c.getLocation().getX(), -c.getLocation().getY())); // qe.setSource(c); // qe.setSource(c2); // c2.dispatchEvent(qe); // System.err.println("qe=" + qe); // System.err.println("xe=" + xe); // System.err.println("cevent=" + cevent); // } // } // } } } protected AWTEvent relocateEvent(AWTEvent e, Point2D p) { if(!(e instanceof MouseEvent)) return e; // Only for mouse events MouseEvent me = (MouseEvent)e; MouseEvent cevent = null; if(me.getID() == MouseWheelEvent.MOUSE_WHEEL) { 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); } else { cevent = new SGMouseEvent(new DummyComponent(), me.getID(), me.getWhen(), me.getModifiers(), p.getX(), p.getY(), me.getClickCount(), me.isPopupTrigger(), me.getButton(), me); } return cevent; } protected AWTEvent translateEvent(AWTEvent e, Point2D p) { if(!(e instanceof MouseEvent)) return e; // Only for mouse events MouseEvent me = (MouseEvent)e; MouseEvent cevent = null; if(me.getID() == MouseWheelEvent.MOUSE_WHEEL) { 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); } else { 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); } return cevent; } public void setFocusable(boolean focusable) { this.focusable = focusable; container.setContains(false); // Always false when focusable property is changed component.setFocusable(focusable); } @Override public void init() { if(component == null) return; // FIXME: assert maybe? Container rootPane = NodeUtil.findRootPane(this); if(rootPane != null) { rootPane.add(container); } else { throw new AssertionError("The canvas has no rootPane!"); } container.add(component); component.setFocusable(true); component.setIgnoreRepaint(true); UIDefaults cDefaults = new UIDefaults(); component.putClientProperty("Nimbus.Overrides",cDefaults); component.putClientProperty("Nimbus.Overrides.InheritDefaults",false); // component.addMouseListener(new MouseListener() { // // @Override // public void mouseClicked(MouseEvent e) { // System.err.println("aff"); // } // // @Override // public void mousePressed(MouseEvent e) { // System.err.println("aff2"); // } // // @Override // public void mouseReleased(MouseEvent e) { // System.err.println("aff3"); // } // // @Override // public void mouseEntered(MouseEvent e) { // // TODO Auto-generated method stub // // } // // @Override // public void mouseExited(MouseEvent e) { // // TODO Auto-generated method stub // // } // // }); NodeUtil.getEventDelegator(this).addMouseListener(this); NodeUtil.getEventDelegator(this).addMouseMotionListener(this); NodeUtil.getEventDelegator(this).addKeyListener(this); NodeUtil.getEventDelegator(this).addFocusListener(this); } @Override public Rectangle2D getBoundsInLocal() { return bounds; } @Override public void finalize() throws Throwable { cleanup(); super.finalize(); } @Override public void cleanup() { retractMapping(); Container rootPane = NodeUtil.findRootPane(this); if(container != null) { rootPane.remove(container); container.setContains(false); if(container.getParent() != null) container.getParent().remove(container); // eh... if (component != null) container.remove(component); } container = null; component = null; NodeUtil.getEventDelegator(this).removeMouseListener(this); NodeUtil.getEventDelegator(this).removeMouseMotionListener(this); NodeUtil.getEventDelegator(this).removeKeyListener(this); NodeUtil.getEventDelegator(this).removeFocusListener(this); super.cleanup(); } @Override public void keyTyped(KeyEvent e) { handleEvent(e); } @Override public void keyPressed(KeyEvent e) { handleEvent(e); } @Override public void keyReleased(KeyEvent e) { handleEvent(e); } @Override public void mouseDragged(MouseEvent e) { handleEvent(e); } @Override public void mouseMoved(MouseEvent e) { handleEvent(e); } @Override public void mouseClicked(MouseEvent e) { handleEvent(e); } @Override public void mousePressed(MouseEvent e) { handleEvent(e); } @Override public void mouseReleased(MouseEvent e) { handleEvent(e); } @Override public void mouseEntered(MouseEvent e) { handleEvent(e); } @Override public void mouseExited(MouseEvent e) { handleEvent(e); } @Override public void focusGained(FocusEvent e) { handleEvent(e); } @Override public void focusLost(FocusEvent e) { handleEvent(e); } }