/*******************************************************************************
- * Copyright (c) 2007, 2010 Association for Decentralized Information Management
+ * Copyright (c) 2007, 2018 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
import org.simantics.g2d.canvas.impl.HintReflection.HintListener;
import org.simantics.g2d.canvas.impl.SGNodeReflection.SGCleanup;
import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit;
+import org.simantics.g2d.participant.RulerPainter;
import org.simantics.g2d.utils.Alignment;
import org.simantics.layer0.Layer0;
import org.simantics.modeling.ModelingResources;
import org.simantics.scenegraph.g2d.G2DParentNode;
+import org.simantics.scenegraph.utils.DPIUtil;
import org.simantics.structural.stubs.StructuralResource2;
import org.simantics.utils.datastructures.hints.IHintContext;
import org.simantics.utils.datastructures.hints.IHintContext.Key;
Resource input;
IsInActiveModelListener listener;
TextNode bannerNode;
+ Rectangle2D bounds;
+ boolean rulerVisible = false;
+ double rulerSize = 0;
public DiagramModelActivityTracker(Resource input) {
if (input == null)
@HintListener(Class=Hints.class, Field="KEY_CONTROL_BOUNDS")
public void controlBoundsChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
+ bounds = (Rectangle2D) newValue;
+ resetBannerNode(bounds, rulerVisible, rulerSize);
+ }
+
+ @HintListener(Class=RulerPainter.class, Field="KEY_RULER_ENABLED")
+ public void rulerToggled(IHintObservable sender, Key key, Object oldValue, Object newValue) {
+ rulerVisible = Boolean.TRUE.equals(newValue);
+ resetBannerNode(bounds, rulerVisible, rulerSize);
+ }
+
+ @HintListener(Class=RulerPainter.class, Field="KEY_RULER_SIZE")
+ public void rulerSizeChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) {
+ rulerSize = newValue != null ? (Double) newValue : 0.0;
+ resetBannerNode(bounds, rulerVisible, rulerSize);
+ }
+
+ private void resetBannerNode(Rectangle2D bounds, boolean rulerVisible, double rulerSize) {
if (bannerNode == null)
return;
- if (newValue != null) {
- Rectangle2D bounds = (Rectangle2D) newValue;
+ if (bounds != null) {
AffineTransform at = new AffineTransform();
at.translate(5,5);
-// at.translate(bounds.getWidth() / 2, bounds.getHeight() / 2);
-// at.scale(2, 2);
-// at.rotate(-Math.PI/6);
-// at.translate(bounds.getWidth() - 150, bounds.getHeight() - 30);
-// at.scale(1, 1);
+ if (rulerVisible) {
+ double s = DPIUtil.upscale(rulerSize);
+ at.translate(s, s);
+ }
bannerNode.setTransform(at);
+ bannerNode.setColor(Color.BLACK);
} else {
// Disable rendering
bannerNode.setColor(null);
public void addedToContext(ICanvasContext ctx) {
super.addedToContext(ctx);
+ rulerVisible = Boolean.TRUE.equals(getHint(RulerPainter.KEY_RULER_ENABLED));
+ Double rs = getHint(RulerPainter.KEY_RULER_SIZE);
+ rulerSize = rs != null ? rs : 0;
+
listener = new IsInActiveModelListener(ctx);
Simantics.getSession().async(new IsActiveDiagram(input), listener);
bannerNode.setVerticalAlignment((byte) Alignment.LEADING.ordinal());
//bannerNode.setText("Model is not active.");
bannerNode.setText("");
- bannerNode.setFont(Font.decode("Arial 16"));
+ int fontSize = DPIUtil.upscale(16);
+ bannerNode.setFont(new Font("Tahoma", Font.PLAIN, fontSize));
//bannerNode.setBackgroundColor(new Color(192, 192, 192, 128));
bannerNode.setPadding(10, 10);
bannerNode.setBorderColor(Color.GRAY);
bannerNode.setBorderWidth(0);
bannerNode.setEditable(false);
+ bannerNode.setColor(Color.BLACK);
}
@SGCleanup
public static final Key KEY_RULER_ENABLED = new KeyOf(Boolean.class, "RULER_ENABLED");
+ /**
+ * Size of ruler in normalized (non-zoomed or 100% zoom) coordinates, not screen pixel coordinates.
+ */
+ public static final Key KEY_RULER_SIZE = new KeyOf(Double.class, "RULER_SIZE");
+
/** Background color */
public static final Key KEY_RULER_BACKGROUND_COLOR = new KeyOf(Color.class, "RULER_BACKGROUND_COLOR");
@Override
public void addedToContext(ICanvasContext ctx) {
super.addedToContext(ctx);
+
+ if (!hasHint(KEY_RULER_SIZE))
+ setHint(KEY_RULER_SIZE, RULER_WIDTH);
+
getHintStack().addKeyHintListener(getThread(), KEY_RULER_ENABLED, hintListener);
+ getHintStack().addKeyHintListener(getThread(), KEY_RULER_SIZE, hintListener);
getHintStack().addKeyHintListener(getThread(), KEY_RULER_BACKGROUND_COLOR, hintListener);
getHintStack().addKeyHintListener(getThread(), KEY_RULER_TEXT_COLOR, hintListener);
getHintStack().addKeyHintListener(getThread(), GridPainter.KEY_GRID_SIZE, hintListener);
@Override
public void removedFromContext(ICanvasContext ctx) {
getHintStack().removeKeyHintListener(getThread(), KEY_RULER_ENABLED, hintListener);
+ getHintStack().removeKeyHintListener(getThread(), KEY_RULER_SIZE, hintListener);
getHintStack().removeKeyHintListener(getThread(), KEY_RULER_BACKGROUND_COLOR, hintListener);
getHintStack().removeKeyHintListener(getThread(), KEY_RULER_TEXT_COLOR, hintListener);
getHintStack().removeKeyHintListener(getThread(), GridPainter.KEY_GRID_SIZE, hintListener);
protected void updateNode() {
node.setEnabled(isPaintingEnabled());
node.setGridSize(getGridSize());
+ node.setRulerSize(getRulerSize());
}
private double getGridSize() {
return !Boolean.FALSE.equals(b);
}
+ private double getRulerSize() {
+ Double d = getHint(KEY_RULER_SIZE);
+ return d != null ? d : RULER_WIDTH;
+ }
+
public Color getRulerTextColor()
{
Color c = getHint(KEY_RULER_TEXT_COLOR);
import java.util.Locale;
import org.simantics.scenegraph.g2d.G2DNode;
+import org.simantics.scenegraph.utils.DPIUtil;
import org.simantics.scenegraph.utils.GridUtils;
public class RulerNode extends G2DNode {
*/
private static final long serialVersionUID = 2490944880914577411L;
- /**
- * FIXME: this is a hack for the map UI that has to be solved some other way.
- */
- private static final boolean MAP_Y_SCALING = false;
-
private static final Color GRAY = new Color(100, 100, 100);
protected Boolean enabled = true;
protected double gridSize = 1.0;
+ protected double rulerSize = 20;
+
@SyncField("enabled")
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
this.gridSize = gridSize;
}
+ @SyncField("rulerSize")
+ public void setRulerSize(double rulerSize) {
+ this.rulerSize = rulerSize;
+ }
+
@Override
public void render(Graphics2D g) {
if (!enabled)
return;
-
+
AffineTransform tr = g.getTransform();
double scaleX = Math.abs(tr.getScaleX());
double scaleY = Math.abs(tr.getScaleY());
double offsetY = tr.getTranslateY();
g.setTransform(new AffineTransform());
- Font rulerFont = new Font("Tahoma", Font.PLAIN, 9);
+ Font rulerFont = new Font("Tahoma", Font.PLAIN, DPIUtil.upscale(9));
//g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setStroke(new BasicStroke(1));
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.8f));
- Rectangle2D vertical = new Rectangle2D.Double(bounds.getMinX(), bounds.getMinY(), bounds.getMaxX(), bounds.getMinY()+20);
+ int rulerPixelSize = (int) DPIUtil.upscale(rulerSize);
+ int u7 = DPIUtil.upscale(7);
+ int u4 = DPIUtil.upscale(4);
+ int u2 = DPIUtil.upscale(2);
+
+ Rectangle2D vertical = new Rectangle2D.Double(bounds.getMinX(), bounds.getMinY(), bounds.getMaxX(), bounds.getMinY()+rulerPixelSize);
g.fill(vertical);
- Rectangle2D horizontal = new Rectangle2D.Double(bounds.getMinX(), bounds.getMinY()+20, bounds.getMinX()+20, bounds.getMaxY());
+ Rectangle2D horizontal = new Rectangle2D.Double(bounds.getMinX(), bounds.getMinY()+rulerPixelSize, bounds.getMinX()+rulerPixelSize, bounds.getMaxY());
g.fill(horizontal);
// stepX and stepY should be something between 50 and 100
g.setColor(GRAY);
g.setFont(rulerFont);
+ FontMetrics fm = g.getFontMetrics();
double previousText = -100;
// Vertical ruler
for(double x = offsetX%stepX-stepX; x < bounds.getMaxX(); x+=stepX) {
- if(x > 20) {
+ if(x > rulerPixelSize) {
double val = (x-offsetX)/scaleX / getTransform().getScaleX();
double modifiedValue = modifyHorizontalValue(val);
String str = formatValue(modifiedValue, getMaxDigits());
- FontMetrics fm = g.getFontMetrics();
Rectangle2D r = fm.getStringBounds(str, g);
if((x-r.getWidth()/2) > previousText) {
g.setColor(Color.BLACK);
}
g.setColor(GRAY);
- g.drawLine((int)x, (int)bounds.getMinY()+12, (int)x, (int)bounds.getMinY()+19);
+ g.drawLine((int)x, (int)bounds.getMinY()+rulerPixelSize-1-u7, (int)x, (int)bounds.getMinY()+rulerPixelSize-1);
}
if(stepX/5 > 2) {
for(double x2 = x+stepX/5; x2 < x+stepX; x2+=stepX/5) {
- if(x2 > 20) {
- g.drawLine((int)x2, (int)bounds.getMinY()+15, (int)x2, (int)bounds.getMinY()+19);
+ if(x2 > rulerPixelSize) {
+ g.drawLine((int)x2, (int)bounds.getMinY()+rulerPixelSize-1-u4, (int)x2, (int)bounds.getMinY()+rulerPixelSize-1);
}
}
for(double x2 = x+stepX/10; x2 < x+stepX; x2+=stepX/5) {
- if(x2 > 20) {
- g.drawLine((int)x2, (int)bounds.getMinY()+17, (int)x2, (int)bounds.getMinY()+19);
+ if(x2 > rulerPixelSize) {
+ g.drawLine((int)x2, (int)bounds.getMinY()+rulerPixelSize-1-u2, (int)x2, (int)bounds.getMinY()+rulerPixelSize-1);
}
}
-
}
}
// Horizontal ruler
previousText = -100;
for(double y = offsetY%stepY-stepY; y < bounds.getMaxY(); y+=stepY) {
- if(y > 20) {
+ if(y > rulerPixelSize) {
double val = (y-offsetY)/scaleY / getTransform().getScaleY();
double modifiedValue = modifyVerticalValue(val);
String str = formatValue(modifiedValue, getMaxDigits());
- FontMetrics fm = g.getFontMetrics();
Rectangle2D r = fm.getStringBounds(str, g);
if(y-1+r.getHeight()/2 > previousText) {
g.setColor(Color.BLACK);
previousText = y-1+r.getHeight();
}
g.setColor(GRAY);
- g.drawLine((int)bounds.getMinX()+12, (int)y, (int)bounds.getMinX()+19, (int)y);
+ g.drawLine((int)bounds.getMinX()+rulerPixelSize-1-u7, (int)y, (int)bounds.getMinX()+rulerPixelSize-1, (int)y);
}
if(stepY/5 > 2) {
for(double y2 = y+stepY/5; y2 < y+stepY; y2+=stepY/5) {
- if(y2 > 20) {
- g.drawLine((int)bounds.getMinX()+15, (int)y2, (int)bounds.getMinX()+19, (int)y2);
+ if(y2 > rulerPixelSize) {
+ g.drawLine((int)bounds.getMinX()+rulerPixelSize-1-u4, (int)y2, (int)bounds.getMinX()+rulerPixelSize-1, (int)y2);
}
}
for(double y2 = y+stepY/10; y2 < y+stepY; y2+=stepY/5) {
- if(y2 > 20) {
- g.drawLine((int)bounds.getMinX()+17, (int)y2, (int)bounds.getMinX()+19, (int)y2);
+ if(y2 > rulerPixelSize) {
+ g.drawLine((int)bounds.getMinX()+rulerPixelSize-1-u2, (int)y2, (int)bounds.getMinX()+rulerPixelSize-1, (int)y2);
}
}
}
--- /dev/null
+/*******************************************************************************
+ * Copyright (c) 2018 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:
+ * Semantum Oy - initial API and implementation
+ *******************************************************************************/
+package org.simantics.scenegraph.utils;
+
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.Toolkit;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+
+/**
+ * @author Tuukka Lehtonen
+ * @since 1.36.0
+ */
+public class DPIUtil {
+
+ private static boolean initialized = false;
+ private static boolean hasZoom;
+
+ private static float upscaleFactorF;
+ private static double upscaleFactorD;
+ private static float downscaleFactorF;
+ private static double downscaleFactorD;
+
+ private static void initialize() {
+ if (initialized)
+ return;
+
+ double dpi = Toolkit.getDefaultToolkit().getScreenResolution();
+ double baseDpi = 96;
+ int zoom = (int) Math.round(100.0 * dpi / baseDpi);
+ hasZoom = zoom != 100;
+
+ upscaleFactorD = dpi / baseDpi;
+ upscaleFactorF = (float) upscaleFactorD;
+ downscaleFactorD = baseDpi / dpi;
+ downscaleFactorF = (float) downscaleFactorD;
+
+// System.out.format("DPIUtil:%n\tswt zoom = %d%n\tdownscale factor = %f%n\tupscale factor = %f%n",
+// zoom,
+// downscaleFactorD,
+// upscaleFactorD
+// );
+
+ initialized = true;
+ }
+
+ // Internals
+
+ private static Rectangle scale(float s, Rectangle r, Rectangle target) {
+ if (s == 1.0f) {
+ if (r == target)
+ return r;
+ if (target == null) {
+ return new Rectangle(r.x, r.y, r.width, r.height);
+ } else {
+ target.x = r.x;
+ target.y = r.y;
+ target.width = r.width;
+ target.height = r.height;
+ return target;
+ }
+ }
+ if (target == null) {
+ return new Rectangle(
+ Math.round(r.x*s),
+ Math.round(r.y*s),
+ Math.round(r.width*s),
+ Math.round(r.height*s));
+ } else {
+ target.x = Math.round(r.x*s);
+ target.y = Math.round(r.y*s);
+ target.width = Math.round(r.width*s);
+ target.height = Math.round(r.height*s);
+ return target;
+ }
+ }
+
+ private static Rectangle2D scale(double s, Rectangle2D r, Rectangle2D target) {
+ if (s == 1.0) {
+ if (r == target)
+ return r;
+ if (target == null)
+ return (Rectangle2D) r.clone();
+ target.setFrame(r);
+ return target;
+ }
+ if (target == null)
+ target = (Rectangle2D) r.clone();
+ target.setFrame(r.getX()*s, r.getY()*s, r.getWidth()*s, r.getHeight()*s);
+ return target;
+ }
+
+ private static double downscale0(double x) {
+ return hasZoom ? x * downscaleFactorD : x;
+ }
+
+ private static int downscaleToInteger0(double x) {
+ return (int)(hasZoom ? Math.round((double) x * downscaleFactorD) : x);
+ }
+
+ private static int downscale0(int x) {
+ return hasZoom ? (int) Math.round((double) x * downscaleFactorD) : x;
+ }
+
+ private static double upscale0(double x) {
+ return hasZoom ? x * upscaleFactorD : x;
+ }
+
+ private static int upscaleToInteger0(double x) {
+ return (int)(hasZoom ? Math.round((double) x * upscaleFactorD) : x);
+ }
+
+ private static int upscale0(int x) {
+ return hasZoom ? (int) Math.round((double) x * upscaleFactorD) : x;
+ }
+
+ // Downscaling
+
+ public static double downscale(double x) {
+ initialize();
+ return downscale0(x);
+ }
+
+ public static int downscale(int x) {
+ initialize();
+ return downscale0(x);
+ }
+
+ public static Point2D downscale(double x, double y) {
+ initialize();
+ if (!hasZoom)
+ return new Point2D.Double(x, y);
+ double s = downscaleFactorD;
+ return new Point2D.Double(x * s, y * s);
+ }
+
+ public static Point downscale(int x, int y) {
+ initialize();
+ return new Point(downscale0(x), downscale0(y));
+ }
+
+ public static Point2D downscale(Point2D p) {
+ return downscale(p.getX(), p.getY());
+ }
+
+ public static Point downscaleToInteger(Point2D p) {
+ initialize();
+ return new Point(downscaleToInteger0(p.getX()), downscaleToInteger0(p.getY()));
+ }
+
+ public static Rectangle2D downscale(Rectangle2D r, Rectangle2D target) {
+ initialize();
+ return scale(downscaleFactorD, r, target);
+ }
+
+ public static Rectangle2D downscale(Rectangle2D r) {
+ return downscale(r, null);
+ }
+
+ public static Rectangle downscale(Rectangle r, Rectangle target) {
+ initialize();
+ return scale(downscaleFactorF, r, target);
+ }
+
+ public static Rectangle downscale(Rectangle r) {
+ return downscale(r, null);
+ }
+
+ public static Rectangle downscaleToInteger(Rectangle2D r) {
+ initialize();
+ return new Rectangle(
+ downscaleToInteger0(r.getMinX()),
+ downscaleToInteger0(r.getMinY()),
+ downscaleToInteger0(r.getWidth()),
+ downscaleToInteger0(r.getHeight()));
+ }
+
+ // Upscaling
+
+ public static double upscale(double x) {
+ initialize();
+ return upscale0(x);
+ }
+
+ public static int upscale(int x) {
+ initialize();
+ return upscale0(x);
+ }
+
+ public static Point2D upscale(double x, double y) {
+ initialize();
+ if (!hasZoom)
+ return new Point2D.Double(x, y);
+ double s = upscaleFactorD;
+ return new Point2D.Double(x * s, y * s);
+ }
+
+ public static Point upscale(int x, int y) {
+ initialize();
+ return new Point(upscale0(x), upscale0(y));
+ }
+
+ public static Point2D upscale(Point2D p) {
+ initialize();
+ return (hasZoom && p != null) ? upscale(p.getX(), p.getY()) : p;
+ }
+
+ public static Point upscaleToInteger(Point2D p) {
+ initialize();
+ return new Point(upscaleToInteger0(p.getX()), upscaleToInteger0(p.getY()));
+ }
+
+ public static Point upscale(Point p) {
+ initialize();
+ return (hasZoom && p != null) ? upscale(p.x, p.y) : p;
+ }
+
+ public static Rectangle2D upscale(Rectangle2D r, Rectangle2D target) {
+ initialize();
+ return scale(upscaleFactorD, r, target);
+ }
+
+ public static Rectangle upscale(Rectangle r, Rectangle target) {
+ initialize();
+ return scale(upscaleFactorF, r, target);
+ }
+
+ public static Rectangle2D upscale(Rectangle2D r) {
+ return upscale(r, null);
+ }
+
+ public static Rectangle upscale(Rectangle r) {
+ return upscale(r, null);
+ }
+
+ public static Rectangle upscaleToInteger(Rectangle2D r) {
+ return new Rectangle(
+ upscaleToInteger0(r.getMinX()),
+ upscaleToInteger0(r.getMinY()),
+ upscaleToInteger0(r.getWidth()),
+ upscaleToInteger0(r.getHeight()));
+ }
+
+}