All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jdesktop.swingx.JXImageView Maven / Gradle / Ivy

There is a newer version: 1.7.2
Show newest version
/*
 * $Id: JXImageView.java 4248 2012-11-13 18:12:08Z kschaefe $
 *
 * Copyright 2006 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package org.jdesktop.swingx;

import org.jdesktop.beans.JavaBean;
import org.jdesktop.swingx.error.ErrorListener;
import org.jdesktop.swingx.error.ErrorSupport;
import org.jdesktop.swingx.painter.MattePainter;
import org.jdesktop.swingx.util.GraphicsUtilities;
import org.jdesktop.swingx.util.PaintUtils;

import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import javax.swing.TransferHandler;
import javax.swing.event.MouseInputAdapter;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dialog;
import java.awt.FileDialog;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Window;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 

A panel which shows an image centered. The user can drag an image into the * panel from other applications and move the image around within the view. * The JXImageView has built in actions for scaling, rotating, opening a new * image, and saving. These actions can be obtained using the relevant get*Action() * methods. *

* *

TODO: has dashed rect and text indicating you should drag there.

* * *

If the user drags more than one photo at a time into the JXImageView only * the first photo will be loaded and shown. Any errors generated internally, * such as dragging in a list of files which are not images, will be reported * to any attached {@link ErrorListener} added by the * {@link #addErrorListener}() method.

* * @author Joshua Marinacci [email protected] */ @JavaBean public class JXImageView extends JXPanel { private static final Logger LOG = Logger.getLogger(JXImageView.class.getName()); /* ======= instance variables ========= */ // the image this view will show private Image image; // the url of the image, if available private URL imageURL; // support for error listeners private final ErrorSupport errorSupport = new ErrorSupport(this); // location to draw image. if null then draw in the center private Point2D imageLocation; // the scale for drawing the image private double scale = 1.0; // controls whether the user can move images around private boolean editable = true; // the handler for moving the image around within the panel private final MoveHandler moveHandler = new MoveHandler(this); // controls the drag part of drag and drop private boolean dragEnabled = false; // controls the filename of the dropped file private String exportName = "UntitledImage"; // controls the format and filename extension of the dropped file private String exportFormat = "png"; /** * Creates a new instance of JXImageView */ public JXImageView() { // fix for: java.net/jira/browse/SWINGX-1479 setBackgroundPainter(new MattePainter(PaintUtils.getCheckerPaint(Color.white, new Color(250, 250, 250), 50))); setEditable(true); } /* ========= properties ========= */ /** * Gets the current image location. This location can be changed programmatically * or by the user dragging the image within the JXImageView. * * @return the current image location */ public Point2D getImageLocation() { return imageLocation; } /** * Set the current image location. * * @param imageLocation The new image location. */ public void setImageLocation(Point2D imageLocation) { Point2D old = getImageLocation(); this.imageLocation = imageLocation; firePropertyChange("imageLocation", old, getImageLocation()); repaint(); } /** * Gets the currently set image, or null if no image is set. * * @return the currently set image, or null if no image is set. */ public Image getImage() { return image; } /** * Sets the current image. Can set null if there should be no image show. * * @param image the new image to set, or null. */ public void setImage(Image image) { Image oldImage = getImage(); this.image = image; setImageLocation(null); setScale(1.0); firePropertyChange("image", oldImage, image); repaint(); } /** * Set the current image to an image pointed to by this URL. * * @param url a URL pointing to an image, or null * @throws IOException thrown if the image cannot be loaded */ public void setImage(URL url) throws IOException { setImageURL(url); } /** * Set the current image to an image pointed to by this File. * * @param file a File pointing to an image * @throws IOException thrown if the image cannot be loaded */ public void setImage(File file) throws IOException { setImageURL(file.toURI().toURL()); } /** * Gets the current image scale . When the scale is set to 1.0 * then one image pixel = one screen pixel. When scale < 1.0 the draw image * will be smaller than it's real size. When scale > 1.0 the drawn image will * be larger than it's real size. 1.0 is the default value. * * @return the current image scale */ public double getScale() { return scale; } /** * Sets the current image scale . When the scale is set to 1.0 * then one image pixel = one screen pixel. When scale < 1.0 the draw image * will be smaller than it's real size. When scale > 1.0 the drawn image will * be larger than it's real size. 1.0 is the default value. * * @param scale the new image scale */ public void setScale(double scale) { double oldScale = this.scale; this.scale = scale; this.firePropertyChange("scale", oldScale, scale); repaint(); } /** * Returns whether or not the user can drag images. * * @return whether or not the user can drag images */ public boolean isEditable() { return editable; } /** * Sets whether or not the user can drag images. When set to true the user can * drag the photo around with their mouse. Also the cursor will be set to the * 'hand' cursor. When set to false the user cannot drag photos around * and the cursor will be set to the default. * * @param editable whether or not the user can drag images */ public void setEditable(boolean editable) { boolean old = isEditable(); this.editable = editable; if (editable) { addMouseMotionListener(moveHandler); addMouseListener(moveHandler); this.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); try { this.setTransferHandler(new DnDHandler()); } catch (ClassNotFoundException ex) { LOG.log(Level.WARNING, "Cannot create DnD handler", ex); fireError(ex); } } else { removeMouseMotionListener(moveHandler); removeMouseListener(moveHandler); this.setCursor(Cursor.getDefaultCursor()); setTransferHandler(null); } firePropertyChange("editable", old, isEditable()); } /** * Sets the dragEnabled property, which determines whether or not * the user can drag images out of the image view and into other components or * application. Note: setting * this to true will disable the ability to move the image around within the * well., though it will not change the editable property directly. * * @param dragEnabled the value to set the dragEnabled property to. */ public void setDragEnabled(boolean dragEnabled) { boolean old = isDragEnabled(); this.dragEnabled = dragEnabled; firePropertyChange("dragEnabled", old, isDragEnabled()); } /** * Gets the current value of the dragEnabled property. * * @return the current value of the dragEnabled property */ public boolean isDragEnabled() { return dragEnabled; } /** * Adds an ErrorListener to the list of listeners to be notified * of ErrorEvents * * @param el an ErrorListener to add */ public void addErrorListener(ErrorListener el) { errorSupport.addErrorListener(el); } /** * Remove an ErrorListener from the list of listeners to be notified of ErrorEvents. * * @param el an ErrorListener to remove */ public void removeErrorListener(ErrorListener el) { errorSupport.removeErrorListener(el); } /** * Send a new ErrorEvent to all registered ErrorListeners * * @param throwable the Error or Exception which was thrown */ protected void fireError(Throwable throwable) { errorSupport.fireErrorEvent(throwable); } private static FileDialog getSafeFileDialog(Component comp) { Window win = SwingUtilities.windowForComponent(comp); if (win instanceof Dialog) { return new FileDialog((Dialog) win); } if (win instanceof Frame) { return new FileDialog((Frame) win); } return null; } // an action which will open a file chooser and load the selected image // if any. /** * Returns an Action which will open a file chooser, ask the user for an image file * then load the image into the view. If the load fails an error will be fired * to all registered ErrorListeners * * @return the action * @see ErrorListener * @deprecated see SwingX issue 990 */ @Deprecated public Action getOpenAction() { Action action = new AbstractAction() { @Override public void actionPerformed(ActionEvent actionEvent) { FileDialog fd = getSafeFileDialog(JXImageView.this); fd.setMode(FileDialog.LOAD); fd.setVisible(true); if (fd.getFile() != null) { try { setImage(new File(fd.getDirectory(), fd.getFile())); } catch (IOException ex) { fireError(ex); } } } }; action.putValue(Action.NAME, "Open"); return action; } // an action that will open a file chooser then save the current image to // the selected file, if any. /** * Returns an Action which will open a file chooser, ask the user for an image file * then save the image from the view. If the save fails an error will be fired * to all registered ErrorListeners * * @return an Action * @deprecated see SwingX issue 990 */ @Deprecated public Action getSaveAction() { Action action = new AbstractAction() { @Override public void actionPerformed(ActionEvent evt) { Image img = getImage(); BufferedImage dst = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB); Graphics2D g = (Graphics2D) dst.getGraphics(); try { // smooth scaling g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); g.drawImage(img, 0, 0, null); } finally { g.dispose(); } FileDialog fd = new FileDialog((Frame) SwingUtilities.windowForComponent(JXImageView.this)); fd.setMode(FileDialog.SAVE); fd.setVisible(true); if (fd.getFile() != null) { try { ImageIO.write(dst, "png", new File(fd.getDirectory(), fd.getFile())); } catch (IOException ex) { fireError(ex); } } } }; action.putValue(Action.NAME, "Save"); return action; } /** * Get an action which will rotate the currently selected image clockwise. * * @return an action * @deprecated see SwingX issue 990 */ @Deprecated public Action getRotateClockwiseAction() { Action action = new AbstractAction() { @Override public void actionPerformed(ActionEvent evt) { Image img = getImage(); BufferedImage src = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB); BufferedImage dst = new BufferedImage(img.getHeight(null), img.getWidth(null), BufferedImage.TYPE_INT_ARGB); Graphics2D g = (Graphics2D) src.getGraphics(); try { // smooth scaling g.drawImage(img, 0, 0, null); } finally { g.dispose(); } AffineTransform trans = AffineTransform.getRotateInstance(Math.PI / 2, 0, 0); trans.translate(0, -src.getHeight()); BufferedImageOp op = new AffineTransformOp(trans, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); op.filter(src, dst); setImage(dst); } }; action.putValue(Action.NAME, "Rotate Clockwise"); return action; } /** * Gets an action which will rotate the current image counter clockwise. * * @return an Action * @deprecated see SwingX issue 990 */ @Deprecated public Action getRotateCounterClockwiseAction() { Action action = new AbstractAction() { @Override public void actionPerformed(ActionEvent evt) { Image img = getImage(); BufferedImage src = new BufferedImage(img.getWidth(null), img.getHeight(null), BufferedImage.TYPE_INT_ARGB); BufferedImage dst = new BufferedImage(img.getHeight(null), img.getWidth(null), BufferedImage.TYPE_INT_ARGB); Graphics2D g = (Graphics2D) src.getGraphics(); try { // smooth scaling g.drawImage(img, 0, 0, null); } finally { g.dispose(); } AffineTransform trans = AffineTransform.getRotateInstance(-Math.PI / 2, 0, 0); trans.translate(-src.getWidth(), 0); BufferedImageOp op = new AffineTransformOp(trans, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); op.filter(src, dst); setImage(dst); } }; action.putValue(Action.NAME, "Rotate CounterClockwise"); return action; } /** * Gets an action which will zoom the current image out by a factor of 2. * * @return an action * @deprecated see SwingX issue 990 */ @Deprecated public Action getZoomOutAction() { Action action = new AbstractAction() { @Override public void actionPerformed(ActionEvent actionEvent) { setScale(getScale() * 0.5); } }; action.putValue(Action.NAME, "Zoom Out"); return action; } /** * Gets an action which will zoom the current image in by a factor of 2 * * @return an action * @deprecated see SwingX issue 990 */ @Deprecated public Action getZoomInAction() { Action action = new AbstractAction() { @Override public void actionPerformed(ActionEvent actionEvent) { setScale(getScale() * 2); } }; action.putValue(Action.NAME, "Zoom In"); return action; } /* === overriden methods === */ /** * Implementation detail. * * @param g */ @Override protected void paintComponent(Graphics g) { super.paintComponent(g); if (getImage() != null) { Point2D center = new Point2D.Double(getWidth() / 2, getHeight() / 2); if (getImageLocation() != null) { center = getImageLocation(); } Point2D loc = new Point2D.Double(); double width = getImage().getWidth(null) * getScale(); double height = getImage().getHeight(null) * getScale(); loc.setLocation(center.getX() - width / 2, center.getY() - height / 2); g.drawImage(getImage(), (int) loc.getX(), (int) loc.getY(), (int) width, (int) height, null); } } /* === Internal helper classes === */ private class MoveHandler extends MouseInputAdapter { private final JXImageView panel; private Point prev = null; private Point start = null; MoveHandler(JXImageView panel) { this.panel = panel; } @Override public void mousePressed(MouseEvent evt) { prev = evt.getPoint(); start = prev; } @Override public void mouseDragged(MouseEvent evt) { Point curr = evt.getPoint(); if (isDragEnabled()) { if (curr.distance(start) > 5) { LOG.fine("starting the drag: "); panel.getTransferHandler().exportAsDrag((JComponent) evt.getSource(), evt, TransferHandler.COPY); return; } } int offx = curr.x - prev.x; int offy = curr.y - prev.y; Point2D offset = getImageLocation(); if (offset == null) { if (image != null) { offset = new Point2D.Double(getWidth() / 2, getHeight() / 2); } else { offset = new Point2D.Double(0, 0); } } offset = new Point2D.Double(offset.getX() + offx, offset.getY() + offy); setImageLocation(offset); prev = curr; repaint(); } @Override public void mouseReleased(MouseEvent evt) { prev = null; } } private class DnDHandler extends TransferHandler { private DataFlavor urlFlavor; DnDHandler() throws ClassNotFoundException { urlFlavor = new DataFlavor("application/x-java-url;class=java.net.URL"); } @Override public int getSourceActions(JComponent c) { return COPY; } @Override protected void exportDone(JComponent source, Transferable data, int action) { } @Override public boolean canImport(JComponent c, DataFlavor[] flavors) { for (DataFlavor flavor : flavors) { if (DataFlavor.javaFileListFlavor.equals(flavor)) { return true; } if (DataFlavor.imageFlavor.equals(flavor)) { return true; } if (urlFlavor.match(flavor)) { return true; } } return false; } @Override protected Transferable createTransferable(JComponent c) { JXImageView view = (JXImageView) c; return new ImageTransferable(view.getImage(), view.getExportName(), view.getExportFormat()); } @Override @SuppressWarnings("unchecked") public boolean importData(JComponent comp, Transferable t) { if (canImport(comp, t.getTransferDataFlavors())) { try { if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { List files = (List) t.getTransferData(DataFlavor.javaFileListFlavor); if (files.size() > 0) { File file = files.get(0); setImageString(file.toURI().toURL().toString()); return true; } } Object obj = t.getTransferData(urlFlavor); if (obj instanceof URL) { setImageString(obj.toString()); } return true; } catch (Exception ex) { LOG.log(Level.SEVERE, ex.getMessage(), ex); fireError(ex); } } return false; } } private static class ImageTransferable implements Transferable { private final Image img; private List files; private final String exportName; private final String exportFormat; ImageTransferable(Image img, String exportName, String exportFormat) { this.img = img; this.exportName = exportName; this.exportFormat = exportFormat; } @Override public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[] {DataFlavor.imageFlavor, DataFlavor.javaFileListFlavor}; } @Override public boolean isDataFlavorSupported(DataFlavor flavor) { if (flavor == DataFlavor.imageFlavor) { return true; } return flavor == DataFlavor.javaFileListFlavor; } @Override public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { if (flavor == DataFlavor.imageFlavor) { return img; } if (flavor == DataFlavor.javaFileListFlavor) { if (files == null) { files = new ArrayList<>(); File file = File.createTempFile(exportName, "." + exportFormat); ImageIO.write(GraphicsUtilities.convertToBufferedImage(img), exportFormat, file); files.add(file); } return files; } return null; } } public String getExportName() { return exportName; } public void setExportName(String exportName) { String old = getExportName(); this.exportName = exportName; firePropertyChange("exportName", old, getExportName()); } public String getExportFormat() { return exportFormat; } public void setExportFormat(String exportFormat) { String old = getExportFormat(); this.exportFormat = exportFormat; firePropertyChange("exportFormat", old, getExportFormat()); } public URL getImageURL() { return imageURL; } public void setImageURL(URL imageURL) throws IOException { URL old = getImageURL(); this.imageURL = imageURL; firePropertyChange("imageURL", old, getImageURL()); setImage(ImageIO.read(getImageURL())); } /** * Returns the current image's URL (if available) as a string. * If the image has no URL, or if there is no image, then this * method will return null. * * @return the url of the image as a string */ public String getImageString() { if (getImageURL() == null) { return null; } return getImageURL().toString(); } /** * Sets the current image using a string. This string must * contain a valid URL. * * @param url string of a URL * @throws IOException thrown if the URL does not parse */ public void setImageString(String url) throws IOException { String old = getImageString(); setImageURL(new URL(url)); firePropertyChange("imageString", old, url); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy