X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.scenegraph%2Fsrc%2Forg%2Fsimantics%2Fscenegraph%2Fg2d%2Fnodes%2FNavigationNode.java;fp=bundles%2Forg.simantics.scenegraph%2Fsrc%2Forg%2Fsimantics%2Fscenegraph%2Fg2d%2Fnodes%2FNavigationNode.java;h=b46b4ad61c81089ff3f022cc6946bf299bf52d74;hb=0ae2b770234dfc3cbb18bd38f324125cf0faca07;hp=e51ff35adec4c3ee6aca49d815a50777549eeafc;hpb=24e2b34260f219f0d1644ca7a138894980e25b14;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/NavigationNode.java b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/NavigationNode.java index e51ff35ad..b46b4ad61 100644 --- a/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/NavigationNode.java +++ b/bundles/org.simantics.scenegraph/src/org/simantics/scenegraph/g2d/nodes/NavigationNode.java @@ -1,42 +1,42 @@ -/******************************************************************************* - * 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 - *******************************************************************************/ +/******************************************************************************* + * 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.g2d.nodes; - -import java.awt.Graphics2D; -import java.awt.Rectangle; -import java.awt.geom.AffineTransform; -import java.awt.geom.Point2D; -import java.awt.geom.Rectangle2D; -import java.beans.PropertyChangeEvent; -import java.beans.PropertyChangeListener; -import java.util.concurrent.ScheduledFuture; - -import org.simantics.scenegraph.g2d.events.EventTypes; -import org.simantics.scenegraph.g2d.events.MouseEvent; -import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent; -import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent; -import org.simantics.scenegraph.g2d.events.MouseEvent.MouseWheelMovedEvent; -import org.simantics.scenegraph.utils.GeometryUtils; -import org.simantics.scenegraph.utils.Quality; -import org.simantics.scenegraph.utils.QualityHints; -import org.simantics.utils.threads.AWTThread; -import org.simantics.utils.threads.Executable; -import org.simantics.utils.threads.ExecutorWorker; - -/** - * @author Tuukka Lehtonen - */ + +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.concurrent.ScheduledFuture; + +import org.simantics.scenegraph.g2d.events.EventTypes; +import org.simantics.scenegraph.g2d.events.MouseEvent; +import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent; +import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent; +import org.simantics.scenegraph.g2d.events.MouseEvent.MouseWheelMovedEvent; +import org.simantics.scenegraph.utils.GeometryUtils; +import org.simantics.scenegraph.utils.Quality; +import org.simantics.scenegraph.utils.QualityHints; +import org.simantics.utils.threads.AWTThread; +import org.simantics.utils.threads.Executable; +import org.simantics.utils.threads.ExecutorWorker; + +/** + * @author Tuukka Lehtonen + */ public class NavigationNode extends TransformNode implements PropertyChangeListener { - + public interface TransformListener { void transformChanged(AffineTransform transform); } @@ -55,39 +55,39 @@ public class NavigationNode extends TransformNode implements PropertyChangeListe protected Boolean navigationEnabled = Boolean.TRUE; - protected Boolean zoomEnabled = Boolean.TRUE; - - protected Quality lowQualityMode = Quality.LOW; - protected Quality highQualityMode = Quality.HIGH; - - /** - * The rendering quality used when {@link #dynamicQuality} is false. - */ - protected Quality staticQualityMode = Quality.LOW; - - protected Boolean dynamicQuality = Boolean.TRUE; - - private TransformListener transformListener = null; - - private static final int REPAINT_DELAY = 250; - private transient boolean qualityPaint = true; - private transient ScheduledFuture pendingTask; - - protected transient Point2D dragDelta = null; - transient Rectangle r = new Rectangle(); - protected transient Rectangle2D performZoomTo = null; - - @Override - public void init() { - super.init(); - addEventHandler(this); - } - - @Override - public void cleanup() { - removeEventHandler(this); - super.cleanup(); - } + protected Boolean zoomEnabled = Boolean.TRUE; + + protected Quality lowQualityMode = Quality.LOW; + protected Quality highQualityMode = Quality.HIGH; + + /** + * The rendering quality used when {@link #dynamicQuality} is false. + */ + protected Quality staticQualityMode = Quality.LOW; + + protected Boolean dynamicQuality = Boolean.TRUE; + + private TransformListener transformListener = null; + + private static final int REPAINT_DELAY = 250; + private transient boolean qualityPaint = true; + private transient ScheduledFuture pendingTask; + + protected transient Point2D dragDelta = null; + transient Rectangle r = new Rectangle(); + protected transient Rectangle2D performZoomTo = null; + + @Override + public void init() { + super.init(); + addEventHandler(this); + } + + @Override + public void cleanup() { + removeEventHandler(this); + super.cleanup(); + } @SyncField("bounds") protected void setBounds(Rectangle2D bounds) { @@ -108,51 +108,51 @@ public class NavigationNode extends TransformNode implements PropertyChangeListe @SyncField("navigationEnabled") public void setNavigationEnabled(Boolean navigationEnabled) { this.navigationEnabled = navigationEnabled; - } - - @SyncField("zoomEnabled") - public void setZoomEnabled(Boolean zoomEnabled) { - this.zoomEnabled = zoomEnabled; - } - - @SyncField("lowQualityMode") - public void setLowQualityMode(Quality mode) { - this.lowQualityMode = mode; - } - - @SyncField("highQualityMode") - public void setHighQualityMode(Quality mode) { - this.highQualityMode = mode; - } - - /** - * @param mode a quality to define a static quality mode that is used when - * {@link #dynamicQuality} is false or null to undefine - * static quality mode - */ - @SyncField("staticQualityMode") - public void setStaticQualityMode(Quality mode) { - this.staticQualityMode = mode; - } - - public Quality getStaticQualityMode() { - return staticQualityMode; - } - - /** - * With dynamic quality rendering will proceed with low quality settings - * during interaction or when instructed to do so through Graphics2D - * rendering hints. Without dynamic quality rendering will always proceed in - * the mode set with {@link #setStaticQualityMode(Quality)}. If swtatic - * quality mode is not set (i.e. null), rendering will proceed - * with whatever settings are in the Graphics2D instance at that time. - * - * @param dynamicQuality - */ - @SyncField("dynamicQuality") - public void setDynamicQuality(Boolean dynamicQuality) { - this.dynamicQuality = dynamicQuality; - } + } + + @SyncField("zoomEnabled") + public void setZoomEnabled(Boolean zoomEnabled) { + this.zoomEnabled = zoomEnabled; + } + + @SyncField("lowQualityMode") + public void setLowQualityMode(Quality mode) { + this.lowQualityMode = mode; + } + + @SyncField("highQualityMode") + public void setHighQualityMode(Quality mode) { + this.highQualityMode = mode; + } + + /** + * @param mode a quality to define a static quality mode that is used when + * {@link #dynamicQuality} is false or null to undefine + * static quality mode + */ + @SyncField("staticQualityMode") + public void setStaticQualityMode(Quality mode) { + this.staticQualityMode = mode; + } + + public Quality getStaticQualityMode() { + return staticQualityMode; + } + + /** + * With dynamic quality rendering will proceed with low quality settings + * during interaction or when instructed to do so through Graphics2D + * rendering hints. Without dynamic quality rendering will always proceed in + * the mode set with {@link #setStaticQualityMode(Quality)}. If swtatic + * quality mode is not set (i.e. null), rendering will proceed + * with whatever settings are in the Graphics2D instance at that time. + * + * @param dynamicQuality + */ + @SyncField("dynamicQuality") + public void setDynamicQuality(Boolean dynamicQuality) { + this.dynamicQuality = dynamicQuality; + } public boolean isVisible() { return visible; @@ -221,7 +221,7 @@ public class NavigationNode extends TransformNode implements PropertyChangeListe } @Override - public void render(Graphics2D g2d) { + public void render(Graphics2D g2d) { Rectangle newBounds = g2d.getClipBounds(r); if (!newBounds.equals(bounds)) { if (bounds != null) { @@ -236,34 +236,34 @@ public class NavigationNode extends TransformNode implements PropertyChangeListe } setBounds(newBounds); // FIXME: not very good idea to send bounds to server } - if (bounds != null && performZoomTo != null) { + if (bounds != null && performZoomTo != null) { setTransform(GeometryUtils.fitArea(bounds, performZoomTo)); performZoomTo = null; transformChanged(); - } - - if (visible) { - QualityHints origQualityHints = null; - - Quality mode = null; - if (dynamicQuality) { - mode = qualityPaint ? highQualityMode : lowQualityMode; - } else if (staticQualityMode != null) { - mode = staticQualityMode; - } - - if (mode != null) { - QualityHints qualityHints = QualityHints.getHints(mode); - if (qualityHints != null) { - origQualityHints = QualityHints.getQuality(g2d); - qualityHints.setQuality(g2d); - } - } - - super.render(g2d); - - if (origQualityHints != null) - origQualityHints.setQuality(g2d); + } + + if (visible) { + QualityHints origQualityHints = null; + + Quality mode = null; + if (dynamicQuality) { + mode = qualityPaint ? highQualityMode : lowQualityMode; + } else if (staticQualityMode != null) { + mode = staticQualityMode; + } + + if (mode != null) { + QualityHints qualityHints = QualityHints.getHints(mode); + if (qualityHints != null) { + origQualityHints = QualityHints.getQuality(g2d); + qualityHints.setQuality(g2d); + } + } + + super.render(g2d); + + if (origQualityHints != null) + origQualityHints.setQuality(g2d); } } @@ -274,12 +274,12 @@ public class NavigationNode extends TransformNode implements PropertyChangeListe @Override public String toString() { - return super.toString() + " [visible=" + visible + ", bounds=" + bounds + ", zoomInLimit=" + zoomInLimit - + ", zoomOutLimit=" + zoomOutLimit + ", adaptViewportToResize=" + adaptViewportToResizedControl + "]"; + return super.toString() + " [visible=" + visible + ", bounds=" + bounds + ", zoomInLimit=" + zoomInLimit + + ", zoomOutLimit=" + zoomOutLimit + ", adaptViewportToResize=" + adaptViewportToResizedControl + "]"; } private void transformChanged() { - if (transformListener != null) { + if (transformListener != null) { transformListener.transformChanged(transform); } } @@ -290,105 +290,105 @@ public class NavigationNode extends TransformNode implements PropertyChangeListe @Override public void propertyChange(PropertyChangeEvent evt) { - if (transformListener != null && "transform".equals(evt.getPropertyName())) { + if (transformListener != null && "transform".equals(evt.getPropertyName())) { transformListener.transformChanged((AffineTransform)evt.getNewValue()); } - } - - @Override - public boolean mouseWheelMoved(MouseWheelMovedEvent me) { - if (navigationEnabled && zoomEnabled) { - double scroll = Math.min(0.9, -me.wheelRotation / 20.0); - double z = 1 - scroll; - double dx = (me.controlPosition.getX() - transform.getTranslateX()) / transform.getScaleX(); - double dy = (me.controlPosition.getY() - transform.getTranslateY()) / transform.getScaleY(); - dx = dx * (1 - z); - dy = dy * (1 - z); - double limitedScale = limitScaleFactor(z); - if (limitedScale != 1.0) { - translate(dx, dy); - scale(z, z); - transformChanged(); - dropQuality(); - repaint(); - } - } - return false; - } - - @Override - public boolean mouseButtonPressed(MouseButtonPressedEvent e) { - if (navigationEnabled) { - if (isPanState(e)) { - dragDelta = new Point2D.Double(e.controlPosition.getX(), e.controlPosition.getY()); - // TODO : why to repaint here? Mouse has not been dragged, so it is not necessary, an causes unnecessary delay in start of panning movement. - //repaint(); - //return true; // hmm.. why? - } - } - return false; - } - - @Override - public boolean mouseMoved(MouseMovedEvent e) { - if (navigationEnabled && dragDelta != null) { - if (isPanState(e)) { - double x = (e.controlPosition.getX() - dragDelta.getX()) / transform.getScaleX(); - double y = (e.controlPosition.getY() - dragDelta.getY()) / transform.getScaleY(); - translate(x, y); - transformChanged(); - dragDelta = new Point2D.Double(e.controlPosition.getX(), e.controlPosition.getY()); - dropQuality(); - repaint(); - return true; - } - } - return false; - } - - protected boolean isPanState(MouseEvent e) { - boolean anyPanButton = e.hasAnyButton(MouseEvent.MIDDLE_MASK | MouseEvent.RIGHT_MASK); - boolean middle = e.hasAnyButton(MouseEvent.MIDDLE_MASK); - boolean shift = e.hasAnyModifier(MouseEvent.SHIFT_MASK); - return middle || (anyPanButton && shift); - } - - /** - * Utility method for dropping the paint quality and scheduling repaint with good quality. - * This can be used to speed up rendering while navigating. - */ - private void dropQuality() { - if (!dynamicQuality) return; - //System.out.println("dropQuality: " + qualityPaint); - if (pendingTask!=null) { - //System.out.println("cancel quality task"); - pendingTask.cancel(false); - pendingTask = null; - } - // Render with better quality soon. - qualityPaint = false; - scheduleRepaint(); - } - - private void scheduleRepaint() { - //System.out.println("schedule quality improvement"); - Executable exe = new Executable(AWTThread.getThreadAccess(), new Runnable() { - @Override - public void run() { - //System.out.println("run: " + qualityPaint); - // we have waited for [delay], now its time to render with good quality - // Render next time with good quality - qualityPaint = true; - repaint(); - } - }); - // Render with good quality later - pendingTask = ExecutorWorker.getInstance().timerExec(exe, REPAINT_DELAY); - } - - @Override - public int getEventMask() { - return EventTypes.MouseMovedMask | EventTypes.MouseButtonPressedMask | EventTypes.MouseWheelMask; - } + } + + @Override + public boolean mouseWheelMoved(MouseWheelMovedEvent me) { + if (navigationEnabled && zoomEnabled) { + double scroll = Math.min(0.9, -me.wheelRotation / 20.0); + double z = 1 - scroll; + double dx = (me.controlPosition.getX() - transform.getTranslateX()) / transform.getScaleX(); + double dy = (me.controlPosition.getY() - transform.getTranslateY()) / transform.getScaleY(); + dx = dx * (1 - z); + dy = dy * (1 - z); + double limitedScale = limitScaleFactor(z); + if (limitedScale != 1.0) { + translate(dx, dy); + scale(z, z); + transformChanged(); + dropQuality(); + repaint(); + } + } + return false; + } + + @Override + public boolean mouseButtonPressed(MouseButtonPressedEvent e) { + if (navigationEnabled) { + if (isPanState(e)) { + dragDelta = new Point2D.Double(e.controlPosition.getX(), e.controlPosition.getY()); + // TODO : why to repaint here? Mouse has not been dragged, so it is not necessary, an causes unnecessary delay in start of panning movement. + //repaint(); + //return true; // hmm.. why? + } + } + return false; + } + + @Override + public boolean mouseMoved(MouseMovedEvent e) { + if (navigationEnabled && dragDelta != null) { + if (isPanState(e)) { + double x = (e.controlPosition.getX() - dragDelta.getX()) / transform.getScaleX(); + double y = (e.controlPosition.getY() - dragDelta.getY()) / transform.getScaleY(); + translate(x, y); + transformChanged(); + dragDelta = new Point2D.Double(e.controlPosition.getX(), e.controlPosition.getY()); + dropQuality(); + repaint(); + return true; + } + } + return false; + } + + protected boolean isPanState(MouseEvent e) { + boolean anyPanButton = e.hasAnyButton(MouseEvent.MIDDLE_MASK | MouseEvent.RIGHT_MASK); + boolean middle = e.hasAnyButton(MouseEvent.MIDDLE_MASK); + boolean shift = e.hasAnyModifier(MouseEvent.SHIFT_MASK); + return middle || (anyPanButton && shift); + } + + /** + * Utility method for dropping the paint quality and scheduling repaint with good quality. + * This can be used to speed up rendering while navigating. + */ + private void dropQuality() { + if (!dynamicQuality) return; + //System.out.println("dropQuality: " + qualityPaint); + if (pendingTask!=null) { + //System.out.println("cancel quality task"); + pendingTask.cancel(false); + pendingTask = null; + } + // Render with better quality soon. + qualityPaint = false; + scheduleRepaint(); + } + + private void scheduleRepaint() { + //System.out.println("schedule quality improvement"); + Executable exe = new Executable(AWTThread.getThreadAccess(), new Runnable() { + @Override + public void run() { + //System.out.println("run: " + qualityPaint); + // we have waited for [delay], now its time to render with good quality + // Render next time with good quality + qualityPaint = true; + repaint(); + } + }); + // Render with good quality later + pendingTask = ExecutorWorker.getInstance().timerExec(exe, REPAINT_DELAY); + } + + @Override + public int getEventMask() { + return EventTypes.MouseMovedMask | EventTypes.MouseButtonPressedMask | EventTypes.MouseWheelMask; + } }