/*
 * @(#)ImageView.java  1.2  2011-01-06
 *
 * Copyright (c) 1999-2011 Werner Randelshofer, Immensee, Switzerland.
 * All rights reserved.
 *
 * You may not use, copy or modify this file, except in compliance with the
 * license agreement you entered into with Werner Randelshofer.
 * For details see accompanying license terms.
 */
package ch.randelshofer.view;

import org.monte.media.gui.ImagePanel;
import ch.randelshofer.util.PropertyModel;
import ch.randelshofer.util.ResourceBundleUtil;

import java.awt.*;
import java.awt.dnd.*;
import java.awt.image.*;
import java.awt.event.*;

import java.beans.*;

import java.io.*;

import javax.activation.*;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

/**
 * View for Image files supported by the standard Java API (GIF and JPG for all
 * JDK's up to 1.2).
 *
 * @author  Werner Randelshofer, Hausmatt 10, CH-6405 Immensee, Switzerland
 * @version 1.2 2011-01-06 Adds information about color model to info panel.
 * <br>1.1.1 2010-09-08 Fixes a memory leak.
 * <br>1.1.0.1 2001-06-17 Converses class to JDK 1.3.
 * <br>1.1   2000-09-28 Removes suffix '_' from instance variable names.
 * <br>1.0.1   2000-06-11 Reads text constants from a ResourceBundle.
 * <br>1.0   1999-10-19 Created.
 */
public class ImageView extends AbstractView implements ActionListener, ItemListener {

    protected ImagePanel imagePanel;
    private JRadioButtonMenuItem ignorePixelAspectItem, multipliedPixelAspectItem, exactPixelAspectItem;
    private JRadioButtonMenuItem ignoreImageAspectItem, exactImageAspectItem;
    private JRadioButtonMenuItem backgroundWhiteItem, backgroundBlackItem, backgroundCheckerboardItem;
    private JRadioButtonMenuItem nearestNeighborItem, bilinearItem, bicubicItem;
    private JCheckBoxMenuItem showScrollbarsItem;
    protected JScrollPane scrollPane;
    private final static BufferedImage checkerboardTexture;

    /** Items of the image list. */
    protected static class ImageListItem {

        private Image img;
        private ImageIcon thumbnail;

        public ImageListItem(Image img, ImageIcon thumbnail) {
            this.img = img;
            this.thumbnail = thumbnail;
        }

        public Image getImage() {
            return img;
        }

        public void setThumbnail(ImageIcon thumbnail) {
            this.thumbnail = thumbnail;
        }

        public Icon getThumbnail() {
            return thumbnail;
        }

        public void dispose() {
            if (img != null) {
                img.flush();
                img = null;
            }
            if (thumbnail != null) {
                thumbnail.getImage().flush();
                thumbnail = null;
            }
        }
    }

    /** Image list cell renderer */
    protected static class ImageCellRenderer extends DefaultListCellRenderer {

        @Override
        public Component getListCellRendererComponent(
                JList list,
                Object value,
                int index,
                boolean isSelected,
                boolean cellHasFocus) {
            super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
            if (value instanceof ImageListItem) {
                setText(null);
                setIcon(((ImageListItem) value).getThumbnail());
            }
            return this;
        }
    }

    static {
        checkerboardTexture = new BufferedImage(16, 16, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = checkerboardTexture.createGraphics();
        g.setColor(Color.white);
        g.fillRect(0, 0, 16, 16);
        g.setColor(new Color(223, 223, 223));
        g.fillRect(0, 0, 8, 8);
        g.fillRect(8, 8, 8, 8);
        g.dispose();
    }
    private final static RenderingHints RENDER_NEAREST_NEIGHBOR;

    static {
        RenderingHints rh = new RenderingHints(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
        rh.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
        rh.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
        rh.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
        rh.put(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
        rh.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
        rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        rh.put(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
        rh.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
        RENDER_NEAREST_NEIGHBOR = rh;
    }
    private final static RenderingHints RENDER_BILINEAR;

    static {
        RenderingHints rh = new RenderingHints(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        rh.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
        rh.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
        rh.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
        rh.put(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
        rh.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        rh.put(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
        rh.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
        RENDER_BILINEAR = rh;
    }
    private final static RenderingHints RENDER_BICUBIC;

    static {
        RenderingHints rh = new RenderingHints(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
        rh.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
        rh.put(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_SPEED);
        rh.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
        rh.put(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
        rh.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        rh.put(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
        rh.put(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
        RENDER_BICUBIC = rh;
    }
    /**
     * Resource bundle for internationalisation.
     */
    private ResourceBundleUtil labels;
    public final static int QUARTER_SIZE = 4;
    public final static int HALF_SIZE = 0;
    public final static int NORMAL_SIZE = 1;
    public final static int DOUBLE_SIZE = 2;
    public final static int SCREEN_SIZE = 3;

    public ImageView() {
        initComponents();
        initMoreComponents();
    }

    private void initMoreComponents() {
        labels = ResourceBundleUtil.getLAFBundle("ch.randelshofer.view.ImageView");

        // setLayout(new BorderLayout());
        listScrollPane.setVisible(false);
        listScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        imageList.setCellRenderer(new ImageCellRenderer());
        imageList.setModel(new DefaultListModel());
        imageList.addListSelectionListener(new ListSelectionListener() {

            @Override
            public void valueChanged(ListSelectionEvent e) {
                ImageListItem ili = (ImageListItem) imageList.getSelectedValue();
                if (ili != null) {
                    imagePanel.setImage(ili.getImage());
                }
            }
        });

        imagePanel = new ImagePanel();
        imagePanel.addPropertyChangeListener(this);
        scrollPane = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
        scrollPane.getHorizontalScrollBar().setUnitIncrement(10);
        scrollPane.getVerticalScrollBar().setUnitIncrement(10);
        scrollPane.setBorder(null);
        add(imagePanel);

        // Menu items for pixel aspect
        ButtonGroup group = new ButtonGroup();
        ignorePixelAspectItem = new JRadioButtonMenuItem(labels.getString("ignorePixelAspect"));
        ignorePixelAspectItem.setMnemonic(labels.getMnem("ignorePixelAspect"));
        ignorePixelAspectItem.setAccelerator(labels.getAcc("ignorePixelAspect"));
        group.add(ignorePixelAspectItem);
        ignorePixelAspectItem.addItemListener(this);
        group.add(multipliedPixelAspectItem = new JRadioButtonMenuItem(labels.getString("roundedPixelAspect")));
        multipliedPixelAspectItem.setMnemonic(labels.getMnem("roundedPixelAspect"));
        multipliedPixelAspectItem.setAccelerator(labels.getAcc("roundedPixelAspect"));
        multipliedPixelAspectItem.addItemListener(this);
        group.add(exactPixelAspectItem = new JRadioButtonMenuItem(labels.getString("exactPixelAspect")));
        exactPixelAspectItem.addItemListener(this);
        exactPixelAspectItem.setMnemonic(labels.getMnem("exactPixelAspect"));
        exactPixelAspectItem.setAccelerator(labels.getAcc("exactPixelAspect"));

        // Menu items for image aspect
        group = new ButtonGroup();
        group.add(ignoreImageAspectItem = new JRadioButtonMenuItem(labels.getString("ignoreImageAspect")));
        ignoreImageAspectItem.setMnemonic(labels.getMnem("ignoreImageAspect"));
        ignoreImageAspectItem.setAccelerator(labels.getAcc("ignoreImageAspect"));
        ignoreImageAspectItem.addItemListener(this);
        group.add(exactImageAspectItem = new JRadioButtonMenuItem(labels.getString("exactImageAspect")));
        exactImageAspectItem.addItemListener(this);
        exactImageAspectItem.setMnemonic(labels.getMnem("exactImageAspect"));
        exactImageAspectItem.setAccelerator(labels.getAcc("exactImageAspect"));

        // Menu items for background color
        group = new ButtonGroup();
        group.add(backgroundBlackItem = new JRadioButtonMenuItem(labels.getString("blackBackground")));
        backgroundBlackItem.setMnemonic(labels.getMnem("blackBackground"));
        backgroundBlackItem.setAccelerator(labels.getAcc("blackBackground"));
        group.add(backgroundWhiteItem = new JRadioButtonMenuItem(labels.getString("whiteBackground")));
        backgroundWhiteItem.setMnemonic(labels.getMnem("whiteBackground"));
        backgroundWhiteItem.setAccelerator(labels.getAcc("whiteBackground"));
        group.add(backgroundCheckerboardItem = new JRadioButtonMenuItem(labels.getString("checkerboardBackground")));
        backgroundCheckerboardItem.setMnemonic(labels.getMnem("checkerboardBackground"));
        backgroundCheckerboardItem.setAccelerator(labels.getAcc("checkerboardBackground"));
        backgroundBlackItem.addItemListener(this);
        backgroundWhiteItem.addItemListener(this);
        backgroundCheckerboardItem.addItemListener(this);

        // Menu items for interpolation
        group = new ButtonGroup();
        group.add(nearestNeighborItem = new JRadioButtonMenuItem(labels.getString("nearestNeighbor")));
        nearestNeighborItem.setMnemonic(labels.getMnem("nearestNeighbor"));
        nearestNeighborItem.setAccelerator(labels.getAcc("nearestNeighbor"));
        group.add(bilinearItem = new JRadioButtonMenuItem(labels.getString("bilinear")));
        bilinearItem.setMnemonic(labels.getMnem("bilinear"));
        bilinearItem.setAccelerator(labels.getAcc("bilinear"));
        group.add(bicubicItem = new JRadioButtonMenuItem(labels.getString("bicubic")));
        bicubicItem.setMnemonic(labels.getMnem("bicubic"));
        bicubicItem.setAccelerator(labels.getAcc("bicubic"));
        nearestNeighborItem.addItemListener(this);
        bilinearItem.addItemListener(this);
        bicubicItem.addItemListener(this);

        // Menu items for scrollbars
        showScrollbarsItem = new JCheckBoxMenuItem(labels.getString("showScrollbars"));
        showScrollbarsItem.setMnemonic(labels.getMnem("showScrollbars"));
        showScrollbarsItem.setAccelerator(labels.getAcc("showScrollbars"));
        showScrollbarsItem.setActionCommand("showScrollbars");
        showScrollbarsItem.addActionListener(this);


        setViewProperties(null);
        setApplicationProperties(null);
    }

    @Override
    public void mergeViewMenuItems(JMenu m) {
        JMenu m2;
        JMenuItem mi;
        JRadioButtonMenuItem cmi;

        mi = new JMenuItem(labels.getString("quarterSize"));
        mi.setMnemonic(labels.getMnem("quarterSize"));
        mi.setAccelerator(labels.getAcc("quarterSize"));
        mi.setActionCommand("quarterSize");
        mi.addActionListener(this);
        m.add(mi);
        mi = new JMenuItem(labels.getString("halfSize"));
        mi.setMnemonic(labels.getMnem("halfSize"));
        mi.setAccelerator(labels.getAcc("halfSize"));
        mi.setActionCommand("halfSize");
        mi.addActionListener(this);
        m.add(mi);
        mi = new JMenuItem(labels.getString("normalSize"));
        mi.setMnemonic(labels.getMnem("normalSize"));
        mi.setAccelerator(labels.getAcc("normalSize"));
        mi.setActionCommand("normalSize");
        mi.addActionListener(this);
        m.add(mi);
        mi = new JMenuItem(labels.getString("doubleSize"));
        mi.setMnemonic(labels.getMnem("doubleSize"));
        mi.setAccelerator(labels.getAcc("doubleSize"));
        mi.setActionCommand("doubleSize");
        mi.addActionListener(this);
        m.add(mi);
        mi = new JMenuItem(labels.getString("fillScreen"));
        mi.setMnemonic(labels.getMnem("fillScreen"));
        mi.setAccelerator(labels.getAcc("fillScreen"));
        mi.setActionCommand("fillScreen");
        mi.addActionListener(this);
        m.add(mi);
        m.addSeparator();

        m2 = new JMenu(labels.getString("imageAspect"));
        m2.setMnemonic(labels.getMnem("imageAspect"));
        m2.add(ignoreImageAspectItem);
        m2.add(exactImageAspectItem);
        m.add(m2);

        m2 = new JMenu(labels.getString("pixelAspect"));
        m2.setMnemonic(labels.getMnem("pixelAspect"));
        m2.add(ignorePixelAspectItem);
        m2.add(multipliedPixelAspectItem);
        m2.add(exactPixelAspectItem);
        m.add(m2);

        m2 = new JMenu(labels.getString("interpolation"));
        m2.setMnemonic(labels.getMnem("interpolation"));
        m2.add(nearestNeighborItem);
        m2.add(bilinearItem);
        m2.add(bicubicItem);
        m.add(m2);

        m2 = new JMenu(labels.getString("background"));
        m2.setMnemonic(labels.getMnem("background"));
        m2.add(backgroundBlackItem);
        m2.add(backgroundWhiteItem);
        m2.add(backgroundCheckerboardItem);
        m.add(m2);

        m.addSeparator();
        m.add(showScrollbarsItem);
    }

    @Override
    public void mergeSettingsMenuItems(JMenu m) {
        JMenu m2;
        JMenuItem mi;
        JRadioButtonMenuItem cmi;
    }

    /** Sets the command context for the bean. The receiver loads
     * the image data provided by the data handler and displays it.
     */
    @Override
    public void setCommandContext(String verb, DataHandler dh)
            throws IOException {
        dispose();
        if (dh != null) {
            InputStream in = dh.getInputStream();
            try {
                ByteArrayOutputStream out = new ByteArrayOutputStream(in.available());
                byte[] buf = new byte[512];
                for (int count = in.read(buf); count != -1; count = in.read(buf)) {
                    out.write(buf, 0, count);
                }
                Image image = getToolkit().createImage(out.toByteArray());
                imagePanel.setImage(image);
                try {
                    MediaTracker mt = new MediaTracker(this);
                    mt.addImage(image, 0);
                    mt.waitForID(0);
                } catch (InterruptedException e) {
                }
            } finally {
                in.close();
            }
        }
    }

    /**
     * Frees any resources that have been allocated by this viewer.
     */
    @Override
    public void dispose() {
        Image image = imagePanel.getImage();
        imagePanel.setImage(null);
        if (image != null) {
            image.flush();
        }
        if (imageList != null) {
            DefaultListModel dlm = (DefaultListModel) imageList.getModel();
            if (dlm != null) {
                for (int i = dlm.getSize() - 1; i >= 0; i--) {
                    ImageListItem ili = (ImageListItem) dlm.elementAt(i);
                    ili.dispose();
                }
            }
            dlm.removeAllElements();
        }

        super.dispose();
    }

    @Override
    public void setViewProperties(PropertyModel p) {
        super.setViewProperties(p);
        updateInitialScaleFactor();
    }

    @Override
    public void addNotify() {
        super.addNotify();
        updateInitialScaleFactor();
    }

    @Override
    public void propertyChange(PropertyChangeEvent event) {
        String key = event.getPropertyName();
        Object source = event.getSource();
        if (source == imagePanel) {
            if (key.equals("scaleFactor")) {
                Double sf = ((Double) event.getNewValue());
                getViewProperties().put("scaleFactor", sf.toString());
            }
        } else if (source == getViewProperties()) {
            Object newValue = event.getNewValue();
            if (key.equals("scaleFactor")) {
                updateScaleFactor();
            } else if (key.equals("pixelAspect")) {
                updatePixelAspect();
            } else if (key.equals("imageAspect")) {
                updateImageAspect();
            } else if (key.equals("background")) {
                updateBackground();
            } else if (key.equals("interpolation")) {
                updateInterpolation();
            }
        } else if (source == getApplicationProperties()) {
            if (key.equals("showScrollbars")) {
                updateShowScrollbars();
            }
        }
    }

    private void updateShowScrollbars() {
        if (hasScrollPane()) {
            remove(imagePanel);
            updateImageAspect();
            scrollPane.setViewportView(imagePanel);
            add(scrollPane);
        } else {
            scrollPane.setViewportView(null);
            remove(scrollPane);
            updateImageAspect();
            add(imagePanel);
        }
        showScrollbarsItem.setSelected(hasScrollPane());
        invalidate();
        validate();
        repaint();
    }

    private void updateScaleFactor() {
        String newValue = getViewProperties().getProperty("scaleFactor", "1");
        double sf = 1.0;
        try {
            sf = Double.parseDouble(newValue);
        } catch (NumberFormatException e) {
        }
        getViewProperties().put("title", " as Image @ " + (int) (100 * sf) + "%");
    }

    private void updateInitialScaleFactor() {
        String newValue = getViewProperties().getProperty("initialScaleFactor", "normal");
        if (newValue.equals("quarter")) {
            setViewSize(QUARTER_SIZE);
        } else if (newValue.equals("half")) {
            setViewSize(HALF_SIZE);
        } else if (newValue.equals("normal")) {
            setViewSize(NORMAL_SIZE);
        } else if (newValue.equals("double")) {
            setViewSize(DOUBLE_SIZE);
        } else if (newValue.equals("fillScreen")) {
            setViewSize(SCREEN_SIZE);
        }
    }

    private void updatePixelAspect() {
        String newValue = getViewProperties().getProperty("pixelAspect", "exact");
        int policy;
        if (newValue.equals("ignore")) {
            policy = ImagePanel.IGNORE_PIXEL_ASPECT;
        } else if (newValue.equals("rounded")) {
            policy = ImagePanel.ROUNDED_PIXEL_ASPECT;
        } else {
            policy = ImagePanel.EXACT_PIXEL_ASPECT;
        }

        imagePanel.setPixelAspectPolicy(policy);
        ignorePixelAspectItem.setSelected(policy == ImagePanel.IGNORE_PIXEL_ASPECT);
        multipliedPixelAspectItem.setSelected(policy == ImagePanel.ROUNDED_PIXEL_ASPECT);
        exactPixelAspectItem.setSelected(policy == ImagePanel.EXACT_PIXEL_ASPECT);
    }

    private void updateImageAspect() {
        if (hasScrollPane()) {
            imagePanel.setImageScalePolicy(ImagePanel.SCALE_TO_IMAGE_SIZE);
        } else {
            String newValue = getViewProperties().getProperty("imageAspect", "exact");
            if (newValue.equals("exact")) {
                imagePanel.setImageScalePolicy(ImagePanel.SCALE_TO_IMAGE_ASPECT);
                exactImageAspectItem.setSelected(true);
            } else {
                imagePanel.setImageScalePolicy(ImagePanel.SCALE_TO_VIEW_SIZE);
                ignoreImageAspectItem.setSelected(true);
            }
        }
    }

    private void updateBackground() {
        String newValue = getViewProperties().getProperty("background", "black");
        if (newValue.equals("white")) {
            imagePanel.setBackground(Color.white);
            imagePanel.setForeground(Color.black);
            imagePanel.setTexture(null);
            backgroundWhiteItem.setSelected(true);
        } else if (newValue.equals("checkerboard")) {
            imagePanel.setBackground(new Color(176, 176, 176));
            imagePanel.setForeground(Color.black);
            imagePanel.setTexture(checkerboardTexture);
            backgroundCheckerboardItem.setSelected(true);
        } else {
            imagePanel.setBackground(Color.black);
            imagePanel.setForeground(Color.white);
            imagePanel.setTexture(null);
            backgroundBlackItem.setSelected(true);
        }
        repaint();
    }

    private void updateInterpolation() {
        String newValue = getViewProperties().getProperty("interpolation", "nearestNeighbor");
        if (newValue.equals("nearestNeighbor")) {
            imagePanel.setRenderingHints(RENDER_NEAREST_NEIGHBOR);
            nearestNeighborItem.setSelected(true);
        } else if (newValue.equals("bilinear")) {
            imagePanel.setRenderingHints(RENDER_BILINEAR);
            bilinearItem.setSelected(true);
        } else {
            imagePanel.setRenderingHints(RENDER_BICUBIC);
            bicubicItem.setSelected(true);
        }
        repaint();
    }

    protected void packParent() {
        if (imagePanel.getImage() == null) {
            return;
        }

        if (!SwingUtilities.isEventDispatchThread()) {
            System.out.flush();
            System.err.println("ImageView.packParent called from non-AWT thread");
            new Throwable().printStackTrace();
            System.err.flush();
        }
        if (hasScrollPane() == false) {
            Container container = getParent();
            if (container != null) {
                while (container.getParent() != null) {
                    container = container.getParent();
                }
            }
            if (container != null && container instanceof JFrame) {
                ((JFrame) container).pack();
            }
        }
    }

    @Override
    protected void updateViewProperties() {
        updatePixelAspect();
        updateImageAspect();
        updateBackground();
        updateInitialScaleFactor();
        updateScaleFactor();
        updateInterpolation();
    }

    @Override
    protected void updateApplicationProperties() {
        updateShowScrollbars();
    }

    @Override
    public Dimension getPreferredSize() {
        Dimension id = super.getPreferredSize();

        Dimension sd = getGraphicsConfiguration().getBounds().getSize();
        Insets screenInsets = getToolkit().getScreenInsets(getGraphicsConfiguration());
        Insets insets = getRootPane().getParent().getInsets();
        insets.top += screenInsets.top;
        insets.left += screenInsets.left;
        insets.bottom += screenInsets.bottom;
        insets.right += screenInsets.right;
        Rectangle r = getRootPane().getContentPane().getBounds();
        insets.top += r.y;
        insets.left += r.x;

        id.width = Math.min(sd.width - (insets.left + insets.right), id.width);
        id.height = Math.min(sd.height - (insets.top + insets.bottom), id.height);

        return id;
    }

    @Override
    public void actionPerformed(ActionEvent event) {
        String actionCommand = event.getActionCommand();

        if (actionCommand.equals("quarterSize")) {
            setViewSize(QUARTER_SIZE);
            packParent();
        } else if (actionCommand.equals("halfSize")) {
            setViewSize(HALF_SIZE);
            packParent();
        } else if (actionCommand.equals("normalSize")) {
            setViewSize(NORMAL_SIZE);
            packParent();
        } else if (actionCommand.equals("doubleSize")) {
            setViewSize(DOUBLE_SIZE);
            packParent();
        } else if (actionCommand.equals("fillScreen")) {
            setViewSize(SCREEN_SIZE);
            packParent();
        } else if (actionCommand.equals("showScrollbars")) {
            getApplicationProperties().put("showScrollbars", showScrollbarsItem.isSelected() ? "true" : "false");
        }
    }

    public void setViewSize(int size) {
        PropertyModel p = getViewProperties();

        switch (size) {
            case QUARTER_SIZE:
                imagePanel.setScaleFactor(0.25);
                p.put("initialScaleFactor", "quarter");
                break;
            case HALF_SIZE:
                imagePanel.setScaleFactor(0.5);
                p.put("initialScaleFactor", "half");
                break;

            case NORMAL_SIZE:
                imagePanel.setScaleFactor(1.0);
                p.put("initialScaleFactor", "normal");
                break;

            case DOUBLE_SIZE:
                imagePanel.setScaleFactor(2.0);
                p.put("initialScaleFactor", "double");
                break;

            case SCREEN_SIZE:
                p.put("initialScaleFactor", "fillScreen");
                if (imagePanel.getImage() != null && isDisplayable()) {
                    Dimension id = imagePanel.getPreferredImageSize();
                    Dimension sd = getGraphicsConfiguration().getBounds().getSize();
                    Insets screenInsets = getToolkit().getScreenInsets(getGraphicsConfiguration());
                    Insets insets = getRootPane().getParent().getInsets();
                    insets.top += screenInsets.top;
                    insets.left += screenInsets.left;
                    insets.bottom += screenInsets.bottom;
                    insets.right += screenInsets.right;
                    Rectangle r = getRootPane().getContentPane().getBounds();
                    insets.top += r.y;
                    insets.left += r.x;

                    double factor = Math.min(
                            (sd.width - (insets.left + insets.right)) / (double) id.width,
                            (sd.height - (insets.top + insets.bottom)) / (double) id.height);

                    imagePanel.setScaleFactor(factor);
                    updateScaleFactor();
                }
                break;
        }
    }

    @Override
    public void itemStateChanged(ItemEvent event) {
        Object source = event.getSource();
        PropertyModel p = getViewProperties();

        if (event.getStateChange() == ItemEvent.SELECTED) {
            if (source == ignorePixelAspectItem) {
                p.put("pixelAspect", "ignore");
            } else if (source == multipliedPixelAspectItem) {
                p.put("pixelAspect", "rounded");
            } else if (source == exactPixelAspectItem) {
                p.put("pixelAspect", "exact");

            } else if (source == ignoreImageAspectItem) {
                p.put("imageAspect", "ignore");
            } else if (source == exactImageAspectItem) {
                p.put("imageAspect", "exact");

            } else if (source == backgroundBlackItem) {
                p.put("background", "black");
            } else if (source == backgroundWhiteItem) {
                p.put("background", "white");
            } else if (source == backgroundCheckerboardItem) {
                p.put("background", "checkerboard");


            } else if (source == nearestNeighborItem) {
                p.put("interpolation", "nearestNeighbor");
            } else if (source == bilinearItem) {
                p.put("interpolation", "bilinear");
            } else if (source == bicubicItem) {
                p.put("interpolation", "bicubic");
            }
        }
    }

    @Override
    public boolean hasScrollPane() {
        return getApplicationProperties().getProperty("showScrollbars", "false").equals("true");
    }

    protected String getViewName() {
        return "Java AWT Image View";
    }

    @Override
    public String toString() {
        StringBuffer buf = new StringBuffer();
        buf.append(getViewName());

        Image image = imagePanel.getImage();
        if (image != null) {
            Object comment = image.getProperty("comment", this);

            buf.append("\n\nImage Size: " + image.getWidth(this) + " x " + image.getHeight(this) + " pixels");

            if (image instanceof BufferedImage) {
                BufferedImage bimg = (BufferedImage) image;
                ColorModel cm = bimg.getColorModel();
                if (cm instanceof DirectColorModel) {
                    DirectColorModel dcm = (DirectColorModel) cm;

                    buf.append("\nDirect Color Model: ");
                    buf.append(dcm.getPixelSize());
                    buf.append(" bit");
                } else if (cm instanceof IndexColorModel) {
                    IndexColorModel icm = (IndexColorModel) cm;
                    buf.append("\nIndex Color Model: ");
                    buf.append(icm.getPixelSize());
                    buf.append(" bit, ");
                    buf.append(icm.getMapSize());
                    buf.append(icm.getValidPixels());
                    buf.append(" colors");
                }
                /*buf.append("\nColor Space: ");
                ColorSpace cs = bimg.getColorModel().getColorSpace();
                buf.append(cs.getType());*/
            }

            if (comment != Image.UndefinedProperty && comment != null) {
                buf.append("\n\nComment:");
                buf.append("\n   " + comment);
            }
        }
        return buf.toString();
    }

    /**
     * This method adds the given drop target listener to
     * all components of the viewer.
     */
    @Override
    public void addDropTargetListener(DropTargetListener listener) {
        new DropTarget(this, listener);
        new DropTarget(imagePanel, listener);
        new DropTarget(scrollPane, listener);
        new DropTarget(imageList, listener);
        new DropTarget(listScrollPane, listener);
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        listScrollPane = new javax.swing.JScrollPane();
        imageList = new javax.swing.JList();

        setLayout(new java.awt.BorderLayout());

        listScrollPane.setBorder(javax.swing.BorderFactory.createMatteBorder(0, 0, 0, 1, new java.awt.Color(180, 180, 180)));

        imageList.setBackground(new java.awt.Color(222, 228, 234));
        listScrollPane.setViewportView(imageList);

        add(listScrollPane, java.awt.BorderLayout.LINE_START);
    }// </editor-fold>//GEN-END:initComponents
    // Variables declaration - do not modify//GEN-BEGIN:variables
    protected javax.swing.JList imageList;
    protected javax.swing.JScrollPane listScrollPane;
    // End of variables declaration//GEN-END:variables
}
