Java tutorial
/* * This file is part of the ZoRa project: http://www.photozora.org. * * ZoRa is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * ZoRa is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with ZoRa; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * (c) 2009-2018 Berthold Daum */ package com.bdaum.zoom.ui.internal.views; import java.awt.geom.Rectangle2D; import java.io.File; import java.io.IOException; import java.net.URI; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.IPreferencesService; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.osgi.util.NLS; import org.eclipse.swt.SWT; import org.eclipse.swt.events.HelpEvent; import org.eclipse.swt.events.HelpListener; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseListener; import org.eclipse.swt.events.MouseMoveListener; import org.eclipse.swt.events.MouseWheelListener; 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.Device; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.graphics.Region; import org.eclipse.swt.graphics.TextLayout; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.ToolTip; import org.eclipse.ui.IWorkbenchWindow; import org.piccolo2d.util.PAffineTransform; import org.piccolo2d.util.PAffineTransformException; import com.bdaum.zoom.batch.internal.ExifTool; import com.bdaum.zoom.batch.internal.IFileWatcher; import com.bdaum.zoom.cat.model.asset.Asset; import com.bdaum.zoom.core.Constants; import com.bdaum.zoom.core.Core; import com.bdaum.zoom.core.internal.CoreActivator; import com.bdaum.zoom.core.internal.peer.AssetOrigin; import com.bdaum.zoom.core.internal.peer.ConnectionLostException; import com.bdaum.zoom.core.internal.peer.IPeerService; import com.bdaum.zoom.css.internal.CssActivator; import com.bdaum.zoom.image.ImageConstants; import com.bdaum.zoom.image.ImageUtilities; import com.bdaum.zoom.image.ZImage; import com.bdaum.zoom.image.recipe.Recipe; import com.bdaum.zoom.ui.IFrameListener; import com.bdaum.zoom.ui.IFrameProvider; import com.bdaum.zoom.ui.IStateListener; import com.bdaum.zoom.ui.ITransformListener; import com.bdaum.zoom.ui.ITransformProvider; import com.bdaum.zoom.ui.Ui; import com.bdaum.zoom.ui.dialogs.AcousticMessageDialog; import com.bdaum.zoom.ui.internal.Icons; import com.bdaum.zoom.ui.internal.UiActivator; import com.bdaum.zoom.ui.internal.UiConstants; import com.bdaum.zoom.ui.internal.hover.HoverInfo; import com.bdaum.zoom.ui.internal.widgets.FadingShell; import com.bdaum.zoom.ui.preferences.PreferenceConstants; @SuppressWarnings("restriction") public class ImageViewer extends AbstractMediaViewer implements HelpListener, UiConstants, IFrameProvider, ITransformProvider { private static final long USEPREVIEWTHRESHOLD = 4000000L; private static final double MAXZOOM = 4d; private static final int INCREMENT = 10; private static final int PAGEINCREMENT = 100; protected static final int HOTSPOTSIZE = 48; private final class InertiaMousePanListener implements MouseListener, MouseMoveListener { double xSpeed, ySpeed; double lag = 0.7d; double stop = 0.05d; boolean mouseDown = false; boolean altClick = false; Point targetPnt = new Point(0, 0); Point mousePnt = new Point(0, 0); private long lastTime; private int mouseButton; private int softness; private ScheduledFuture<?> panTask; private double previousRatio = Double.NaN; public InertiaMousePanListener() { softness = Platform.getPreferencesService().getInt(UiActivator.PLUGIN_ID, PreferenceConstants.WHEELSOFTNESS, 50, null); lag = softness * 0.005d + 0.45d; } public void cancel() { if (panTask != null) { panTask.cancel(true); panTask = null; } xSpeed = ySpeed = 0d; } public void mouseDoubleClick(MouseEvent e) { if (hotspot.contains(e.x, e.y)) { fireStateEvent(IStateListener.SYNC); fireTransformEvent(); } else if (highResVisible && e.button == 1) { cancel(); mousePoint.x = e.x; mousePoint.y = e.y; resetView(); } } public void mouseDown(MouseEvent e) { if (hotspot.contains(e.x, e.y)) fireStateEvent(IStateListener.SYNC); else { mouseButton = e.button; if (e.count == 1 && highResVisible) { mouseDown = true; targetPnt.x = mousePoint.x = mousePnt.x = e.x; targetPnt.y = mousePoint.y = mousePnt.y = e.y; altClick = e.button == 3 && modMask == SWT.BUTTON3 || (e.stateMask & modMask) == modMask; lastTime = ((long) e.time) & 0x00000000FFFFFFFF; lastMouseX = e.x; boolean enlarged = isEnlarged(); setCursorForObject(e, enlarged ? CURSOR_GRABBING : null, CURSOR_MMINUS, CURSOR_MPLUS, null); if (!enlarged && e.button == 1) { previousRatio = 1 / viewTransform.getScale(); fix(1, e.x, e.y); } } } } public void mouseUp(MouseEvent e) { if (!Double.isNaN(previousRatio)) { fix(previousRatio, e.x, e.y); previousRatio = Double.NaN; } mouseDown = false; String cursor = isEnlarged() ? CURSOR_OPEN_HAND : null; setCursorForObject(e, cursor, cursor, cursor, null); } public void mouseMove(final MouseEvent e) { final double scale = viewTransform.getScale(); boolean enlarged = scale > wheelListener.getMinScale(); if (mouseDown) { if (softness == 0) { setCursorForObject(e, enlarged ? CURSOR_GRABBING : null, CURSOR_MMINUS, CURSOR_MPLUS, null); double f = (altClick ? 0.4d / scale : 1d); xSpeed = (e.x - mousePnt.x) * f; ySpeed = (e.y - mousePnt.y) * f; targetPnt.x = e.x; targetPnt.y = e.y; performMouseAction(e); mousePnt.x = e.x; mousePnt.y = e.y; return; } long time = ((long) e.time) & 0x00000000FFFFFFFF; long dt = time - lastTime; if (dt > 0) { double f = (altClick ? 40d / scale : 120d / Math.sqrt(scale)) / dt; xSpeed = (xSpeed + (e.x - mousePnt.x) * f) / 2; ySpeed = (ySpeed + (e.y - mousePnt.y) * f) / 2; } lastTime = time; if (panTask == null && xSpeed != 0 && ySpeed != 0) { targetPnt.x = e.x; targetPnt.y = e.y; panTask = UiActivator.getScheduledExecutorService().scheduleAtFixedRate(() -> { if (e.display.isDisposed() || topCanvas.isDisposed()) xSpeed = ySpeed = 0; else { e.display.syncExec(() -> { if (!topCanvas.isDisposed()) performMouseAction(e); }); xSpeed *= lag; ySpeed *= lag; } if (Math.abs(xSpeed) < stop && Math.abs(ySpeed) < stop) InertiaMousePanListener.this.cancel(); }, 0L, 60L, TimeUnit.MILLISECONDS); } } if (hotspot.contains(e.x, e.y)) { topCanvas.setCursor(e.display.getSystemCursor(SWT.CURSOR_HAND)); systemCursorSet = true; return; } if (systemCursorSet) { topCanvas.setCursor(UiActivator.getDefault().getCursor(e.display, currentCustomCursor)); systemCursorSet = false; } mousePnt.x = e.x; mousePnt.y = e.y; if (mouseDown) { e.button = mouseButton; String zoomCursor = (xSpeed < 0) ? CURSOR_MMINUS : CURSOR_MPLUS; setCursorForObject(e, enlarged ? CURSOR_GRABBING : null, zoomCursor, zoomCursor, null); } else { String cursor = enlarged ? CURSOR_OPEN_HAND : null; setCursorForObject(e, cursor, cursor, cursor, enlarged ? null : CURSOR_MPLUS); } } private void performMouseAction(MouseEvent e) { if (altClick) zoom(xSpeed, targetPnt.x, targetPnt.y); else pan(xSpeed, ySpeed); } } public class HighResJob extends Job { private File imageFile; private boolean adv; private int cms1; private boolean firstCall; private ToolTip tooltip; public HighResJob(File imageFile, boolean advanced, int cms, boolean subSampling) { super("ImageLoading"); //$NON-NLS-1$ this.imageFile = imageFile; this.adv = advanced; this.cms1 = cms; this.firstCall = subSampling; setSystem(true); setPriority(Job.INTERACTIVE); } @Override public boolean belongsTo(Object family) { return Constants.DAEMONS == family; } @Override protected IStatus run(final IProgressMonitor monitor) { String opId = java.util.UUID.randomUUID().toString(); IFileWatcher fileWatcher = CoreActivator.getDefault().getFileWatchManager(); try { MultiStatus status = new MultiStatus(UiActivator.PLUGIN_ID, 0, Messages.getString("ImageViewer.Image_loading_report"), null); //$NON-NLS-1$ try { boolean secondCallPossible = false; final MultiStatus status1 = status; if (!firstCall) { for (int i = 0; i < 16; i++) { if (topShell.isDisposed()) break; display.syncExec(() -> { topShell.setAlpha(Math.max(0, topShell.getAlpha() - 16)); }); try { Thread.sleep(40); } catch (InterruptedException ex) { // do nothing } } image.dispose(); image = null; } image = CoreActivator.getDefault().getHighresImageLoader().loadImage(null, status1, imageFile, asset.getRotation(), asset.getFocalLengthIn35MmFilm(), null, firstCall ? 1d : 0d, 1d, adv, cms1, bwmode, null, cropmode == ZImage.ORIGINAL ? Recipe.NULL : null, fileWatcher, opId, null); if (image != null) { display.syncExec(() -> { if (!topCanvas.isDisposed()) { Rectangle area = topCanvas.getClientArea(); image.develop(monitor, display, cropmode, area.width, area.height, ZImage.SWTIMAGE); } }); int superSamplingFactor = image.getSuperSamplingFactor(); ibounds = image.getBounds(); secondCallPossible = firstCall && superSamplingFactor > 1; final FadingShell formerShell = (previewShown) ? previewShell : bottomShell; fadein(status1, monitor, topShell, topCanvas, formerShell, !secondCallPossible); highResVisible = true; if (secondCallPossible) { if (status1.isOK()) { display.syncExec(() -> { Shell sh = topShell.getShell(); Rectangle bounds = sh.getBounds(); tooltip = new ToolTip(sh, SWT.NONE); tooltip.setLocation(bounds.x + bounds.width / 3, bounds.y); tooltip.setText(NLS.bind(Messages.getString("ImageViewer.downsampled"), //$NON-NLS-1$ superSamplingFactor)); tooltip.setMessage(Messages.getString("ImageViewer.click_for_fullres")); //$NON-NLS-1$ tooltip.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { highResVisible = false; viewTransform = null; tooltip.setVisible(false); highResJob = new HighResJob(file, advanced, cms, false); highResJob.schedule(); } }); tooltip.setVisible(true); }); while (!display.isDisposed()) { display.syncExec(() -> { if (formerShell.isDisposed()) monitor.setCanceled(true); else if (!tooltip.isVisible() && highResVisible) { formerShell.close(); monitor.setCanceled(true); } }); if (monitor.isCanceled()) break; try { Thread.sleep(100); } catch (InterruptedException e) { break; } } } } else { display.syncExec(() -> { if (!formerShell.isDisposed()) formerShell.close(); }); } } } catch (UnsupportedOperationException e) { loadFailed = e.getMessage(); display.syncExec(() -> { if (!bottomCanvas.isDisposed() && bottomShell.isVisible()) bottomCanvas.redraw(); if (!previewCanvas.isDisposed() && previewShell.isVisible()) previewCanvas.redraw(); }); } return status; } finally { fileWatcher.stopIgnoring(opId); } } } public class PreviewJob extends Job { private File ifile; public PreviewJob(File imageFile) { super("PreviewLoading"); //$NON-NLS-1$ ifile = imageFile; setSystem(true); setPriority(Job.INTERACTIVE); } @Override public boolean belongsTo(Object family) { return Constants.DAEMONS == family; } @Override protected IStatus run(IProgressMonitor monitor) { MultiStatus status = new MultiStatus(UiActivator.PLUGIN_ID, 0, Messages.getString("ImageViewer.Preview_loading_report"), null); //$NON-NLS-1$ try (ExifTool tool = new ExifTool(ifile, true)) { previewImage = tool.getPreviewImage(false); if (previewImage != null && image == null) { ExifTool.fixOrientation(previewImage, asset.getOrientation(), asset.getRotation()); previewImage.develop(monitor, display, ZImage.UNCROPPED, -1, -1, ZImage.SWTIMAGE); previewImage.getSwtImage(display, false, ZImage.UNCROPPED, SWT.DEFAULT, SWT.DEFAULT); previewShown = true; fadein(status, monitor, previewShell, previewCanvas, bottomShell, true); return status; } } catch (Exception e) { // do nothing - ignore preview image } return status; } } private final class InertiaMouseWheelListener implements MouseWheelListener { double nonlinearity = 0.33333333d; double currentSpeed; double minScale = 0.01d; double maxScale = 100d; double sensitivity = 0.8d; double lag = 0.8d; private int softness; private ScheduledFuture<?> wheelTask; public InertiaMouseWheelListener() { softness = UiActivator.getDefault().getPreferenceStore().getInt(PreferenceConstants.WHEELSOFTNESS); lag = softness * 0.003d + 0.65d; } public double getMinScale() { return minScale; } public void setMinScale(double minScale) { this.minScale = minScale; } public double getMaxScale() { return maxScale; } public void setMaxScale(double maxScale) { this.maxScale = maxScale; } public void cancel() { if (wheelTask != null) { wheelTask.cancel(true); wheelTask = null; } currentSpeed = 0d; } public void mouseScrolled(final MouseEvent e) { if (!highResVisible) return; if (softness == 0) { currentSpeed = e.count; zoom(currentSpeed, e.x, e.y); return; } currentSpeed += sensitivity * e.count * Math.pow(Math.abs(e.count), nonlinearity); if (wheelTask == null && currentSpeed != 0) { wheelTask = UiActivator.getScheduledExecutorService().scheduleAtFixedRate(() -> { if (e.display.isDisposed()) currentSpeed = 0; else { e.display.syncExec(() -> { if (!((Canvas) e.widget).isDisposed()) zoom(currentSpeed, e.x, e.y); }); currentSpeed = currentSpeed * lag; } if (Math.abs(currentSpeed) < lag) InertiaMouseWheelListener.this.cancel(); }, 0L, 60L, TimeUnit.MILLISECONDS); } } } public String loadFailed; private static final double EPSILON = 0.005d; private ZImage image; private Rectangle ibounds; private ZImage previewImage; private PAffineTransform viewTransform; private FadingShell bottomShell; private FadingShell previewShell; private FadingShell topShell; private Canvas bottomCanvas; private File file; private TextLayout tlayout; private HighResJob highResJob; private Canvas topCanvas; private InertiaMouseWheelListener wheelListener; private java.awt.Rectangle rectSrc = new java.awt.Rectangle(); private java.awt.Rectangle rectDst = new java.awt.Rectangle(); private Point mousePoint = new Point(0, 0); private boolean advanced; private int cms; private Canvas previewCanvas; private PreviewJob previewJob; private boolean highResVisible = false; private boolean previewShown = false; private boolean preview; private InertiaMousePanListener panListener; private int lastMouseX; private int modMask; private Shell controlShell; private Canvas controlCanvas; private String currentCustomCursor; private Region controlRegion; private Job transferJob; private Image bwImage; private boolean enlarge; private boolean addNoise; private double oldRatio = -1d; private double oldScale; private int oldKeyCode; private long oldTime; private int canvasXoffset; private int canvasYoffset; private ListenerList<IFrameListener> frameListeners = new ListenerList<>(); private ListenerList<ITransformListener> transformListeners = new ListenerList<>(); private Rectangle2D currentFrame = new Rectangle2D.Double(0d, 0d, 1d, 1d); private boolean sync = false; private boolean vertical; protected Rectangle hotspot = new Rectangle(0, 0, 0, 0); protected boolean systemCursorSet = false; private void fadein(MultiStatus status, IProgressMonitor monitor, final FadingShell shell, final Canvas canvas, final FadingShell formerShell, boolean closeFormer) { if (monitor.isCanceled()) { status.add(Status.CANCEL_STATUS); return; } if (!canvas.isDisposed()) display.syncExec(() -> { if (!canvas.isDisposed()) { canvas.redraw(); shell.setActive(); } }); for (int i = 0; i < 16; i++) { if (shell.isDisposed()) break; if (monitor.isCanceled()) break; display.syncExec(() -> { shell.setAlpha(Math.min(255, shell.getAlpha() + 16)); }); try { Thread.sleep(60); } catch (InterruptedException e) { // do nothing } } if (!display.isDisposed()) display.syncExec(() -> { if (closeFormer) formerShell.close(); if (!shell.isDisposed()) { shell.forceActive(); shell.forceFocus(); canvas.setFocus(); } }); } /* * (nicht-Javadoc) * * @see * com.bdaum.zoom.ui.views.IMediaViewer#init(org.eclipse.ui.IWorkbenchWindow, * org.eclipse.swt.graphics.RGB, int) */ public void init(IWorkbenchWindow window, int kind, RGB bw, int crop) { super.init(window, kind, bw, crop); IPreferencesService preferencesService = Platform.getPreferencesService(); advanced = preferencesService.getBoolean(UiActivator.PLUGIN_ID, PreferenceConstants.ADVANCEDGRAPHICS, false, null); cms = preferencesService.getInt(UiActivator.PLUGIN_ID, PreferenceConstants.COLORPROFILE, ImageConstants.SRGB, null); preview = preferencesService.getBoolean(UiActivator.PLUGIN_ID, PreferenceConstants.PREVIEW, false, null); int zoomKey = preferencesService.getInt(UiActivator.PLUGIN_ID, PreferenceConstants.ZOOMKEY, SWT.ALT, null); enlarge = preferencesService.getBoolean(UiActivator.PLUGIN_ID, PreferenceConstants.ENLARGESMALL, false, null); addNoise = preferencesService.getBoolean(UiActivator.PLUGIN_ID, PreferenceConstants.ADDNOISE, true, null); switch (zoomKey) { case PreferenceConstants.ZOOMALT: modMask = SWT.ALT; break; case PreferenceConstants.ZOOMRIGHT: modMask = SWT.BUTTON3; break; case PreferenceConstants.ZOOMSHIFT: modMask = SWT.SHIFT; break; default: modMask = -1; break; } } /* * (nicht-Javadoc) * * @see com.bdaum.zoom.ui.internal.AbstractKiosk#create() */ public void create() { super.create(); bottomShell = new FadingShell(createKioskShell(Messages.getString("ImageViewer.lowres_viewer")), false, //$NON-NLS-1$ -1); bottomCanvas = new Canvas(bottomShell.getShell(), SWT.DOUBLE_BUFFERED); previewShell = new FadingShell(createKioskShell(Messages.getString("ImageViewer.preview_viewer")), true, //$NON-NLS-1$ Constants.SLIDE_TRANSITION_FADE); previewCanvas = new Canvas(previewShell.getShell(), SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND); topShell = new FadingShell(createKioskShell(Messages.getString("ImageViewer.highres_viewer")), true, //$NON-NLS-1$ Constants.SLIDE_TRANSITION_FADE); topCanvas = new Canvas(topShell.getShell(), SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND); controlShell = new Shell(display, SWT.NO_TRIM); controlRegion = new Region(display); controlRegion.add(new int[] { 4, 1, 44, 1, 47, 4, 47, 43, 44, 46, 4, 46, 1, 43, 1, 4 }); controlShell.setRegion(controlRegion); controlShell.setText(Constants.APPNAME); controlShell.setLayout(new FillLayout()); controlCanvas = new Canvas(controlShell, SWT.DOUBLE_BUFFERED | SWT.NO_BACKGROUND); controlCanvas.addMouseListener(new MouseAdapter() { @Override public void mouseUp(MouseEvent e) { close(); } }); bottomCanvas.addKeyListener(this); previewCanvas.addKeyListener(this); MouseMoveListener mouseMoveListener = new MouseMoveListener() { public void mouseMove(MouseEvent e) { Canvas canvas = (Canvas) e.widget; Rectangle clientArea = canvas.getClientArea(); if (e.y < clientArea.y + 2 * HOTSPOTSIZE) { if (e.x < clientArea.x + 2 * HOTSPOTSIZE && kind != RIGHT) showCloseButton(canvas.toDisplay(clientArea.x, clientArea.y)); else if (e.x > clientArea.x + clientArea.width - 2 * HOTSPOTSIZE && kind != LEFT) showCloseButton( canvas.toDisplay(clientArea.x + clientArea.width - HOTSPOTSIZE, clientArea.y)); else controlShell.setVisible(false); } else controlShell.setVisible(false); if (controlShell.getVisible()) controlShell.forceActive(); } private void showCloseButton(Point pnt) { controlShell.setLocation(pnt); controlShell.setVisible(true); } }; topCanvas.addMouseMoveListener(mouseMoveListener); bottomCanvas.addMouseMoveListener(mouseMoveListener); previewCanvas.addMouseMoveListener(mouseMoveListener); topCanvas.addKeyListener(this); topCanvas.addHelpListener(this); wheelListener = new InertiaMouseWheelListener(); wheelListener.setMinScale(1d); wheelListener.setMaxScale(MAXZOOM); topCanvas.addMouseWheelListener(wheelListener); panListener = new InertiaMousePanListener(); topCanvas.addMouseListener(panListener); topCanvas.addMouseMoveListener(panListener); PaintListener listener = new PaintListener() { public void paintControl(PaintEvent e) { Canvas canvas = (Canvas) e.widget; Shell shell = canvas.getShell(); Rectangle sbnds = canvas.getClientArea(); CssActivator.getDefault().setColors(shell); GC gc = e.gc; gc.setBackground(shell.getBackground()); gc.fillRectangle(sbnds); if (!highResVisible) { Image im = previewImage != null ? previewImage.getSwtImage(display, false, ZImage.UNCROPPED, SWT.DEFAULT, SWT.DEFAULT) : bwmode != null ? getBwImage(asset, bwmode) : getImage(asset); if (im != null) { Rectangle ibnds = im.getBounds(); double factor = Math.min((double) sbnds.width / ibnds.width, (double) sbnds.height / ibnds.height); if (!enlarge) { double factor2 = Math.min((double) sbnds.width / asset.getWidth(), (double) sbnds.height / asset.getHeight()); if (factor2 > 1d) factor /= factor2; } int w = (int) (ibnds.width * factor); int h = (int) (ibnds.height * factor); gc.drawImage(im, 0, 0, ibnds.width, ibnds.height, (sbnds.width - w) / 2, (sbnds.height - h) / 2, w, h); } String volume = asset.getVolume(); String text; if (file == null) text = volume == null || volume.trim().isEmpty() ? Messages.getString("ImageViewer.highres_not_available") //$NON-NLS-1$ : NLS.bind(Messages.getString("ImageViewer.high_res_image_not_available"), //$NON-NLS-1$ volume); else if (loadFailed != null) text = loadFailed; else text = ((previewImage != null) ? Messages.getString("ImageViewer.loading_highres") : //$NON-NLS-1$ Messages.getString("ImageViewer.loading_thumbnail")) //$NON-NLS-1$ + "\n\n" + getKeyboardHelp(false) + '\n'; //$NON-NLS-1$ if (tlayout == null) { tlayout = new TextLayout(display); tlayout.setAlignment(SWT.CENTER); tlayout.setWidth(sbnds.width); tlayout.setFont(JFaceResources.getFont(UiConstants.VIEWERFONT)); } tlayout.setText(text); Rectangle tbounds = tlayout.getBounds(); gc.setForeground( display.getSystemColor((file == null || loadFailed != null) ? SWT.COLOR_DARK_RED : (previewImage != null) ? SWT.COLOR_DARK_CYAN : SWT.COLOR_DARK_GREEN)); int y = (sbnds.height - tbounds.height) / 2; tlayout.draw(gc, 1, y + 1); gc.setForeground(display.getSystemColor((file == null || loadFailed != null) ? SWT.COLOR_RED : (previewImage != null) ? SWT.COLOR_CYAN : SWT.COLOR_GREEN)); tlayout.draw(gc, 0, y); } } }; bottomCanvas.addPaintListener(listener); previewCanvas.addPaintListener(listener); topCanvas.addPaintListener(new PaintListener() { public void paintControl(PaintEvent e) { if (image != null) { Rectangle sbounds = topCanvas.getClientArea(); Shell shell = topShell.getShell(); CssActivator.getDefault().setColors(shell); GC gc = e.gc; gc.setBackground(shell.getBackground()); gc.fillRectangle(sbounds); int iwidth = ibounds.width; int iheight = ibounds.height; if (viewTransform == null) { viewTransform = new PAffineTransform(); double zoomfactor = Math.min((double) sbounds.width / iwidth, (double) sbounds.height / iheight); if (!enlarge && zoomfactor > 1d) zoomfactor = 1d; wheelListener.setMinScale(zoomfactor); viewTransform.scale(zoomfactor, zoomfactor); } rectSrc.setBounds(ibounds.x, ibounds.y, iwidth, iheight); viewTransform.transform(rectSrc, rectDst); int canvasWidth = Math.min((int) (rectDst.getWidth()), sbounds.width); int canvasHeight = Math.min((int) (rectDst.getHeight()), sbounds.height); canvasXoffset = Math.max(0, (sbounds.width - canvasWidth) / 2); canvasYoffset = Math.max(0, (sbounds.height - canvasHeight) / 2); rectSrc.setBounds(0, 0, canvasWidth, canvasHeight); viewTransform.inverseTransform(rectSrc, rectDst); int cropWidth = Math.min((int) (rectDst.getWidth()), iwidth); int cropHeight = Math.min((int) (rectDst.getHeight()), iheight); int cropXoffset = Math.max(0, Math.min(iwidth - cropWidth, (int) (rectDst.getX()))); int cropYoffset = Math.max(0, Math.min(iheight - cropHeight, (int) (rectDst.getY()))); if (advanced) { gc.setAntialias(SWT.ON); gc.setInterpolation(SWT.HIGH); } try { image.draw(gc, cropXoffset, cropYoffset, cropWidth, cropHeight, canvasXoffset, canvasYoffset, canvasWidth, canvasHeight, cropmode, sbounds.width, sbounds.height, true); currentFrame.setFrame((double) cropXoffset / iwidth, (double) cropYoffset / iheight, (double) cropWidth / iwidth, (double) cropHeight / iheight); } catch (Exception e1) { UiActivator.getDefault().logError(Messages.getString("ImageViewer.error_when_resizing"), //$NON-NLS-1$ e1); } gc.setBackground(topShell.getShell().getBackground()); if (canvasXoffset > 0) gc.fillRectangle(0, 0, canvasXoffset, sbounds.height); if (canvasXoffset + canvasWidth < sbounds.width) gc.fillRectangle(canvasXoffset + canvasWidth, 0, sbounds.width - (canvasXoffset + canvasWidth), sbounds.height); if (canvasYoffset > 0) gc.fillRectangle(0, 0, sbounds.width, canvasYoffset); if (canvasYoffset + canvasHeight < sbounds.height) gc.fillRectangle(0, canvasYoffset + canvasHeight, sbounds.width, sbounds.height - (canvasYoffset + canvasHeight)); if (!transformListeners.isEmpty()) { Image img = sync ? Icons.sync32.getImage() : Icons.sync32d.getImage(); Rectangle bounds = img.getBounds(); int x, y; if (vertical) { hotspot.width = bounds.width; hotspot.height = bounds.height / 2; hotspot.x = x = 0; y = kind == LEFT ? sbounds.height - hotspot.height : -hotspot.height; hotspot.y = kind == LEFT ? y : 0; } else { hotspot.width = bounds.width / 2; hotspot.height = bounds.height; x = kind == LEFT ? sbounds.width - hotspot.width : -hotspot.width; hotspot.x = kind == LEFT ? x : 0; hotspot.y = y = sbounds.height - 2 * bounds.height; } gc.setBackground( display.getSystemColor(sync ? SWT.COLOR_GREEN : SWT.COLOR_WIDGET_NORMAL_SHADOW)); gc.fillOval(x - 2, y - 2, bounds.width + 4, bounds.height + 4); gc.drawImage(img, x, y); } } } }); bottomCanvas.redraw(); controlCanvas.addPaintListener(new PaintListener() { public void paintControl(PaintEvent e) { e.gc.drawImage(Icons.closeButton.getImage(), 0, 0); } }); controlCanvas.redraw(); controlShell.setBounds(Constants.OSX ? mbounds.x : mbounds.x + mbounds.width - HOTSPOTSIZE, mbounds.y, HOTSPOTSIZE, HOTSPOTSIZE); controlShell.setVisible(false); bottomShell.layout(); previewShell.layout(); topShell.layout(); Ui.getUi().getFrameManager().registerFrameProvider(this); } private Image getBwImage(Asset asset, RGB filter) { if (bwImage == null) { Image image = getImage(asset); Device device = image.getDevice(); ImageData imageData = image.getImageData(); image.dispose(); ImageUtilities.convert2Bw(imageData, filter); bwImage = new Image(device, imageData); } return bwImage; } private String getKeyboardHelp(boolean help) { String msg = NLS.bind(help ? Messages.getString("ImageViewer.use_mouse_wheel2") //$NON-NLS-1$ : Messages.getString("ImageViewer.use_mouse_wheel"), //$NON-NLS-1$ modMask == SWT.BUTTON3 ? Messages.getString("ImageViewer.right_mouse_button") //$NON-NLS-1$ : modMask == SWT.ALT ? Messages.getString("ImageViewer.ALT") //$NON-NLS-1$ : Messages.getString("ImageViewer.SHIFT"));//$NON-NLS-1$ if (!transformListeners.isEmpty()) msg += Messages.getString("ImageViewer.use_capslock"); //$NON-NLS-1$ return msg; } /* * (non-Javadoc) * * @see com.bdaum.zoom.ui.views.IImageViewer#open(com.bdaum.zoom.cat.model.asset * .Asset) */ public void open(Asset[] assets) throws IOException { asset = assets[0]; URI uri = Core.getCore().getVolumeManager().findExistingFile(asset, false); File tempFile = null; if (uri != null) { if (Constants.FILESCHEME.equals(uri.getScheme())) { IPeerService peerService = Core.getCore().getPeerService(); AssetOrigin assetOrigin = peerService != null ? peerService.getAssetOrigin(asset.getStringId()) : null; if (peerService != null && assetOrigin != null) { try { if (peerService.checkCredentials(IPeerService.VIEW, asset.getSafety(), assetOrigin)) { transferJob = peerService.scheduleTransferJob(asset, assetOrigin); if (transferJob != null) { while (true) { try { tempFile = file = transferJob.getAdapter(File.class); Job.getJobManager().join(transferJob, null); break; } catch (OperationCanceledException e) { transferJob = null; return; } catch (InterruptedException e) { // just continue } } } } else { AcousticMessageDialog.openError(null, Messages.getString("ImageViewer.access_restricted"), //$NON-NLS-1$ Messages.getString("ImageViewer.rights_not_sufficient")); //$NON-NLS-1$ return; } } catch (ConnectionLostException e) { AcousticMessageDialog.openError(null, Messages.getString("ImageViewer.connection_lost"), //$NON-NLS-1$ e.getLocalizedMessage()); return; } } else file = new File(uri); } else tempFile = file = Core.download(uri, null); } if (bottomShell == null) create(); int previewSize = asset.getPreviewSize(); int mxsize = Math.max(mbounds.width, mbounds.height); mousePoint.x = mbounds.width / 2; mousePoint.y = mbounds.height / 2; boolean usePreview = preview && ((previewSize < 0 || previewSize * 2 >= mxsize) && asset.getRotation() == 0); bottomShell.open(); if (file != null) { usePreview &= file.length() > USEPREVIEWTHRESHOLD; if (usePreview) { Rectangle ibnds = Core.getCore().getImageCache().getImage(asset).getBounds(); usePreview = (Math.max(ibnds.width, ibnds.height) < mxsize); } if (usePreview) { previewShell.open(); previewJob = new PreviewJob(file); previewJob.schedule(); } topShell.open(); highResJob = new HighResJob(file, advanced, cms, true); highResJob.schedule(); } if (kind == PRIMARY) while (!isDisposed()) if (!display.readAndDispatch()) display.sleep(); if (tempFile != null) tempFile.delete(); } @Override public boolean isDisposed() { if (file != null) return topShell == null || topShell.isDisposed(); return bottomShell == null || bottomShell.isDisposed(); } public void releaseKey(KeyEvent e) { long time = e.time & 0xFFFFFFFFL; if (oldKeyCode != e.keyCode || time - oldTime >= 100L) { oldTime = time; oldKeyCode = e.keyCode; if (e.character == Messages.getString("ImageViewer.w").charAt(0)) //$NON-NLS-1$ fix((double) ibounds.width / topShell.getBounds().width, mousePoint.x, mousePoint.y); else if (e.character == Messages.getString("ImageViewer.h").charAt(0)) //$NON-NLS-1$ fix((double) ibounds.height / topShell.getBounds().height, mousePoint.x, mousePoint.y); else switch (e.character) { case SWT.TAB: if (highResVisible) close(); break; case '+': zoom(2, mousePoint.x, mousePoint.y); break; case '-': zoom(-2, mousePoint.x, mousePoint.y); break; case ' ': case '*': resetView(); break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': fix(e.character - '0', mousePoint.x, mousePoint.y); break; default: if ((e.stateMask & SWT.SHIFT) != 0) { switch (e.keyCode) { case '1': case '2': case '3': case '4': fix(1d / (e.keyCode - '0'), mousePoint.x, mousePoint.y); break; } break; } int f = (e.stateMask & SWT.CONTROL) != 0 ? 5 : 1; switch (e.keyCode) { case SWT.ESC: close(); break; case SWT.ARROW_LEFT: pan(-INCREMENT * f, 0); break; case SWT.ARROW_RIGHT: pan(INCREMENT * f, 0); break; case SWT.ARROW_UP: pan(0, -INCREMENT * f); break; case SWT.ARROW_DOWN: pan(0, INCREMENT * f); break; case SWT.HOME: pan(-PAGEINCREMENT * f, 0); break; case SWT.END: pan(PAGEINCREMENT * f, 0); break; case SWT.PAGE_UP: pan(0, -PAGEINCREMENT * f); break; case SWT.PAGE_DOWN: pan(0, PAGEINCREMENT * f); break; case SWT.F2: metadataRequested(asset); break; case SWT.F4: if (kind != PRIMARY) fireStateEvent(IStateListener.SYNC); break; } break; } } } public boolean close() { currentFrame.setFrame(0d, 0d, 1d, 1d); fireFrameEvent(); Ui.getUi().getFrameManager().deregisterFrameProvider(this); fireStateEvent(IStateListener.CLOSED); if (transferJob != null) Job.getJobManager().cancel(transferJob); if (previewJob != null) previewJob.cancel(); if (previewImage != null) previewImage.dispose(); if (bwImage != null) bwImage.dispose(); if (highResJob != null) highResJob.cancel(); if (topShell != null) { topShell.close(); topShell = null; } if (previewShell != null) { previewShell.close(); previewShell = null; } if (bottomShell != null) { bottomShell.close(); bottomShell = null; } if (controlShell != null) { controlShell.close(); controlShell = null; } if (controlRegion != null) { controlRegion.dispose(); controlRegion = null; } if (tlayout != null) { tlayout.dispose(); tlayout = null; } if (image != null) { image.dispose(); image = null; } return super.close(); } private void resetView() { wheelListener.cancel(); panListener.cancel(); if (isEnlarged()) { viewTransform.setToIdentity(); oldRatio = wheelListener.getMinScale(); viewTransform.setToScale(oldRatio, oldRatio); } else fix(1, mousePoint.x, mousePoint.y); topCanvas.redraw(); fireTransformEvent(); fireFrameEvent(); } private void pan(double xDir, double yDir) { viewTransform.translate(xDir, yDir); rectSrc.setBounds(ibounds.x, ibounds.y, ibounds.width, ibounds.height); viewTransform.transform(rectSrc, rectDst); Rectangle clientArea = topCanvas.getClientArea(); if (rectDst.x > 0) viewTransform.translate(-rectDst.x, 0); else viewTransform.translate(-Math.min(0, rectDst.x + rectDst.width - clientArea.width), 0); if (rectDst.y > 0) viewTransform.translate(0, -rectDst.y); else viewTransform.translate(0, -Math.min(0, rectDst.y + rectDst.height - clientArea.height)); topCanvas.redraw(); fireTransformEvent(); fireFrameEvent(); } private void zoom(double factor, double x, double y) { zoomDelta(1d + factor * 0.01d, x - viewTransform.getTranslateX(), y - viewTransform.getTranslateY()); } private void fix(double ratio, double x, double y) { double scale = viewTransform.getScale(); double vx = viewTransform.getTranslateX(); double vy = viewTransform.getTranslateY(); if (oldRatio == ratio) { zoomDelta(oldScale / scale, x - vx, y - vy); oldRatio = -1d; } else { if (oldRatio < 0) oldScale = scale; oldRatio = ratio; zoomDelta(1 / (ratio * scale), x - vx, y - vy); if (!isEnlarged()) oldRatio = -1d; } } private void zoomDelta(double delta, double x, double y) { double scale = viewTransform.getScale(); double newScale = scale * delta; double minScale = Math.min(1d / 9, wheelListener.getMinScale()); if ((newScale >= minScale || scale > minScale) && newScale <= wheelListener.getMaxScale()) { newScale = Math.max(newScale, minScale); delta = newScale / scale; try { viewTransform.translate((x - canvasXoffset) / scale, (y - canvasYoffset) / scale); viewTransform.scale(delta, delta); viewTransform.translate(-(x - canvasXoffset) / scale, -(y - canvasYoffset) / scale); topCanvas.redraw(); fireTransformEvent(); fireFrameEvent(); } catch (PAffineTransformException e) { // ignore } } } private void setCursorForObject(MouseEvent e, String surface, String altLeft, String altRight, String dflt) { int button = e.button; if (button == 3 && modMask == SWT.BUTTON3 || button == 1 && (e.stateMask & modMask) == modMask) { setCanvasCursor((e.x >= lastMouseX) ? altRight : altLeft); lastMouseX = e.x; } else if (button != 0) setCanvasCursor(surface); else if (button == 0 && dflt != null) setCanvasCursor(dflt); } private void setCanvasCursor(String cursor) { if (cursor != currentCustomCursor) { currentCustomCursor = cursor; topCanvas.setCursor(UiActivator.getDefault().getCursor(display, cursor)); } } public void helpRequested(HelpEvent e) { ToolTip toolTip = new ToolTip(topShell.getShell(), SWT.BALLOON); toolTip.setAutoHide(true); toolTip.setLocation(mbounds.x, mbounds.y); toolTip.setText(Messages.getString("ImageViewer.image_viewer")); //$NON-NLS-1$ toolTip.setMessage(getKeyboardHelp(true)); toolTip.setVisible(true); } private void metadataRequested(Asset asset) { ToolTip toolTip = new ToolTip(topShell.getShell(), SWT.BALLOON); toolTip.setAutoHide(true); toolTip.setLocation(mbounds.x + mbounds.width / 2, mbounds.y + mbounds.height / 2); toolTip.setText(NLS.bind(Messages.getString("SlideShowPlayer.metadata"), asset.getName())); //$NON-NLS-1$ toolTip.setMessage(new HoverInfo(asset, (ImageRegion[]) null).getText()); toolTip.setVisible(true); } public String getName() { return Messages.getString("ImageViewer.viewer_name"); //$NON-NLS-1$ } public String getId() { return "com.bdaum.zoom.image"; //$NON-NLS-1$ } private Image getImage(Asset asset) { Image im = Core.getCore().getImageCache().getImage(asset); return (addNoise) ? ImageUtilities.applyNoise(im) : im; } private boolean isEnlarged() { return viewTransform.getScale() > wheelListener.getMinScale() + EPSILON; } @Override public void addFrameListener(IFrameListener listener) { frameListeners.add(listener); } @Override public Rectangle2D getCurrentFrame() { return currentFrame; } @Override public void removeFrameListener(IFrameListener listener) { frameListeners.remove(listener); } private void fireFrameEvent() { for (IFrameListener listener : frameListeners) listener.frameChanged(this, asset.getStringId(), currentFrame.getX(), currentFrame.getY(), currentFrame.getWidth(), currentFrame.getHeight()); } @Override public void addTransformListener(ITransformListener listener) { transformListeners.add(listener); } @Override public void removeTransformListener(ITransformListener listener) { transformListeners.remove(listener); } private void fireTransformEvent() { if (viewTransform != null && !isDisposed()) for (ITransformListener listener : transformListeners) listener.transformChanged(this, viewTransform.getTranslateX(), viewTransform.getTranslateY(), viewTransform.getScale()); } @Override public void setTransform(double translateX, double translateY, double scale) { if (viewTransform == null || viewTransform.getTranslateX() != translateX || viewTransform.getTranslateY() != translateY || viewTransform.getScale() != scale) { if (viewTransform == null) viewTransform = new PAffineTransform(); else viewTransform.setToIdentity(); viewTransform.setScale(scale); viewTransform.setOffset(translateX, translateY); topCanvas.redraw(); fireTransformEvent(); fireFrameEvent(); } } @Override public void setSync(boolean sync, boolean vertical) { this.sync = sync; this.vertical = vertical; if (topShell.isVisible()) topCanvas.redraw(); } }