org.spotter.eclipse.ui.viewers.ResourceViewer.java Source code

Java tutorial

Introduction

Here is the source code for org.spotter.eclipse.ui.viewers.ResourceViewer.java

Source

/**
 * Copyright 2014 SAP AG
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.spotter.eclipse.ui.viewers;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseTrackAdapter;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.graphics.Cursor;
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.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spotter.eclipse.ui.Activator;
import org.spotter.eclipse.ui.navigator.SpotterProjectParent;
import org.spotter.eclipse.ui.util.DialogUtils;
import org.spotter.eclipse.ui.util.ImageUtils;

/**
 * A resource viewer which offers the possibility to view different types of
 * resource files such as images or pdfs.
 * <p>
 * The viewer looks best if placed within a composite with a
 * <code>FillLayout</code> or similar in order to use all the available space.
 * </p>
 * 
 * @author Denis Knoepfle
 * 
 */
public class ResourceViewer {

    private static final String NOT_AVAILABLE_IMG_TEXT = "not available";
    private static final String DLG_RESOURCE_TITLE = "Resource '%s'%s";
    private static final String PDF_EXTENSION = "pdf";
    private static final int PDF_IMAGE_TYPE = BufferedImage.TYPE_INT_RGB;
    private static final int PDF_VIEW_RESOLUTION = 96; // [dpi]

    private static final Logger LOGGER = LoggerFactory.getLogger(ResourceViewer.class);

    private String projectName;
    private String resourceFile;
    private Map<String, Shell> resourceShells;

    private Canvas canvas;
    private ImageData resourceImageData;
    private Image resourceImage;

    /**
     * Creates a resource viewer under the given parent.
     * 
     * @param parent
     *            the parent of this viewer
     */
    public ResourceViewer(Composite parent) {
        this.projectName = null;
        this.resourceFile = null;
        this.resourceShells = new HashMap<>();
        this.canvas = new Canvas(parent, SWT.NONE);
        addCanvasListeners();
    }

    /**
     * Clears the viewer and disposes of all images.
     */
    public void clear() {
        projectName = null;
        resourceFile = null;
        if (resourceImage != null) {
            resourceImage.dispose();
            resourceImage = null;
        }
        resourceImageData = null;
        canvas.setBackgroundImage(null);
    }

    /**
     * Disposes of this viewer. This method should be called by clients to clean
     * up.
     */
    public void dispose() {
        Set<Shell> shells = new HashSet<>();
        shells.addAll(resourceShells.values());
        for (Shell shell : shells) {
            shell.close();
        }

        if (resourceImage != null) {
            resourceImage.dispose();
            resourceImage = null;
        }
    }

    /**
     * Sets the resource of the viewer.
     * 
     * @param resourceFile
     *            the absolute filename of the file to display
     * @param projectName
     *            the name of the project the resource belongs to or
     *            <code>null</code>
     */
    public void setResource(String resourceFile, String projectName) {
        if (resourceImage != null) {
            resourceImage.dispose();
            resourceImage = null;
        }
        resourceImageData = null;
        this.resourceFile = resourceFile;
        this.projectName = projectName;
        File file = new File(resourceFile);
        int canvasWidth = canvas.getBounds().width;
        int canvasHeight = canvas.getBounds().height;
        boolean isCanvasVisible = canvasWidth > 0 && canvasHeight > 0;

        if (isCanvasVisible) {
            boolean loadSuccessful = false;
            if (file.exists()) {
                try {
                    resourceImageData = loadImageData(resourceFile);
                    int width = Math.min(canvasWidth, resourceImageData.width);
                    int height = Math.min(canvasHeight, resourceImageData.height);
                    resourceImage = new Image(canvas.getDisplay(), resourceImageData.scaledTo(width, height));
                    loadSuccessful = true;
                } catch (SWTException e) {
                    String message = "Could not load the resource!";
                    String cause;
                    switch (e.code) {
                    case SWT.ERROR_IO:
                        cause = "I/O exception occured";
                        break;
                    case SWT.ERROR_INVALID_IMAGE:
                        cause = "Image file contains invalid data";
                        break;
                    case SWT.ERROR_UNSUPPORTED_FORMAT:
                        cause = "Image file contains an unsupported or unrecognized format";
                        break;
                    default:
                        cause = "unknown";
                        break;
                    }
                    DialogUtils.openError(DialogUtils.appendCause(message, cause, true));
                    LOGGER.error(DialogUtils.appendCause(message, cause, false), e);
                }
            }
            if (!loadSuccessful) {
                // draw "not available image" picture using GC
                final Display display = canvas.getDisplay();
                final Rectangle bounds = canvas.getBounds();
                resourceImage = new Image(display, bounds);
                drawNotAvailableImage(resourceImage, display, bounds);
            }
        }

        canvas.setBackgroundImage(resourceImage);
    }

    private ImageData loadImageData(String resourceFile) {
        File file = new File(resourceFile);
        Path path = file.toPath().getFileName();
        String name = path == null ? null : path.toString();
        if (name == null) {
            return new ImageData(resourceFile);
        }
        int index = name.lastIndexOf('.');
        String extension = index == -1 ? null : name.substring(index + 1);

        if (PDF_EXTENSION.equalsIgnoreCase(extension)) {
            return createImageDataFromPdf(resourceFile);
        } else {
            return new ImageData(resourceFile);
        }
    }

    // creates image data from the first page of the pdf file
    private ImageData createImageDataFromPdf(String resourceFile) {
        try {
            PDDocument document = PDDocument.load(resourceFile);
            @SuppressWarnings("unchecked")
            List<PDPage> pages = document.getDocumentCatalog().getAllPages();
            if (pages.isEmpty()) {
                throw new SWTException(SWT.ERROR_INVALID_IMAGE);
            }
            BufferedImage bufferedImage = pages.get(0).convertToImage(PDF_IMAGE_TYPE, PDF_VIEW_RESOLUTION);
            return ImageUtils.convertToImageData(bufferedImage);
        } catch (IOException e) {
            throw new SWTException(SWT.ERROR_IO);
        }
    }

    private void addCanvasListeners() {
        canvas.addMouseTrackListener(new MouseTrackAdapter() {

            @Override
            public void mouseEnter(MouseEvent e) {
                if (resourceImage != null) {
                    Cursor zoomCursor = Display.getDefault().getSystemCursor(SWT.CURSOR_HAND);
                    canvas.setCursor(zoomCursor);
                }
            }

            @Override
            public void mouseExit(MouseEvent e) {
                canvas.setCursor(null);
            }
        });

        canvas.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseDown(MouseEvent e) {
                if (resourceFile != null && resourceImageData != null) {
                    if (resourceShells.containsKey(resourceFile)) {
                        resourceShells.get(resourceFile).setFocus();
                    } else {
                        openResourcePopupShell(e.display, resourceFile);
                    }
                }
            }
        });

        canvas.addControlListener(new ControlAdapter() {
            @Override
            public void controlResized(ControlEvent e) {
                resizeCanvasImage();
            }
        });
    }

    private void resizeCanvasImage() {
        if (resourceFile == null) {
            return;
        } else if (resourceImageData == null) {
            setResource(resourceFile, projectName);
            return;
        }

        if (resourceImage != null) {
            resourceImage.dispose();
        }
        int canvasWidth = canvas.getBounds().width;
        int canvasHeight = canvas.getBounds().height;
        if (canvasWidth > 0 && canvasHeight > 0) {
            int width = Math.min(canvasWidth, resourceImageData.width);
            int height = Math.min(canvasHeight, resourceImageData.height);
            resourceImage = new Image(canvas.getDisplay(), resourceImageData.scaledTo(width, height));
            canvas.setBackgroundImage(resourceImage);
        }
    }

    private void drawNotAvailableImage(Image image, Display display, Rectangle bounds) {
        GC gc = new GC(image);
        gc.setBackground(display.getSystemColor(SWT.COLOR_WHITE));
        gc.setForeground(display.getSystemColor(SWT.COLOR_RED));

        gc.fillRectangle(0, 0, bounds.width, bounds.height);
        gc.drawLine(0, 0, bounds.width, bounds.height);
        gc.drawLine(0, bounds.height, bounds.width, 0);

        final int flags = SWT.DRAW_TRANSPARENT;
        Point textExtent = gc.textExtent(NOT_AVAILABLE_IMG_TEXT, flags);

        int x = (bounds.width - textExtent.x) / 2;
        int y = bounds.height - textExtent.y;
        gc.drawText(NOT_AVAILABLE_IMG_TEXT, x, y, flags);
        gc.dispose();
    }

    private void openResourcePopupShell(final Display display, final String resourceFile) {
        final Shell popupShell = new Shell(SWT.BORDER | SWT.RESIZE | SWT.CLOSE);
        Label label = new Label(popupShell, SWT.NONE);
        Rectangle clientArea = display.getPrimaryMonitor().getClientArea();

        final Image image = new Image(display, ImageUtils.clampToClientArea(clientArea, resourceImageData));
        label.setImage(image);

        popupShell.setLayout(new FillLayout());
        popupShell.setImage(Activator.getImage(SpotterProjectParent.IMAGE_PATH));
        String projectSuffix = projectName != null ? " (" + projectName + ")" : "";

        Path resourcePath = new File(resourceFile).toPath().getFileName();
        String resourceName = resourcePath != null ? resourcePath.toString() : "-";
        popupShell.setText(String.format(DLG_RESOURCE_TITLE, resourceName, projectSuffix));
        popupShell.pack();

        Rectangle shellRect = popupShell.getBounds();
        int x = clientArea.x + (clientArea.width - shellRect.width) / 2;
        int y = clientArea.y + (clientArea.height - shellRect.height) / 2;
        popupShell.setLocation(x, y);

        popupShell.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (e.keyCode == SWT.ESC) {
                    popupShell.close();
                }
            }
        });
        popupShell.addShellListener(new ShellAdapter() {
            @Override
            public void shellClosed(ShellEvent e) {
                image.dispose();
                resourceShells.remove(resourceFile);
            }
        });

        resourceShells.put(resourceFile, popupShell);
        popupShell.open();
    }

}