/******************************************************************************* * Copyright (c) 2004 Chengdong Li : cdli@ccs.uky.edu * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html *******************************************************************************/ package org.erlide.wrangler.refactoring.codeinspection.view; import java.awt.geom.AffineTransform; import org.eclipse.core.runtime.Path; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ControlAdapter; import org.eclipse.swt.events.ControlEvent; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.ImageLoader; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.FileDialog; import org.eclipse.swt.widgets.ScrollBar; /** * A scrollable image canvas that extends org.eclipse.swt.graphics.Canvas. * <p/> * It requires Eclipse (version >= 2.1) on Win32/win32; Linux/gtk; * MacOSX/carbon. * <p/> * This implementation using the pure SWT, no UI AWT package is used. For * convenience, I put everything into one class. However, the best way to * implement this is to use inheritance to create multiple hierarchies. * * @author Chengdong Li: cli4@uky.edu */ public class SWTImageCanvas extends Canvas { /* zooming rates in x and y direction are equal. */ final float ZOOMIN_RATE = 1.1f; /* zoomin rate */ final float ZOOMOUT_RATE = 0.9f; /* zoomout rate */ private Image sourceImage; /* original image */ Image screenImage; /* screen image */ private AffineTransform transform = new AffineTransform(); private String currentDir = ""; /* remembering file open directory */ public SWTImageCanvas(final Composite parent) { this(parent, SWT.NULL); } /** * Constructor for ScrollableCanvas. * * @param parent * the parent of this control. * @param style * the style of this control. */ public SWTImageCanvas(final Composite parent, final int style) { super(parent, style | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL | SWT.NO_BACKGROUND); addControlListener(new ControlAdapter() { /* resize listener. */ @Override public void controlResized(final ControlEvent event) { syncScrollBars(); } }); addPaintListener(new PaintListener() { /* paint listener. */ @Override public void paintControl(final PaintEvent event) { paint(event.gc); } }); initScrollBars(); } /** * Dispose the garbage here */ @Override public void dispose() { if (sourceImage != null && !sourceImage.isDisposed()) { sourceImage.dispose(); } if (screenImage != null && !screenImage.isDisposed()) { screenImage.dispose(); } } /* Paint function */ private void paint(final GC gc) { final Rectangle clientRect = getClientArea(); /* Canvas' painting area */ if (sourceImage != null) { Rectangle imageRect = SWT2Dutil.inverseTransformRect(transform, clientRect); final int gap = 2; /* find a better start point to render */ imageRect.x -= gap; imageRect.y -= gap; imageRect.width += 2 * gap; imageRect.height += 2 * gap; final Rectangle imageBound = sourceImage.getBounds(); imageRect = imageRect.intersection(imageBound); final Rectangle destRect = SWT2Dutil.transformRect(transform, imageRect); if (screenImage != null) { screenImage.dispose(); } screenImage = new Image(getDisplay(), clientRect.width, clientRect.height); final GC newGC = new GC(screenImage); newGC.setClipping(clientRect); newGC.drawImage(sourceImage, imageRect.x, imageRect.y, imageRect.width, imageRect.height, destRect.x, destRect.y, destRect.width, destRect.height); newGC.dispose(); gc.drawImage(screenImage, 0, 0); } else { gc.setClipping(clientRect); gc.fillRectangle(clientRect); initScrollBars(); } } /* Initalize the scrollbar and register listeners. */ private void initScrollBars() { final ScrollBar horizontal = getHorizontalBar(); horizontal.setEnabled(false); horizontal.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent event) { scrollHorizontally((ScrollBar) event.widget); } }); final ScrollBar vertical = getVerticalBar(); vertical.setEnabled(false); vertical.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent event) { scrollVertically((ScrollBar) event.widget); } }); } /* Scroll horizontally */ private void scrollHorizontally(final ScrollBar scrollBar) { if (sourceImage == null) { return; } final AffineTransform af = transform; final double tx = af.getTranslateX(); final double select = -scrollBar.getSelection(); af.preConcatenate(AffineTransform.getTranslateInstance(select - tx, 0)); transform = af; syncScrollBars(); } /* Scroll vertically */ private void scrollVertically(final ScrollBar scrollBar) { if (sourceImage == null) { return; } final AffineTransform af = transform; final double ty = af.getTranslateY(); final double select = -scrollBar.getSelection(); af.preConcatenate(AffineTransform.getTranslateInstance(0, select - ty)); transform = af; syncScrollBars(); } /** * Source image getter. * * @return sourceImage. */ public Image getSourceImage() { return sourceImage; } /** * Synchronize the scrollbar with the image. If the transform is out of * range, it will correct it. This function considers only following factors * :<b> transform, image size, client area</b>. */ public void syncScrollBars() { if (sourceImage == null) { redraw(); return; } AffineTransform af = transform; final double sx = af.getScaleX(), sy = af.getScaleY(); double tx = af.getTranslateX(), ty = af.getTranslateY(); if (tx > 0) { tx = 0; } if (ty > 0) { ty = 0; } final ScrollBar horizontal = getHorizontalBar(); horizontal.setIncrement(getClientArea().width / 100); horizontal.setPageIncrement(getClientArea().width); final Rectangle imageBound = sourceImage.getBounds(); final int cw = getClientArea().width, ch = getClientArea().height; if (imageBound.width * sx > cw) { /* image is wider than client area */ horizontal.setMaximum((int) (imageBound.width * sx)); horizontal.setEnabled(true); if ((int) -tx > horizontal.getMaximum() - cw) { tx = -horizontal.getMaximum() + cw; } } else { /* image is narrower than client area */ horizontal.setEnabled(false); tx = (cw - imageBound.width * sx) / 2; // center if too small. } horizontal.setSelection((int) -tx); horizontal.setThumb(getClientArea().width); final ScrollBar vertical = getVerticalBar(); vertical.setIncrement(getClientArea().height / 100); vertical.setPageIncrement(getClientArea().height); if (imageBound.height * sy > ch) { /* image is higher than client area */ vertical.setMaximum((int) (imageBound.height * sy)); vertical.setEnabled(true); if ((int) -ty > vertical.getMaximum() - ch) { ty = -vertical.getMaximum() + ch; } } else { /* image is less higher than client area */ vertical.setEnabled(false); ty = (ch - imageBound.height * sy) / 2; // center if too small. } vertical.setSelection((int) -ty); vertical.setThumb(getClientArea().height); /* update transform. */ af = AffineTransform.getScaleInstance(sx, sy); af.preConcatenate(AffineTransform.getTranslateInstance(tx, ty)); transform = af; redraw(); } /** * Reload image from a file * * @param filename * image file * @return swt image created from image file */ public Image loadImage(final String filename) { if (sourceImage != null && !sourceImage.isDisposed()) { sourceImage.dispose(); sourceImage = null; } sourceImage = new Image(getDisplay(), filename); showOriginal(); return sourceImage; } /** * Load image * * @param img * image object */ public void setImage(final Image img) { sourceImage = img; showOriginal(); } /** * Save image to a file */ public void onFileSave() { final FileDialog fileChooser = new FileDialog(getShell(), SWT.SAVE); fileChooser.setText("Save image file"); fileChooser.setFilterPath(currentDir); fileChooser.setFilterExtensions(new String[] { "*.jpg;*.png" }); fileChooser.setFilterNames(new String[] { "Image file " + " (jpeg, png)" }); final String filename = fileChooser.open(); if (filename != null) { final ImageLoader imageLoader = new ImageLoader(); imageLoader.data = new ImageData[] { sourceImage.getImageData() }; final Path p = new Path(filename); if (p.getFileExtension() == "jpg") { imageLoader.save(filename, SWT.IMAGE_JPEG); } else { imageLoader.save(filename, SWT.IMAGE_PNG); } } } /** * Call back funtion of button "open". Will open a file dialog, and choose * the image file. It supports image formats supported by Eclipse. */ public void onFileOpen() { final FileDialog fileChooser = new FileDialog(getShell(), SWT.OPEN); fileChooser.setText("Open image file"); fileChooser.setFilterPath(currentDir); fileChooser.setFilterExtensions( new String[] { "*.gif; *.jpg; *.png; *.ico; *.bmp" }); fileChooser.setFilterNames( new String[] { "SWT image" + " (gif, jpeg, png, ico, bmp)" }); final String filename = fileChooser.open(); if (filename != null) { loadImage(filename); currentDir = fileChooser.getFilterPath(); } } /** * Get the image data. (for future use only) * * @return image data of canvas */ public ImageData getImageData() { return sourceImage.getImageData(); } /** * Reset the image data and update the image * * @param data * image data to be set */ public void setImageData(final ImageData data) { if (sourceImage != null) { sourceImage.dispose(); } if (data != null) { sourceImage = new Image(getDisplay(), data); } syncScrollBars(); } /** * Fit the image onto the canvas */ public void fitCanvas() { if (sourceImage == null) { return; } final Rectangle imageBound = sourceImage.getBounds(); final Rectangle destRect = getClientArea(); final double sx = (double) destRect.width / (double) imageBound.width; final double sy = (double) destRect.height / (double) imageBound.height; final double s = Math.min(sx, sy); final double dx = 0.5 * destRect.width; final double dy = 0.5 * destRect.height; centerZoom(dx, dy, s, new AffineTransform()); } /** * Show the image with the original size */ public void showOriginal() { if (sourceImage == null) { return; } transform = new AffineTransform(); syncScrollBars(); } /** * Perform a zooming operation centered on the given point (dx, dy) and * using the given scale factor. The given AffineTransform instance is * preconcatenated. * * @param dx * center x * @param dy * center y * @param scale * zoom rate * @param af * original affinetransform */ public void centerZoom(final double dx, final double dy, final double scale, final AffineTransform af) { af.preConcatenate(AffineTransform.getTranslateInstance(-dx, -dy)); af.preConcatenate(AffineTransform.getScaleInstance(scale, scale)); af.preConcatenate(AffineTransform.getTranslateInstance(dx, dy)); transform = af; syncScrollBars(); } /** * Zoom in around the center of client Area. */ public void zoomIn() { if (sourceImage == null) { return; } final Rectangle rect = getClientArea(); final int w = rect.width, h = rect.height; final double dx = (double) w / 2; final double dy = (double) h / 2; centerZoom(dx, dy, ZOOMIN_RATE, transform); } /** * Zoom out around the center of client Area. */ public void zoomOut() { if (sourceImage == null) { return; } final Rectangle rect = getClientArea(); final int w = rect.width, h = rect.height; final double dx = (double) w / 2; final double dy = (double) h / 2; centerZoom(dx, dy, ZOOMOUT_RATE, transform); } }