/**
 * FScreenGrabber - a java application for grabbing screen shots.
 * 
 * by Fredrik Fornwall, fredrikfornwall@gmail.com (http://fornwall.net/)
 */
package net.fornwall.fscreengrabber;

import java.awt.AWTException;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.filechooser.FileFilter;

/**
 * @author Fredrik Fornwall
 */
public class FScreenGrabber {
    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) {
        }

        JFrame grabber = new JFrame();
        grabber.setTitle("FScreenGrabber");
        grabber.setContentPane(new MainPanel());
        grabber.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        grabber.pack();
        grabber.setVisible(true);
    }
}

class ImageTransferable implements Transferable {
    private DataFlavor[] dataFlavors = new DataFlavor[]{DataFlavor.imageFlavor};
    
    private Image image;
    
    public ImageTransferable(Image image) {
        this.image = image;
    }

    public DataFlavor[] getTransferDataFlavors() {
        return dataFlavors;
    }

    public boolean isDataFlavorSupported(DataFlavor flavor) {
        return flavor.equals(DataFlavor.imageFlavor);
    }

    public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
        if (!flavor.equals(DataFlavor.imageFlavor)) {
            throw new UnsupportedFlavorException(flavor);
        }
        return image;
    }
    
}

class FullScreenPicker extends JFrame implements MouseListener, MouseMotionListener {

    private ImageDisplay imageDisplay;

    private GraphicsDevice device;

    private GraphicsEnvironment env;

    private BufferedImage image;

    private Robot robot;

    private int pressY = -1;

    private int pressX = -1;

    boolean mousePressed = false;

    public FullScreenPicker(ImageDisplay imageDisplay) {
        this.imageDisplay = imageDisplay;

        addMouseMotionListener(this);
        setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));

        setContentPane(new JPanel() {
            public void paint(Graphics g) {
                g.drawImage(image, 0, 0, null);
            }
        });

        addMouseListener(this);

        env = GraphicsEnvironment.getLocalGraphicsEnvironment();
        device = env.getDefaultScreenDevice();
        setUndecorated(true);
    }

    public void mouseClicked(MouseEvent e) {
    }

    public void mouseDragged(MouseEvent arg0) {
    }

    public void mouseEntered(MouseEvent arg0) {
    }

    public void mouseExited(MouseEvent arg0) {
    }

    private int lastX = -1;

    private int lastY = -1;

    public void mouseMoved(MouseEvent e) {
        if (mousePressed) {
            Graphics g = getGraphics();
            g.setXORMode(Color.WHITE);

            int x = Math.min(pressX, lastX);
            int y = Math.min(pressY, lastY);
            int width = Math.abs(pressX - lastX);
            int height = Math.abs(pressY - lastY);
            if (lastX != -1 && lastY != -1) {
                // Erase old rectangle
                g.setXORMode(Color.WHITE);
                g.drawRect(x, y, width, height);
                g.setXORMode(Color.BLUE);
                g.fillRect(x, y, width, height);
            }

            lastX = e.getX();
            lastY = e.getY();

            x = Math.min(pressX, lastX);
            y = Math.min(pressY, lastY);
            width = Math.abs(pressX - lastX);
            height = Math.abs(pressY - lastY);

            g.setXORMode(Color.WHITE);
            g.drawRect(x, y, width, height);
            g.setXORMode(Color.BLUE);
            g.fillRect(x, y, width, height);
        }

        lastX = e.getX();
        lastY = e.getY();
    }

    public void mousePressed(MouseEvent e) {
        if (mousePressed) {
            if (e.getX() == pressX || e.getY() == pressY) {
                return;
            }
            int x = Math.min(pressX, lastX);
            int y = Math.min(pressY, lastY);
            int width = Math.abs(pressX - lastX);
            int height = Math.abs(pressY - lastY);
            imageDisplay.setImage(image.getSubimage(x, y, width, height));
            dispose();
        } else {
            mousePressed = true;
            pressX = e.getX();
            pressY = e.getY();
        }
    }

    public void mouseReleased(MouseEvent arg0) {
    }

    public void paint(Graphics g) {
        g.drawImage(image, 0, 0, null);
    }

    public void start() throws AWTException {
        mousePressed = false;

        if (robot == null) {
            robot = new Robot();
        }
        image = robot
                .createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));

        setVisible(true);

        if (this != device.getFullScreenWindow()) {
            device.setFullScreenWindow(this);
        }
    }

}

class ImageDisplay extends JPanel implements ComponentListener, DragGestureListener {

    BufferedImage image;

    Image scaledImage;

    boolean scale = false;

    
    public Dimension getPreferredSize() {
        if (image == null) {
            return new Dimension(600, 600);
        } else {
            return new Dimension(image.getWidth(), image.getHeight());
        }
    }

    public ImageDisplay() {
        addComponentListener(this);
        DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, this);
    }

    public void paint(Graphics g) {
        if (image != null) {
            if (!scale) {
                g.setColor(getBackground());
                g.fillRect(0, 0, getWidth(), getHeight());
            }
            g.drawImage((scale) ? scaledImage : image, 0, 0, null);
        } else {
            g.fillRect(0, 0, getWidth(), getHeight());
        }
    }

    public BufferedImage getImage() {
        if (scale) {
            BufferedImage result = (BufferedImage) createImage(getWidth(), getHeight());
            result.getGraphics().drawImage(scaledImage, 0, 0, null);
            return result;
        } else {
            return image;
        }
    }

    public void setImage(BufferedImage image) {
        this.image = image;
        if (scale) {
            doScale();
        } else {
            repaint();
        }
    }

    public void setScaled(boolean scale) {
        this.scale = scale;
        if (scale) {
            doScale();
        } else {
            repaint();
        }
    }

    private void doScale() {
        if (image != null) {
            scaledImage = image.getScaledInstance(getWidth(), getHeight(), Image.SCALE_SMOOTH);
            repaint();
        }
    }

    public void componentResized(ComponentEvent e) {
        if (scale && image != null) {
            doScale();
        }
    }

    public void componentMoved(ComponentEvent arg0) {
    }

    public void componentShown(ComponentEvent arg0) {
    }

    public void componentHidden(ComponentEvent arg0) {
    }

    public void dragGestureRecognized(DragGestureEvent dge) {
        dge.startDrag(DragSource.DefaultCopyDrop, new ImageTransferable(scale ? getImage(): image));
    }

}

class MainPanel extends JPanel implements ActionListener {
    private JButton exitButton = new JButton(new AbstractAction("Exit") {
        {
            putValue(Action.SHORT_DESCRIPTION, "The short description");
            putValue(Action.LONG_DESCRIPTION, "A really long description");
        }

        public void actionPerformed(ActionEvent e) {
            System.exit(0);
        }
    });

    private JButton grabButton = new JButton("Grab");

    private ImageDisplay imageDisplay = new ImageDisplay();

    private FullScreenPicker picker = new FullScreenPicker(imageDisplay);

    private JButton saveButton = new JButton("Save");

    private JCheckBox scaleCheck = new JCheckBox("Scale");

    private JFileChooser saveFileChooser = new JFileChooser();

    public MainPanel() {
        setLayout(new GridBagLayout());
        GridBagConstraints gc = new GridBagConstraints();
        gc.insets = new Insets(5, 5, 5, 5);

        gc.gridx = gc.gridy = 0;
        gc.weightx = 1.0;
        gc.weighty = 1.0;
        gc.gridwidth = 6;
        gc.fill = GridBagConstraints.BOTH;
        add(imageDisplay, gc);

        gc.anchor = GridBagConstraints.EAST;
        gc.weightx = 1.0;
        gc.weighty = 0.0;
        gc.fill = GridBagConstraints.HORIZONTAL;
        gc.gridy++;
        gc.gridwidth = 1;

        gc.gridx++;
        add(scaleCheck, gc);
        gc.gridx++;
        add(grabButton, gc);
        gc.gridx++;
        add(saveButton, gc);
        gc.gridx++;
        add(exitButton, gc);

        grabButton.addActionListener(this);
        saveButton.addActionListener(this);
        //exitButton.addActionListener(this);
        scaleCheck.addActionListener(this);

        scaleCheck.setMnemonic(KeyEvent.VK_C);
        grabButton.setMnemonic(KeyEvent.VK_G);
        saveButton.setMnemonic(KeyEvent.VK_S);
        exitButton.setMnemonic(KeyEvent.VK_X);

        saveButton.setEnabled(false);

        saveFileChooser.setFileFilter(new FileFilter() {
            public boolean accept(File f) {
                return f.isDirectory() || f.getName().toLowerCase().endsWith(".png");
            }

            public String getDescription() {
                return "png";
            }
        });
    }

    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == exitButton) {
            System.exit(0);
        } else if (e.getSource() == grabButton) {
            try {
                picker.start();
                saveButton.setEnabled(true);
            } catch (AWTException ex) {
                JOptionPane.showMessageDialog(this, "Error doing screen grab:\n" + ex.getMessage());
            }
        } else if (e.getSource() == scaleCheck) {
            imageDisplay.setScaled(scaleCheck.isSelected());
        } else if (e.getSource() == saveButton) {
            if (saveFileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) {
                try {
                    ImageIO.write((BufferedImage) imageDisplay.getImage(), "png", saveFileChooser
                            .getSelectedFile());
                } catch (IOException ex) {
                    ex.printStackTrace();
                    JOptionPane.showMessageDialog(this, "Error saving image:\n" + ex.getMessage());
                }
            }
        }
    }
}
