-/*******************************************************************************\r
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management\r
- * in Industry THTH ry.\r
- * All rights reserved. This program and the accompanying materials\r
- * are made available under the terms of the Eclipse Public License v1.0\r
- * which accompanies this distribution, and is available at\r
- * http://www.eclipse.org/legal/epl-v10.html\r
- *\r
- * Contributors:\r
- * VTT Technical Research Centre of Finland - initial API and implementation\r
- *******************************************************************************/\r
+/*******************************************************************************
+ * 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;
-\r
-import java.awt.Graphics2D;\r
-import java.awt.Rectangle;\r
-import java.awt.geom.AffineTransform;\r
-import java.awt.geom.Point2D;\r
-import java.awt.geom.Rectangle2D;\r
-import java.beans.PropertyChangeEvent;\r
-import java.beans.PropertyChangeListener;\r
-import java.util.concurrent.ScheduledFuture;\r
-\r
-import org.simantics.scenegraph.g2d.events.EventTypes;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseButtonPressedEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseMovedEvent;\r
-import org.simantics.scenegraph.g2d.events.MouseEvent.MouseWheelMovedEvent;\r
-import org.simantics.scenegraph.utils.GeometryUtils;\r
-import org.simantics.scenegraph.utils.Quality;\r
-import org.simantics.scenegraph.utils.QualityHints;\r
-import org.simantics.utils.threads.AWTThread;\r
-import org.simantics.utils.threads.Executable;\r
-import org.simantics.utils.threads.ExecutorWorker;\r
-
-/**\r
- * @author Tuukka Lehtonen\r
- */\r
+
+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 {
-\r
+
public interface TransformListener {
void transformChanged(AffineTransform transform);
}
protected Boolean navigationEnabled = Boolean.TRUE;
- protected Boolean zoomEnabled = Boolean.TRUE;\r
-\r
- protected Quality lowQualityMode = Quality.LOW;\r
- protected Quality highQualityMode = Quality.HIGH;\r
-\r
- /**\r
- * The rendering quality used when {@link #dynamicQuality} is false.\r
- */\r
- protected Quality staticQualityMode = Quality.LOW;\r
-\r
- protected Boolean dynamicQuality = Boolean.TRUE;\r
-\r
- private TransformListener transformListener = null;\r
-\r
- private static final int REPAINT_DELAY = 250;\r
- private transient boolean qualityPaint = true;\r
- private transient ScheduledFuture<Object> pendingTask;\r
-\r
- protected transient Point2D dragDelta = null;\r
- transient Rectangle r = new Rectangle();\r
- protected transient Rectangle2D performZoomTo = null;\r
-
- @Override\r
- public void init() {\r
- super.init();\r
- addEventHandler(this);\r
- }\r
-\r
- @Override\r
- public void cleanup() {\r
- removeEventHandler(this);\r
- super.cleanup();\r
- }\r
+ 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<Object> 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) {
@SyncField("navigationEnabled")
public void setNavigationEnabled(Boolean navigationEnabled) {
this.navigationEnabled = navigationEnabled;
- }\r
-
- @SyncField("zoomEnabled")\r
- public void setZoomEnabled(Boolean zoomEnabled) {\r
- this.zoomEnabled = zoomEnabled;\r
- }\r
-\r
- @SyncField("lowQualityMode")\r
- public void setLowQualityMode(Quality mode) {\r
- this.lowQualityMode = mode;\r
- }\r
-\r
- @SyncField("highQualityMode")\r
- public void setHighQualityMode(Quality mode) {\r
- this.highQualityMode = mode;\r
- }\r
-\r
- /**\r
- * @param mode a quality to define a static quality mode that is used when\r
- * {@link #dynamicQuality} is false or <code>null</code> to undefine\r
- * static quality mode\r
- */\r
- @SyncField("staticQualityMode")\r
- public void setStaticQualityMode(Quality mode) {\r
- this.staticQualityMode = mode;\r
- }\r
-\r
- public Quality getStaticQualityMode() {\r
- return staticQualityMode;\r
- }\r
-\r
- /**\r
- * With dynamic quality rendering will proceed with low quality settings\r
- * during interaction or when instructed to do so through Graphics2D\r
- * rendering hints. Without dynamic quality rendering will always proceed in\r
- * the mode set with {@link #setStaticQualityMode(Quality)}. If swtatic\r
- * quality mode is not set (i.e. <code>null</code>), rendering will proceed\r
- * with whatever settings are in the Graphics2D instance at that time.\r
- * \r
- * @param dynamicQuality\r
- */\r
- @SyncField("dynamicQuality")\r
- public void setDynamicQuality(Boolean dynamicQuality) {\r
- this.dynamicQuality = dynamicQuality;\r
- }\r
+ }
+
+ @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 <code>null</code> 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. <code>null</code>), 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;
}
@Override
- public void render(Graphics2D g2d) {\r
+ public void render(Graphics2D g2d) {
Rectangle newBounds = g2d.getClipBounds(r);
if (!newBounds.equals(bounds)) {
if (bounds != null) {
}
setBounds(newBounds); // FIXME: not very good idea to send bounds to server
}
- if (bounds != null && performZoomTo != null) {\r
+ if (bounds != null && performZoomTo != null) {
setTransform(GeometryUtils.fitArea(bounds, performZoomTo));
performZoomTo = null;
transformChanged();
- }\r
-\r
- if (visible) {\r
- QualityHints origQualityHints = null;\r
-\r
- Quality mode = null;\r
- if (dynamicQuality) {\r
- mode = qualityPaint ? highQualityMode : lowQualityMode;\r
- } else if (staticQualityMode != null) {\r
- mode = staticQualityMode;\r
- }\r
-\r
- if (mode != null) {\r
- QualityHints qualityHints = QualityHints.getHints(mode);\r
- if (qualityHints != null) {\r
- origQualityHints = QualityHints.getQuality(g2d);\r
- qualityHints.setQuality(g2d);\r
- }\r
- }\r
-\r
- super.render(g2d);\r
-\r
- if (origQualityHints != null)\r
- origQualityHints.setQuality(g2d);\r
+ }
+
+ 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);
}
}
@Override
public String toString() {
- return super.toString() + " [visible=" + visible + ", bounds=" + bounds + ", zoomInLimit=" + zoomInLimit\r
- + ", zoomOutLimit=" + zoomOutLimit + ", adaptViewportToResize=" + adaptViewportToResizedControl + "]";\r
+ return super.toString() + " [visible=" + visible + ", bounds=" + bounds + ", zoomInLimit=" + zoomInLimit
+ + ", zoomOutLimit=" + zoomOutLimit + ", adaptViewportToResize=" + adaptViewportToResizedControl + "]";
}
private void transformChanged() {
- if (transformListener != null) {\r
+ if (transformListener != null) {
transformListener.transformChanged(transform);
}
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
- if (transformListener != null && "transform".equals(evt.getPropertyName())) {\r
+ if (transformListener != null && "transform".equals(evt.getPropertyName())) {
transformListener.transformChanged((AffineTransform)evt.getNewValue());
}
- }\r
-\r
- @Override\r
- public boolean mouseWheelMoved(MouseWheelMovedEvent me) {\r
- if (navigationEnabled && zoomEnabled) {\r
- double scroll = Math.min(0.9, -me.wheelRotation / 20.0);\r
- double z = 1 - scroll;\r
- double dx = (me.controlPosition.getX() - transform.getTranslateX()) / transform.getScaleX();\r
- double dy = (me.controlPosition.getY() - transform.getTranslateY()) / transform.getScaleY();\r
- dx = dx * (1 - z);\r
- dy = dy * (1 - z);\r
- double limitedScale = limitScaleFactor(z);\r
- if (limitedScale != 1.0) {\r
- translate(dx, dy);\r
- scale(z, z);\r
- transformChanged();\r
- dropQuality();\r
- repaint();\r
- }\r
- }\r
- return false;\r
- }\r
-\r
- @Override\r
- public boolean mouseButtonPressed(MouseButtonPressedEvent e) {\r
- if (navigationEnabled) {\r
- if (isPanState(e)) {\r
- dragDelta = new Point2D.Double(e.controlPosition.getX(), e.controlPosition.getY());\r
- // TODO : why to repaint here? Mouse has not been dragged, so it is not necessary, an causes unnecessary delay in start of panning movement.\r
- //repaint();\r
- //return true; // hmm.. why?\r
- }\r
- }\r
- return false;\r
- }\r
-\r
- @Override\r
- public boolean mouseMoved(MouseMovedEvent e) {\r
- if (navigationEnabled && dragDelta != null) {\r
- if (isPanState(e)) {\r
- double x = (e.controlPosition.getX() - dragDelta.getX()) / transform.getScaleX();\r
- double y = (e.controlPosition.getY() - dragDelta.getY()) / transform.getScaleY();\r
- translate(x, y);\r
- transformChanged();\r
- dragDelta = new Point2D.Double(e.controlPosition.getX(), e.controlPosition.getY());\r
- dropQuality();\r
- repaint();\r
- return true;\r
- }\r
- }\r
- return false;\r
- }\r
-\r
- protected boolean isPanState(MouseEvent e) {\r
- boolean anyPanButton = e.hasAnyButton(MouseEvent.MIDDLE_MASK | MouseEvent.RIGHT_MASK);\r
- boolean middle = e.hasAnyButton(MouseEvent.MIDDLE_MASK);\r
- boolean shift = e.hasAnyModifier(MouseEvent.SHIFT_MASK);\r
- return middle || (anyPanButton && shift);\r
- }\r
-\r
- /**\r
- * Utility method for dropping the paint quality and scheduling repaint with good quality.\r
- * This can be used to speed up rendering while navigating.\r
- */\r
- private void dropQuality() {\r
- if (!dynamicQuality) return;\r
- //System.out.println("dropQuality: " + qualityPaint);\r
- if (pendingTask!=null) {\r
- //System.out.println("cancel quality task");\r
- pendingTask.cancel(false);\r
- pendingTask = null;\r
- }\r
- // Render with better quality soon.\r
- qualityPaint = false;\r
- scheduleRepaint();\r
- }\r
-\r
- private void scheduleRepaint() {\r
- //System.out.println("schedule quality improvement");\r
- Executable exe = new Executable(AWTThread.getThreadAccess(), new Runnable() {\r
- @Override\r
- public void run() {\r
- //System.out.println("run: " + qualityPaint);\r
- // we have waited for [delay], now its time to render with good quality\r
- // Render next time with good quality\r
- qualityPaint = true;\r
- repaint();\r
- }\r
- });\r
- // Render with good quality later\r
- pendingTask = ExecutorWorker.getInstance().timerExec(exe, REPAINT_DELAY);\r
- }\r
-\r
- @Override\r
- public int getEventMask() {\r
- return EventTypes.MouseMovedMask | EventTypes.MouseButtonPressedMask | EventTypes.MouseWheelMask;\r
- }\r
+ }
+
+ @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;
+ }
}