-/*******************************************************************************\r
- * Copyright (c) 2007- VTT Technical Research Centre of Finland.\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
-package org.simantics.image.ui.editor;\r
-\r
-import java.awt.geom.AffineTransform;\r
-import java.awt.geom.Point2D;\r
-import java.awt.geom.Rectangle2D;\r
-import java.awt.image.BufferedImage;\r
-import java.awt.image.DirectColorModel;\r
-import java.awt.image.IndexColorModel;\r
-import java.awt.image.WritableRaster;\r
-import java.io.ByteArrayInputStream;\r
-\r
-import org.eclipse.jface.layout.GridDataFactory;\r
-import org.eclipse.jface.resource.JFaceResources;\r
-import org.eclipse.jface.resource.LocalResourceManager;\r
-import org.eclipse.jface.resource.ResourceManager;\r
-import org.eclipse.swt.SWT;\r
-import org.eclipse.swt.SWTError;\r
-import org.eclipse.swt.SWTException;\r
-import org.eclipse.swt.graphics.GC;\r
-import org.eclipse.swt.graphics.Image;\r
-import org.eclipse.swt.graphics.ImageData;\r
-import org.eclipse.swt.graphics.PaletteData;\r
-import org.eclipse.swt.graphics.Point;\r
-import org.eclipse.swt.graphics.RGB;\r
-import org.eclipse.swt.graphics.Rectangle;\r
-import org.eclipse.swt.widgets.Canvas;\r
-import org.eclipse.swt.widgets.Composite;\r
-import org.eclipse.swt.widgets.Display;\r
-import org.eclipse.swt.widgets.Event;\r
-import org.eclipse.swt.widgets.Listener;\r
-import org.simantics.Simantics;\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.db.ReadGraph;\r
-import org.simantics.db.Resource;\r
-import org.simantics.db.common.request.ParametrizedRead;\r
-import org.simantics.db.common.request.ResourceRead;\r
-import org.simantics.db.exception.DatabaseException;\r
-import org.simantics.db.layer0.request.combinations.Combinators;\r
-import org.simantics.image2.ontology.ImageResource;\r
-import org.simantics.scenegraph.ScenegraphUtils;\r
-import org.simantics.scenegraph.utils.GeometryUtils;\r
-import org.simantics.ui.workbench.IResourceEditorInput;\r
-import org.simantics.ui.workbench.ResourceEditorPart;\r
-import org.simantics.ui.workbench.editor.input.InputValidationCombinators;\r
-import org.simantics.utils.ui.ErrorLogger;\r
-import org.simantics.utils.ui.LayoutUtils;\r
-\r
-import com.kitfox.svg.SVGDiagram;\r
-import com.kitfox.svg.SVGException;\r
-import com.kitfox.svg.SVGUniverse;\r
-\r
-/**\r
- * @author Tuukka Lehtonen\r
- */\r
-public class ImageEditor extends ResourceEditorPart {\r
-\r
- public static final String EDITOR_ID = "org.simantics.wiki.ui.image.editor";\r
-\r
- protected boolean disposed = false;\r
-\r
- protected Image image;\r
- protected SVGDiagram svgDiagram;\r
- private ResourceManager resourceManager;\r
- private Canvas canvas;\r
-\r
- private double zoomLevel = 1;\r
- private Point2D previousTranslate = new Point2D.Double();\r
- private Point2D translate = new Point2D.Double();\r
- private boolean zoomToFit = false;\r
- private SVGUniverse svgUniverse = new SVGUniverse();\r
-\r
- static ImageData convertToSWT(BufferedImage bufferedImage) {\r
- if (bufferedImage.getColorModel() instanceof DirectColorModel) {\r
- DirectColorModel colorModel = (DirectColorModel) bufferedImage\r
- .getColorModel();\r
- PaletteData palette = new PaletteData(colorModel.getRedMask(),\r
- colorModel.getGreenMask(), colorModel.getBlueMask());\r
- ImageData data = new ImageData(bufferedImage.getWidth(),\r
- bufferedImage.getHeight(), colorModel.getPixelSize(),\r
- palette);\r
- WritableRaster raster = bufferedImage.getRaster();\r
- int[] pixelArray = new int[4];\r
- for (int y = 0; y < data.height; y++) {\r
- for (int x = 0; x < data.width; x++) {\r
- raster.getPixel(x, y, pixelArray);\r
- int pixel = palette.getPixel(new RGB(pixelArray[0],\r
- pixelArray[1], pixelArray[2]));\r
- data.setPixel(x, y, pixel);\r
- }\r
- }\r
- return data;\r
- } else if (bufferedImage.getColorModel() instanceof IndexColorModel) {\r
- IndexColorModel colorModel = (IndexColorModel) bufferedImage\r
- .getColorModel();\r
- int size = colorModel.getMapSize();\r
- byte[] reds = new byte[size];\r
- byte[] greens = new byte[size];\r
- byte[] blues = new byte[size];\r
- colorModel.getReds(reds);\r
- colorModel.getGreens(greens);\r
- colorModel.getBlues(blues);\r
- RGB[] rgbs = new RGB[size];\r
- for (int i = 0; i < rgbs.length; i++) {\r
- rgbs[i] = new RGB(reds[i] & 0xFF, greens[i] & 0xFF,\r
- blues[i] & 0xFF);\r
- }\r
- PaletteData palette = new PaletteData(rgbs);\r
- ImageData data = new ImageData(bufferedImage.getWidth(),\r
- bufferedImage.getHeight(), colorModel.getPixelSize(),\r
- palette);\r
- data.transparentPixel = colorModel.getTransparentPixel();\r
- WritableRaster raster = bufferedImage.getRaster();\r
- int[] pixelArray = new int[1];\r
- for (int y = 0; y < data.height; y++) {\r
- for (int x = 0; x < data.width; x++) {\r
- raster.getPixel(x, y, pixelArray);\r
- data.setPixel(x, y, pixelArray[0]);\r
- }\r
- }\r
- return data;\r
- }\r
- return null;\r
- }\r
-\r
- ParametrizedRead<IResourceEditorInput, Boolean> INPUT_VALIDATOR =\r
- Combinators.compose(\r
- InputValidationCombinators.hasURI(),\r
- InputValidationCombinators.extractInputResource()\r
- );\r
-\r
- @Override\r
- protected ParametrizedRead<IResourceEditorInput, Boolean> getInputValidator() {\r
- return INPUT_VALIDATOR;\r
- }\r
-\r
- @Override\r
- public void createPartControl(final Composite parent) {\r
- this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent);\r
-\r
- parent.setLayout(LayoutUtils.createNoBorderGridLayout(1));\r
-\r
- canvas = new Canvas(parent, SWT.DOUBLE_BUFFERED);\r
- canvas.setBackground(resourceManager.createColor(new RGB(255, 255, 255)));\r
- GridDataFactory.fillDefaults().grab(true, true).applyTo(canvas);\r
-\r
- canvas.addListener(SWT.Paint, new Listener() {\r
- @Override\r
- public void handleEvent(Event event) {\r
- if (svgDiagram != null) {\r
- Rectangle2D r = svgDiagram.getViewRect();\r
- if (r.isEmpty())\r
- return;\r
-\r
- // FIXME: this is unsafe, renders unnecessarily large\r
- // buffered images when only control size is ultimately needed.\r
-\r
- AffineTransform tr = AffineTransform.getScaleInstance(zoomLevel, zoomLevel);\r
- tr.translate(translate.getX(), translate.getY());\r
- try {\r
- BufferedImage bi = ScenegraphUtils.paintSVG(svgDiagram, tr, 0f);\r
- Image img = new Image(parent.getDisplay(), convertToSWT(bi));\r
- drawImage(event.gc, img, false);\r
- img.dispose();\r
- } catch (SVGException e) {\r
- }\r
- } else if (image != null) {\r
- drawImage(event.gc, image, true);\r
- }\r
- }\r
- private void drawImage(GC gc, Image image, boolean fitToCanvas) {\r
- Rectangle r = image.getBounds();\r
- if (r.isEmpty())\r
- return;\r
-\r
- Point destSize = canvas.getSize();\r
- int xSpace = destSize.x - r.width;\r
- int ySpace = destSize.y - r.height;\r
- boolean fitsX = xSpace >= 0;\r
- boolean fitsY = ySpace >= 0;\r
- boolean fitsCanvas = fitsX && fitsY;\r
-\r
- // if the image is larger than the canvas, zoom it to fit\r
- if ((!fitsCanvas && fitToCanvas) || zoomToFit) {\r
- gc.setAntialias(SWT.ON);\r
-\r
- // Zoom to fit propertionally\r
- int leftMargin = 0;\r
- int topMargin = 0;\r
- if (xSpace > ySpace) {\r
- double yr = (double) destSize.y / r.height;\r
- double xo = (int)(r.width * yr);\r
- leftMargin = (int) Math.round(((destSize.x - r.width) + (r.width - xo)) * 0.5);\r
- gc.drawImage(image, 0, 0, r.width, r.height, leftMargin, topMargin, (int) xo, destSize.y);\r
- } else {\r
- double xr = (double) destSize.x / r.width;\r
- double yo = (int)(r.height * xr);\r
- topMargin = (int) Math.round(((destSize.y - r.height) + (r.height - yo)) * 0.5);\r
- gc.drawImage(image, 0, 0, r.width, r.height, leftMargin, topMargin, destSize.x, (int) yo);\r
- }\r
- } else {\r
- if (!fitsCanvas) {\r
- int srcX = fitsX ? 0 : (int) Math.round(-xSpace * 0.5);\r
- int srcY = fitsY ? 0 : (int) Math.round(-ySpace * 0.5);\r
- int srcW = fitsX ? r.width : r.width + xSpace;\r
- int srcH = fitsY ? r.height : r.height + ySpace;\r
- int destX = fitsX ? (int) Math.round(xSpace * 0.5) : 0;\r
- int destY = fitsY ? (int) Math.round(ySpace * 0.5) : 0;\r
- int destW = fitsX ? r.width : destSize.x;\r
- int destH = fitsY ? r.height : destSize.y;\r
- gc.drawImage(image, srcX, srcY, srcW, srcH, destX, destY, destW, destH);\r
- } else {\r
- // Center on the canvas.\r
- int leftMargin = (int) Math.round((destSize.x - r.width) * 0.5);\r
- int topMargin = (int) Math.round((destSize.y - r.height) * 0.5);\r
- gc.drawImage(image, 0, 0, r.width, r.height, leftMargin, topMargin, r.width, r.height);\r
- }\r
- }\r
- }\r
- });\r
- canvas.addListener(SWT.MouseDoubleClick, new Listener() {\r
- @Override\r
- public void handleEvent(Event event) {\r
- zoomToFit ^= true;\r
- canvas.redraw();\r
- }\r
- });\r
-\r
- // Mouse tracking support\r
- Listener listener = new Listener() {\r
- boolean pan = false;\r
- Point panStart = new Point(0, 0); \r
- @Override\r
- public void handleEvent(Event e) {\r
- switch (e.type) {\r
- case SWT.MouseUp:\r
- if (e.button == 3) {\r
- pan = false;\r
- previousTranslate.setLocation(translate);\r
- }\r
- break;\r
- case SWT.MouseDown:\r
- if (e.button == 3) {\r
- panStart.x = e.x;\r
- panStart.y = e.y;\r
- pan = true;\r
- previousTranslate.setLocation(translate);\r
- }\r
- break;\r
- case SWT.MouseMove:\r
- if (pan) {\r
- int dx = e.x - panStart.x;\r
- int dy = e.y - panStart.y;\r
- translate.setLocation(\r
- previousTranslate.getX() + dx / zoomLevel,\r
- previousTranslate.getY() + dy / zoomLevel);\r
- canvas.redraw();\r
- }\r
- break;\r
- case SWT.MouseVerticalWheel:\r
- double scroll = Math.min(0.9, -e.count / 20.0);\r
- double z = 1 - scroll;\r
- zoomLevel = limitScaleFactor(zoomLevel, z);\r
- canvas.redraw();\r
- break;\r
- }\r
- }\r
- private double limitScaleFactor(double zoomLevel, double scaleFactor) {\r
- double inLimit = 200.0;\r
- double outLimit = 10;\r
-\r
- AffineTransform view = AffineTransform.getScaleInstance(zoomLevel, zoomLevel);\r
- double currentScale = GeometryUtils.getScale(view) * 100.0;\r
- double newScale = currentScale * scaleFactor;\r
-\r
- if (newScale > currentScale && newScale > inLimit) {\r
- if (currentScale < inLimit)\r
- scaleFactor = inLimit / currentScale;\r
- else\r
- return inLimit / 100.0;\r
- } else if (newScale < currentScale && newScale < outLimit) {\r
- if (currentScale > outLimit)\r
- scaleFactor = outLimit / currentScale;\r
- else\r
- return outLimit / 100.0;\r
- }\r
- return zoomLevel * scaleFactor;\r
- }\r
- };\r
- canvas.addListener(SWT.MouseUp, listener);\r
- canvas.addListener(SWT.MouseDown, listener);\r
- canvas.addListener(SWT.MouseMove, listener);\r
- canvas.addListener(SWT.MouseWheel, listener);\r
-\r
- // Start tracking editor input validity.\r
- activateValidation();\r
-\r
- loadAndTrackInput();\r
- }\r
-\r
- private void loadAndTrackInput() {\r
- final Resource input = getInputResource();\r
- Simantics.getSession().asyncRequest(new ResourceRead<Object>(input) {\r
- @Override\r
- public Object perform(ReadGraph graph) throws DatabaseException {\r
- ImageResource img = ImageResource.getInstance(graph);\r
- if (graph.isInstanceOf(input, img.SvgImage)) {\r
- String text = graph.getPossibleValue(input, Bindings.STRING);\r
- return text;\r
- } else if (graph.isInstanceOf(input, img.Image)) {\r
- byte data[] = graph.getPossibleValue(input, Bindings.BYTE_ARRAY);\r
- return data;\r
- }\r
- return null;\r
- }\r
- }, new org.simantics.db.procedure.Listener<Object>() {\r
- @Override\r
- public void execute(Object result) {\r
- if (result instanceof String) {\r
- // svg text\r
- try {\r
- svgDiagram = ScenegraphUtils.loadSVGDiagram(svgUniverse, (String) result);\r
- scheduleRedraw();\r
- } catch (SVGException e) {\r
- ErrorLogger.defaultLogError(e);\r
- }\r
- } else if (result instanceof byte[]) {\r
- // bitmap image data\r
- Display display = canvas.getDisplay();\r
- if (!display.isDisposed()) {\r
- try {\r
- image = new Image(canvas.getDisplay(), new ByteArrayInputStream((byte[]) result));\r
- scheduleRedraw();\r
- } catch (SWTException e) {\r
- ErrorLogger.defaultLogError(e);\r
- } catch (SWTError e) {\r
- ErrorLogger.defaultLogError(e);\r
- }\r
- }\r
- }\r
- }\r
- private void scheduleRedraw() {\r
- Display d = canvas.getDisplay();\r
- if (!d.isDisposed())\r
- d.asyncExec(new Runnable() {\r
- @Override\r
- public void run() {\r
- if (!canvas.isDisposed())\r
- canvas.redraw();\r
- }\r
- });\r
- }\r
- @Override\r
- public void exception(Throwable t) {\r
- ErrorLogger.defaultLogError(t);\r
- }\r
- @Override\r
- public boolean isDisposed() {\r
- return disposed;\r
- }\r
- });\r
- }\r
-\r
- @Override\r
- public void dispose() {\r
- disposed = true;\r
- if (image != null) {\r
- image.dispose();\r
- image = null;\r
- }\r
- svgUniverse.clear();\r
- }\r
-\r
- @Override\r
- public void setFocus() {\r
- if (canvas != null)\r
- canvas.setFocus();\r
- }\r
-\r
- @SuppressWarnings("rawtypes")\r
- @Override\r
- public Object getAdapter(Class adapter) {\r
- return null;\r
- }\r
-\r
-}\r
+/*******************************************************************************
+ * Copyright (c) 2007- VTT Technical Research Centre of Finland.
+ * 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.image.ui.editor;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.Point2D;
+import java.awt.geom.Rectangle2D;
+import java.awt.image.BufferedImage;
+import java.awt.image.DirectColorModel;
+import java.awt.image.IndexColorModel;
+import java.awt.image.WritableRaster;
+import java.io.ByteArrayInputStream;
+
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.resource.LocalResourceManager;
+import org.eclipse.jface.resource.ResourceManager;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.SWTError;
+import org.eclipse.swt.SWTException;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.PaletteData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.simantics.Simantics;
+import org.simantics.databoard.Bindings;
+import org.simantics.db.ReadGraph;
+import org.simantics.db.Resource;
+import org.simantics.db.common.request.ParametrizedRead;
+import org.simantics.db.common.request.ResourceRead;
+import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.layer0.request.combinations.Combinators;
+import org.simantics.image2.ontology.ImageResource;
+import org.simantics.scenegraph.ScenegraphUtils;
+import org.simantics.scenegraph.utils.GeometryUtils;
+import org.simantics.ui.workbench.IResourceEditorInput;
+import org.simantics.ui.workbench.ResourceEditorPart;
+import org.simantics.ui.workbench.editor.input.InputValidationCombinators;
+import org.simantics.utils.ui.ErrorLogger;
+import org.simantics.utils.ui.LayoutUtils;
+
+import com.kitfox.svg.SVGDiagram;
+import com.kitfox.svg.SVGException;
+import com.kitfox.svg.SVGUniverse;
+
+/**
+ * @author Tuukka Lehtonen
+ */
+public class ImageEditor extends ResourceEditorPart {
+
+ public static final String EDITOR_ID = "org.simantics.wiki.ui.image.editor"; //$NON-NLS-1$
+
+ protected boolean disposed = false;
+
+ protected Image image;
+ protected SVGDiagram svgDiagram;
+ private ResourceManager resourceManager;
+ private Canvas canvas;
+
+ private double zoomLevel = 1;
+ private Point2D previousTranslate = new Point2D.Double();
+ private Point2D translate = new Point2D.Double();
+ private boolean zoomToFit = false;
+ private SVGUniverse svgUniverse = new SVGUniverse();
+
+ static ImageData convertToSWT(BufferedImage bufferedImage) {
+ if (bufferedImage.getColorModel() instanceof DirectColorModel) {
+ DirectColorModel colorModel = (DirectColorModel) bufferedImage
+ .getColorModel();
+ PaletteData palette = new PaletteData(colorModel.getRedMask(),
+ colorModel.getGreenMask(), colorModel.getBlueMask());
+ ImageData data = new ImageData(bufferedImage.getWidth(),
+ bufferedImage.getHeight(), colorModel.getPixelSize(),
+ palette);
+ WritableRaster raster = bufferedImage.getRaster();
+ int[] pixelArray = new int[4];
+ for (int y = 0; y < data.height; y++) {
+ for (int x = 0; x < data.width; x++) {
+ raster.getPixel(x, y, pixelArray);
+ int pixel = palette.getPixel(new RGB(pixelArray[0],
+ pixelArray[1], pixelArray[2]));
+ data.setPixel(x, y, pixel);
+ }
+ }
+ return data;
+ } else if (bufferedImage.getColorModel() instanceof IndexColorModel) {
+ IndexColorModel colorModel = (IndexColorModel) bufferedImage
+ .getColorModel();
+ int size = colorModel.getMapSize();
+ byte[] reds = new byte[size];
+ byte[] greens = new byte[size];
+ byte[] blues = new byte[size];
+ colorModel.getReds(reds);
+ colorModel.getGreens(greens);
+ colorModel.getBlues(blues);
+ RGB[] rgbs = new RGB[size];
+ for (int i = 0; i < rgbs.length; i++) {
+ rgbs[i] = new RGB(reds[i] & 0xFF, greens[i] & 0xFF,
+ blues[i] & 0xFF);
+ }
+ PaletteData palette = new PaletteData(rgbs);
+ ImageData data = new ImageData(bufferedImage.getWidth(),
+ bufferedImage.getHeight(), colorModel.getPixelSize(),
+ palette);
+ data.transparentPixel = colorModel.getTransparentPixel();
+ WritableRaster raster = bufferedImage.getRaster();
+ int[] pixelArray = new int[1];
+ for (int y = 0; y < data.height; y++) {
+ for (int x = 0; x < data.width; x++) {
+ raster.getPixel(x, y, pixelArray);
+ data.setPixel(x, y, pixelArray[0]);
+ }
+ }
+ return data;
+ }
+ return null;
+ }
+
+ ParametrizedRead<IResourceEditorInput, Boolean> INPUT_VALIDATOR =
+ Combinators.compose(
+ InputValidationCombinators.hasURI(),
+ InputValidationCombinators.extractInputResource()
+ );
+
+ @Override
+ protected ParametrizedRead<IResourceEditorInput, Boolean> getInputValidator() {
+ return INPUT_VALIDATOR;
+ }
+
+ @Override
+ public void createPartControl(final Composite parent) {
+ this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), parent);
+
+ parent.setLayout(LayoutUtils.createNoBorderGridLayout(1));
+
+ canvas = new Canvas(parent, SWT.DOUBLE_BUFFERED);
+ canvas.setBackground(resourceManager.createColor(new RGB(255, 255, 255)));
+ GridDataFactory.fillDefaults().grab(true, true).applyTo(canvas);
+
+ canvas.addListener(SWT.Paint, new Listener() {
+ @Override
+ public void handleEvent(Event event) {
+ if (svgDiagram != null) {
+ Rectangle2D r = svgDiagram.getViewRect();
+ if (r.isEmpty())
+ return;
+
+ // FIXME: this is unsafe, renders unnecessarily large
+ // buffered images when only control size is ultimately needed.
+
+ AffineTransform tr = AffineTransform.getScaleInstance(zoomLevel, zoomLevel);
+ tr.translate(translate.getX(), translate.getY());
+ try {
+ BufferedImage bi = ScenegraphUtils.paintSVG(svgDiagram, tr, 0f);
+ Image img = new Image(parent.getDisplay(), convertToSWT(bi));
+ drawImage(event.gc, img, false);
+ img.dispose();
+ } catch (SVGException e) {
+ }
+ } else if (image != null) {
+ drawImage(event.gc, image, true);
+ }
+ }
+ private void drawImage(GC gc, Image image, boolean fitToCanvas) {
+ Rectangle r = image.getBounds();
+ if (r.isEmpty())
+ return;
+
+ Point destSize = canvas.getSize();
+ int xSpace = destSize.x - r.width;
+ int ySpace = destSize.y - r.height;
+ boolean fitsX = xSpace >= 0;
+ boolean fitsY = ySpace >= 0;
+ boolean fitsCanvas = fitsX && fitsY;
+
+ // if the image is larger than the canvas, zoom it to fit
+ if ((!fitsCanvas && fitToCanvas) || zoomToFit) {
+ gc.setAntialias(SWT.ON);
+
+ // Zoom to fit propertionally
+ int leftMargin = 0;
+ int topMargin = 0;
+ if (xSpace > ySpace) {
+ double yr = (double) destSize.y / r.height;
+ double xo = (int)(r.width * yr);
+ leftMargin = (int) Math.round(((destSize.x - r.width) + (r.width - xo)) * 0.5);
+ gc.drawImage(image, 0, 0, r.width, r.height, leftMargin, topMargin, (int) xo, destSize.y);
+ } else {
+ double xr = (double) destSize.x / r.width;
+ double yo = (int)(r.height * xr);
+ topMargin = (int) Math.round(((destSize.y - r.height) + (r.height - yo)) * 0.5);
+ gc.drawImage(image, 0, 0, r.width, r.height, leftMargin, topMargin, destSize.x, (int) yo);
+ }
+ } else {
+ if (!fitsCanvas) {
+ int srcX = fitsX ? 0 : (int) Math.round(-xSpace * 0.5);
+ int srcY = fitsY ? 0 : (int) Math.round(-ySpace * 0.5);
+ int srcW = fitsX ? r.width : r.width + xSpace;
+ int srcH = fitsY ? r.height : r.height + ySpace;
+ int destX = fitsX ? (int) Math.round(xSpace * 0.5) : 0;
+ int destY = fitsY ? (int) Math.round(ySpace * 0.5) : 0;
+ int destW = fitsX ? r.width : destSize.x;
+ int destH = fitsY ? r.height : destSize.y;
+ gc.drawImage(image, srcX, srcY, srcW, srcH, destX, destY, destW, destH);
+ } else {
+ // Center on the canvas.
+ int leftMargin = (int) Math.round((destSize.x - r.width) * 0.5);
+ int topMargin = (int) Math.round((destSize.y - r.height) * 0.5);
+ gc.drawImage(image, 0, 0, r.width, r.height, leftMargin, topMargin, r.width, r.height);
+ }
+ }
+ }
+ });
+ canvas.addListener(SWT.MouseDoubleClick, new Listener() {
+ @Override
+ public void handleEvent(Event event) {
+ zoomToFit ^= true;
+ canvas.redraw();
+ }
+ });
+
+ // Mouse tracking support
+ Listener listener = new Listener() {
+ boolean pan = false;
+ Point panStart = new Point(0, 0);
+ @Override
+ public void handleEvent(Event e) {
+ switch (e.type) {
+ case SWT.MouseUp:
+ if (e.button == 3) {
+ pan = false;
+ previousTranslate.setLocation(translate);
+ }
+ break;
+ case SWT.MouseDown:
+ if (e.button == 3) {
+ panStart.x = e.x;
+ panStart.y = e.y;
+ pan = true;
+ previousTranslate.setLocation(translate);
+ }
+ break;
+ case SWT.MouseMove:
+ if (pan) {
+ int dx = e.x - panStart.x;
+ int dy = e.y - panStart.y;
+ translate.setLocation(
+ previousTranslate.getX() + dx / zoomLevel,
+ previousTranslate.getY() + dy / zoomLevel);
+ canvas.redraw();
+ }
+ break;
+ case SWT.MouseVerticalWheel:
+ double scroll = Math.min(0.9, -e.count / 20.0);
+ double z = 1 - scroll;
+ zoomLevel = limitScaleFactor(zoomLevel, z);
+ canvas.redraw();
+ break;
+ }
+ }
+ private double limitScaleFactor(double zoomLevel, double scaleFactor) {
+ double inLimit = 200.0;
+ double outLimit = 10;
+
+ AffineTransform view = AffineTransform.getScaleInstance(zoomLevel, zoomLevel);
+ double currentScale = GeometryUtils.getScale(view) * 100.0;
+ double newScale = currentScale * scaleFactor;
+
+ if (newScale > currentScale && newScale > inLimit) {
+ if (currentScale < inLimit)
+ scaleFactor = inLimit / currentScale;
+ else
+ return inLimit / 100.0;
+ } else if (newScale < currentScale && newScale < outLimit) {
+ if (currentScale > outLimit)
+ scaleFactor = outLimit / currentScale;
+ else
+ return outLimit / 100.0;
+ }
+ return zoomLevel * scaleFactor;
+ }
+ };
+ canvas.addListener(SWT.MouseUp, listener);
+ canvas.addListener(SWT.MouseDown, listener);
+ canvas.addListener(SWT.MouseMove, listener);
+ canvas.addListener(SWT.MouseWheel, listener);
+
+ // Start tracking editor input validity.
+ activateValidation();
+
+ loadAndTrackInput();
+ }
+
+ private void loadAndTrackInput() {
+ final Resource input = getInputResource();
+ Simantics.getSession().asyncRequest(new ResourceRead<Object>(input) {
+ @Override
+ public Object perform(ReadGraph graph) throws DatabaseException {
+ ImageResource img = ImageResource.getInstance(graph);
+ if (graph.isInstanceOf(input, img.SvgImage)) {
+ String text = graph.getPossibleValue(input, Bindings.STRING);
+ return text;
+ } else if (graph.isInstanceOf(input, img.Image)) {
+ byte data[] = graph.getPossibleValue(input, Bindings.BYTE_ARRAY);
+ return data;
+ }
+ return null;
+ }
+ }, new org.simantics.db.procedure.Listener<Object>() {
+ @Override
+ public void execute(Object result) {
+ if (result instanceof String) {
+ // svg text
+ try {
+ svgDiagram = ScenegraphUtils.loadSVGDiagram(svgUniverse, (String) result);
+ scheduleRedraw();
+ } catch (SVGException e) {
+ ErrorLogger.defaultLogError(e);
+ }
+ } else if (result instanceof byte[]) {
+ // bitmap image data
+ Display display = canvas.getDisplay();
+ if (!display.isDisposed()) {
+ try {
+ image = new Image(canvas.getDisplay(), new ByteArrayInputStream((byte[]) result));
+ scheduleRedraw();
+ } catch (SWTException e) {
+ ErrorLogger.defaultLogError(e);
+ } catch (SWTError e) {
+ ErrorLogger.defaultLogError(e);
+ }
+ }
+ }
+ }
+ private void scheduleRedraw() {
+ Display d = canvas.getDisplay();
+ if (!d.isDisposed())
+ d.asyncExec(new Runnable() {
+ @Override
+ public void run() {
+ if (!canvas.isDisposed())
+ canvas.redraw();
+ }
+ });
+ }
+ @Override
+ public void exception(Throwable t) {
+ ErrorLogger.defaultLogError(t);
+ }
+ @Override
+ public boolean isDisposed() {
+ return disposed;
+ }
+ });
+ }
+
+ @Override
+ public void dispose() {
+ disposed = true;
+ if (image != null) {
+ image.dispose();
+ image = null;
+ }
+ svgUniverse.clear();
+ }
+
+ @Override
+ public void setFocus() {
+ if (canvas != null)
+ canvas.setFocus();
+ }
+
+ @SuppressWarnings("rawtypes")
+ @Override
+ public Object getAdapter(Class adapter) {
+ return null;
+ }
+
+}