X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.scenegraph.swing%2Fsrc%2Forg%2Fsimantics%2Fscenegraph%2Fswing%2FComponentNode.java;fp=bundles%2Forg.simantics.scenegraph.swing%2Fsrc%2Forg%2Fsimantics%2Fscenegraph%2Fswing%2FComponentNode.java;h=c9cb8bc29addafa88a6c3e3c8f77baa82a3896be;hp=0000000000000000000000000000000000000000;hb=969bd23cab98a79ca9101af33334000879fb60c5;hpb=866dba5cd5a3929bbeae85991796acb212338a08 diff --git a/bundles/org.simantics.scenegraph.swing/src/org/simantics/scenegraph/swing/ComponentNode.java b/bundles/org.simantics.scenegraph.swing/src/org/simantics/scenegraph/swing/ComponentNode.java new file mode 100644 index 000000000..c9cb8bc29 --- /dev/null +++ b/bundles/org.simantics.scenegraph.swing/src/org/simantics/scenegraph/swing/ComponentNode.java @@ -0,0 +1,538 @@ +/******************************************************************************* + * 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); + } +}