/*
* Open Source Physics software is free software as described near the bottom of this code file.
*
* For additional information and documentation on Open Source Physics please see:
* <http://www.opensourcephysics.org/>
*/
package org.opensourcephysics.tools;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.File;
import java.rmi.RemoteException;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import javax.swing.ImageIcon;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;
import org.jibble.epsgraphics.EpsGraphics2D;
import org.opensourcephysics.display.OSPRuntime;
import org.opensourcephysics.display.Renderable;
import org.opensourcephysics.media.gif.GIFEncoder;
/**
* This provides a simple way to capture screen images.
*
* @author Francisco Esquembre (http://fem.um.es)
* @author Wolfgang Christian
* @version 1.0
*/
public class SnapshotTool implements Tool {
// ---- Localization
static private final String BUNDLE_NAME = "org.opensourcephysics.resources.tools.tools"; //$NON-NLS-1$
static private ResourceBundle res = ResourceBundle.getBundle(BUNDLE_NAME);
static public void setLocale(Locale locale) {
res = ResourceBundle.getBundle(BUNDLE_NAME, locale);
}
static public String getString(String key) {
try {
return res.getString(key);
} catch(MissingResourceException e) {
return '!'+key+'!';
}
}
/**
* The singleton shared translator tool.
*/
private static SnapshotTool TOOL = new SnapshotTool();
private static JFileChooser chooser;
/**
* Gets the shared SnapshotTool.
*
* @return the shared SnapshotTool
*/
public static SnapshotTool getTool() {
if(TOOL==null) {
TOOL = new SnapshotTool();
}
return TOOL;
}
static protected void createChooser() {
String[] names = javax.imageio.ImageIO.getWriterFormatNames();
String[] allNames = new String[names.length+2];
allNames[0] = "gif"; //$NON-NLS-1$
allNames[1] = "eps"; //$NON-NLS-1$
for(int i = 0; i<names.length; i++) {
allNames[i+2] = names[i];
}
chooser = OSPRuntime.createChooser(res.getString("SnapshotTool.ImageFiles"), allNames); //$NON-NLS-1$
}
/**
* Private constructor.
*/
private SnapshotTool() {
String name = "SnapshotTool"; //$NON-NLS-1$
createChooser();
Toolbox.addTool(name, this);
}
/**
* Sends a job to this tool and specifies a tool to reply to.
*
* @param job the Job
* @param replyTo the tool to notify when the job is complete (may be null)
* @throws RemoteException
*/
public void send(Job job, Tool replyTo) throws RemoteException {}
/**
* Saves the image produced by a component
* <p>
* @param filename the name of a file
* @param component the component to get the image from
* @return true if the file was correctly saved
*/
public boolean saveImage(String filename, Component component) {
return saveImage(filename, component, null);
}
/**
* Saves the image produced by a component to an output stream
* <p>
* @param filename the name of a file (the extension indicates the format). If null, teh user will be prompted for a name
* @param component the component to get the image from
* @param output An optional output stream to save to. If null the image is saved to a file
* @return true if the image was correctly saved
*/
public boolean saveImage(String filename, Component component, java.io.OutputStream output) {
return saveImage(filename, component, output, 1.0);
}
/**
* Saves the (possibly scaled) image produced by a component to an output stream
* <p>
* @param filename the name of a file (the extension indicates the format). If null, the user will be prompted for a name
* @param component the component to get the image from
* @param output An optional output stream to save to. If null the image is saved to a file
* @param scale A scale factor that resizes the image. A value of 1 uses the actual size.
* @return true if the image was correctly saved
*/
public boolean saveImage(String filename, Component component, java.io.OutputStream output, double scale) {
// Generate the image to display in the chooser (will be final if scale = 1)
if(component==null) {
return false;
}
Component originalComponent = component;
if(component instanceof JFrame) {
component = ((JFrame) component).getContentPane();
} else if(component instanceof JDialog) {
component = ((JDialog) component).getContentPane();
}
BufferedImage bi = new BufferedImage(component.getWidth(), component.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
if(component instanceof Renderable) {
bi = ((Renderable) component).render(bi);
} else {
java.awt.Graphics g = bi.getGraphics();
component.paint(g);
g.dispose();
}
// we need either an output stream or a file name to save the image.
if((output==null)&&(filename==null)) { // Select target file and scale
JLabel label = new JLabel();
label.setIcon(new ImageIcon(bi));
/* This code is temporarily disabled because it doesn't work properly for all types of components
*/
JLabel labelScale = new JLabel(res.getString("SnapshotTool.Scale")); //$NON-NLS-1$
labelScale.setBorder(new javax.swing.border.EmptyBorder(0, 5, 0, 5));
JTextField scaleField = new JTextField(Double.toString(scale));
JPanel scalePanel = new JPanel(new BorderLayout());
scalePanel.add(labelScale, BorderLayout.WEST);
scalePanel.add(scaleField, BorderLayout.CENTER);
JPanel panel = new JPanel(new BorderLayout());
panel.add(label, BorderLayout.CENTER);
panel.add(scalePanel, BorderLayout.SOUTH);
chooser.setAccessory(panel);
// chooser.setAccessory(label);
chooser.setSelectedFile(new File("default.jpg")); // filename is null so set a default name. //$NON-NLS-1$
filename = OSPRuntime.chooseFilename(chooser);
scale = Double.parseDouble(scaleField.getText());
}
if(filename==null) {
return false; // user hit cancel or file name not given
}
// Find the desired format
String format = "jpg"; //$NON-NLS-1$
int index = filename.lastIndexOf('.');
if(index>=0) {
format = filename.substring(index+1).toLowerCase();
} else {
filename = filename+"."+format; //$NON-NLS-1$
}
boolean supported = isImageFormatSupported(format);
if(!(supported||"gif".equalsIgnoreCase(format)||"eps".equalsIgnoreCase(format))) { //$NON-NLS-1$ //$NON-NLS-2$
String[] message = new String[] {res.getString("SnapshotTool.FormatNotSupported"), //$NON-NLS-1$
res.getString("SnapshotTool.PreferredFormats")}; //$NON-NLS-1$
JOptionPane.showMessageDialog((JFrame) null, message, res.getString("SnapshotTool.Error"), JOptionPane.WARNING_MESSAGE); //$NON-NLS-1$
return false;
}
Dimension originalSize = null;
Component componentResized = null;
int finalWidth = component.getWidth(), finalHeight = component.getHeight();
// Resize if scale is not 1
if(scale<=0.0) {
scale = 1.0;
}
if(scale!=1.0) { // Take the image again, now enlarged
originalSize = originalComponent.getSize();
finalWidth = (int) (originalSize.width*scale);
finalHeight = (int) (originalSize.height*scale);
// Do the rescaling. Also required for EPS images
if(component instanceof Renderable) {
bi = new BufferedImage(finalWidth, finalHeight, BufferedImage.TYPE_3BYTE_BGR);
bi = ((Renderable) component).render(bi);
component.invalidate();
} else { // resize the Swing element prior to taking the picture
componentResized = originalComponent;
componentResized.setSize(finalWidth, finalHeight);
componentResized.validate();
bi = new BufferedImage(component.getWidth(), component.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
java.awt.Graphics g = bi.getGraphics();
component.paint(g);
g.dispose();
finalWidth = component.getWidth(); // for the case of EPS files
finalHeight = component.getHeight();
}
}
boolean result = true;
try { // Do the job
if(output==null) {
output = new java.io.FileOutputStream(filename);
}
if(supported) {
result = javax.imageio.ImageIO.write(bi, format, output);
} else if("eps".equalsIgnoreCase(format)) { //$NON-NLS-1$
EpsGraphics2D g = new EpsGraphics2D("", output, 0, 0, finalWidth, finalHeight); //$NON-NLS-1$
g.drawImage(bi, new java.awt.geom.AffineTransform(), null);
// component.paint(g); This won't work for resized Renderables
g.scale(0.24, 0.24); // Set resolution to 300 dpi (0.24 = 72/300)
// g.setColorDepth(EpsGraphics2D.BLACK_AND_WHITE); // Black & white
g.close();
} else { // use GIFEncoder
try {
GIFEncoder encoder = new GIFEncoder(bi);
encoder.Write(output);
} catch(Exception exc) {
result = false;
exc.printStackTrace();
}
}
output.close();
} catch(Exception _exc) {
_exc.printStackTrace();
result = false;
}
// Undo possible resizing
if(componentResized!=null) {
componentResized.setSize(originalSize.width, originalSize.height);
componentResized.validate();
}
return result;
}
static public boolean isImageFormatSupported(String format) {
try {
String[] names = javax.imageio.ImageIO.getWriterFormatNames();
for(int i = 0; i<names.length; i++) {
if(names[i].equalsIgnoreCase(format)) {
return true;
}
}
} catch(Exception ex) {}
return false;
}
// methods and classes below added by Doug Brown, 9 June 2007
/**
* Copies the specified image to the system clipboard.
*
* @param image the image to copy
*/
public void copyImage(Image image) {
TransferImage transfer = new TransferImage(image);
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(transfer, null);
}
/**
* Returns the image on the clipboard, if any.
*
* @return the image, or null if none found
*/
public Image getClipboardImage() {
Transferable t = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null);
try {
if((t!=null)&&t.isDataFlavorSupported(DataFlavor.imageFlavor)) {
Image image = (Image) t.getTransferData(DataFlavor.imageFlavor);
return image;
}
} catch(Exception ex) {
ex.printStackTrace();
}
return null;
}
/**
* Copies an image of a component to the clipboard
*
* @param component the component to copy
*/
public void copyImage(Component component) {
new ComponentImage(component).copyToClipboard();
}
/**
* Prints an image of a component
*
* @param component the component to print
*/
public void printImage(Component component) {
new ComponentImage(component).print();
}
/**
* ComponentImage class for creating and printing images of components.
*/
class ComponentImage implements Printable {
private BufferedImage image;
Component c;
/**
* Constructor ComponentImage
* @param comp
*/
public ComponentImage(Component comp) {
c = comp;
if(comp instanceof JFrame) {
comp = ((JFrame) comp).getContentPane();
} else if(comp instanceof JDialog) {
comp = ((JDialog) comp).getContentPane();
}
image = new BufferedImage(comp.getWidth(), comp.getHeight(), BufferedImage.TYPE_3BYTE_BGR);
if(comp instanceof Renderable) {
image = ((Renderable) comp).render(image);
} else {
java.awt.Graphics g = image.getGraphics();
comp.paint(g);
g.dispose();
}
}
public Image getImage() {
return image;
}
public void copyToClipboard() {
copyImage(image);
}
public void print() {
PrinterJob printerJob = PrinterJob.getPrinterJob();
PageFormat format = new PageFormat();
java.awt.print.Book book = new java.awt.print.Book();
book.append(this, format);
printerJob.setPageable(book);
if(printerJob.printDialog()) {
try {
printerJob.print();
} catch(PrinterException pe) {
// JOptionPane.showMessageDialog(c,
// TrackerRes.getString("TActions.Dialog.PrintError.Message"), //$NON-NLS-1$
// TrackerRes.getString("TActions.Dialog.PrintError.Title"), //$NON-NLS-1$
// JOptionPane.ERROR_MESSAGE);
}
}
}
/**
* Implements Printable.
*
* @param g the printer graphics
* @param pageFormat the format
* @param pageIndex the page number
* @return status code
*/
public int print(Graphics g, PageFormat pageFormat, int pageIndex) {
if(pageIndex>=1) { // only one page available
return Printable.NO_SUCH_PAGE;
}
if(g==null) {
return Printable.NO_SUCH_PAGE;
}
Graphics2D g2 = (Graphics2D) g;
double scalex = pageFormat.getImageableWidth()/image.getWidth();
double scaley = pageFormat.getImageableHeight()/image.getHeight();
double scale = Math.min(scalex, scaley);
scale = Math.min(scale, 1.0); // don't magnify images--only reduce if nec
g2.translate((int) pageFormat.getImageableX(), (int) pageFormat.getImageableY());
g2.scale(scale, scale);
g2.drawImage(image, 0, 0, null);
return Printable.PAGE_EXISTS;
}
}
/**
* Transferable class for copying images to the system clipboard.
*/
class TransferImage implements Transferable {
private Image image;
/**
* Constructor TransferImage
* @param image
*/
public TransferImage(Image image) {
this.image = image;
}
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[] {DataFlavor.imageFlavor};
}
public boolean isDataFlavorSupported(DataFlavor flavor) {
return DataFlavor.imageFlavor.equals(flavor);
}
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException {
if(!isDataFlavorSupported(flavor)) {
throw new UnsupportedFlavorException(flavor);
}
return image;
}
}
// end methods and classes added by Doug Brown, 9 June 2007
}
/*
* Open Source Physics software is free software; you can redistribute
* it and/or modify it under the terms of the GNU General Public License (GPL) as
* published by the Free Software Foundation; either version 2 of the License,
* or(at your option) any later version.
*
* Code that uses any portion of the code in the org.opensourcephysics package
* or any subpackage (subdirectory) of this package must must also be be released
* under the GNU GPL license.
*
* This software 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 this; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA
* or view the license online at http://www.gnu.org/copyleft/gpl.html
*
* Copyright (c) 2007 The Open Source Physics project
* http://www.opensourcephysics.org
*/