net.sf.firemox.tools.Picture.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.firemox.tools.Picture.java

Source

/*
 *   Firemox is a turn based strategy simulator
 *   Copyright (C) 2003-2007 Fabrice Daugan
 *
 *   This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option) any
 * later version.
 *
 *   This program 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 program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
package net.sf.firemox.tools;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

import javax.swing.JComponent;

import net.sf.firemox.clickable.target.card.CardFactory;
import net.sf.firemox.database.DatabaseFactory;
import net.sf.firemox.management.MonitoredCheckContent;
import net.sf.firemox.token.IdConst;
import net.sf.firemox.ui.MagicUIComponents;
import net.sf.firemox.ui.ToolKit;
import net.sf.firemox.ui.component.LoaderConsole;
import net.sf.firemox.ui.i18n.LanguageManager;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;

import sun.awt.image.ToolkitImage;

/**
 * A JComponent displaying an image. The picture is sized to suit to the
 * component size.<br>
 * <ul>
 * TODO Add context menu to this component to:
 * <li>display the picture with right size</li>
 * <li>obtain more information about illustrator</li>
 * </ul>
 * 
 * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan </a>
 * @author brius for http proxy configuration
 * @since 0.83 Empty file are deleted to force file to be downloaded.
 */
public class Picture extends JComponent {

    /**
     * The string delimiting the jar/zip content.
     */
    public static final String STR_ZIP_PATH = "!/";

    /**
     * Creates a new instance of Picture <br>
     * The displayed picture will be the back picture of current TBS game. If no
     * TBS game is loaded, no picture would be drawn. The [preferred]size of this
     * component will be set to the given dimensions.
     * 
     * @param width
     *          the fixed width of this picture
     * @param height
     *          the fixed height of this picture
     */
    public Picture(int width, int height) {
        setPreferredSize(new Dimension(width, height));
        setSize(width, height);
        setImage(DatabaseFactory.backImage, null);
    }

    /**
     * Set the image and card's name to display. Repaint method is called
     * immediately.
     * 
     * @param cardImage
     *          the new card's image to display.
     * @param cardName
     *          the new card's name to use as tooltip.
     */
    public void setImage(Image cardImage, String cardName) {
        if (cardImage != this.cardImage || cardName != this.cardName) {
            this.cardImage = cardImage;
            this.cardName = cardName;
            if (cardName == null) {
                setToolTipText("??");
            } else {
                setToolTipText(cardName);
            }
            repaint();
        }
    }

    /**
     * Download a file from the specified URL to the specified local file.
     * 
     * @param localFile
     *          is the new card's picture to try first
     * @param remoteFile
     *          is the URL where this picture will be downloaded in case of the
     *          specified card name has not been found locally.
     * @since 0.83 Empty file are deleted to force file to be downloaded.
     */
    public static void download(String localFile, URL remoteFile) {
        download(localFile, remoteFile, null);
    }

    /**
     * Download a file from the specified URL to the specified local file.
     * 
     * @param localFile
     *          is the new card's picture to try first
     * @param remoteFile
     *          is the URL where this picture will be downloaded in case of the
     *          specified card name has not been found locally.
     * @param listener
     *          the component waiting for this picture.
     * @since 0.83 Empty file are deleted to force file to be downloaded.
     */
    public static synchronized void download(String localFile, URL remoteFile, MonitoredCheckContent listener) {
        BufferedOutputStream out = null;
        BufferedInputStream in = null;
        File toDownload = new File(localFile);
        if (toDownload.exists() && toDownload.length() == 0 && toDownload.canWrite()) {
            toDownload.delete();
        }
        if (!toDownload.exists() || (toDownload.length() == 0 && toDownload.canWrite())) {
            // the file has to be downloaded
            try {
                if ("file".equals(remoteFile.getProtocol())) {
                    File localRemoteFile = MToolKit
                            .getFile(remoteFile.toString().substring(7).replaceAll("%20", " "), false);
                    int contentLength = (int) localRemoteFile.length();
                    Log.info("Copying from " + localRemoteFile.getAbsolutePath());
                    LoaderConsole.beginTask(
                            LanguageManager.getString("downloading") + " " + localRemoteFile.getAbsolutePath() + "("
                                    + FileUtils.byteCountToDisplaySize(contentLength) + ")");

                    // Copy file
                    in = new BufferedInputStream(new FileInputStream(localRemoteFile));
                    byte[] buf = new byte[2048];
                    int currentLength = 0;
                    boolean succeed = false;
                    for (int bufferLen = in.read(buf); bufferLen >= 0; bufferLen = in.read(buf)) {
                        if (!succeed) {
                            toDownload.getParentFile().mkdirs();
                            out = new BufferedOutputStream(new FileOutputStream(localFile));
                            succeed = true;
                        }
                        currentLength += bufferLen;
                        if (out != null) {
                            out.write(buf, 0, bufferLen);
                        }
                        if (listener != null) {
                            listener.updateProgress(contentLength, currentLength);
                        }
                    }

                    // Step 3: close streams
                    IOUtils.closeQuietly(in);
                    IOUtils.closeQuietly(out);
                    in = null;
                    out = null;
                    return;
                }

                // Testing mode?
                if (!MagicUIComponents.isUILoaded()) {
                    return;
                }

                // Step 1: open streams
                final URLConnection connection = MToolKit.getHttpConnection(remoteFile);
                int contentLength = connection.getContentLength();
                in = new BufferedInputStream(connection.getInputStream());
                Log.info("Download from " + remoteFile + "(" + FileUtils.byteCountToDisplaySize(contentLength)
                        + ")");
                LoaderConsole.beginTask(LanguageManager.getString("downloading") + " " + remoteFile + "("
                        + FileUtils.byteCountToDisplaySize(contentLength) + ")");

                // Step 2: read and write until done
                byte[] buf = new byte[2048];
                int currentLength = 0;
                boolean succeed = false;
                for (int bufferLen = in.read(buf); bufferLen >= 0; bufferLen = in.read(buf)) {
                    if (!succeed) {
                        toDownload.getParentFile().mkdirs();
                        out = new BufferedOutputStream(new FileOutputStream(localFile));
                        succeed = true;
                    }
                    currentLength += bufferLen;
                    if (out != null) {
                        out.write(buf, 0, bufferLen);
                    }
                    if (listener != null) {
                        listener.updateProgress(contentLength, currentLength);
                    }
                }

                // Step 3: close streams
                IOUtils.closeQuietly(in);
                IOUtils.closeQuietly(out);
                in = null;
                out = null;
                return;
            } catch (IOException e1) {
                if (MToolKit.getFile(localFile) != null) {
                    MToolKit.getFile(localFile).delete();
                }
                if (remoteFile.getFile().equals(remoteFile.getFile().toLowerCase())) {
                    Log.fatal("could not load picture " + localFile + " from URL " + remoteFile + ", "
                            + e1.getMessage());
                }
                String tmpRemote = remoteFile.toString().toLowerCase();
                try {
                    download(localFile, new URL(tmpRemote), listener);
                } catch (MalformedURLException e) {
                    Log.fatal("could not load picture " + localFile + " from URL " + tmpRemote + ", "
                            + e.getMessage());
                }
            }
        }
    }

    /**
     * Load the file picture and return the Image object.
     * 
     * @param localFile
     *          is the new card's picture to try first
     * @return the Image object representing this fileName picture
     * @throws MalformedURLException
     */
    public static Image loadImage(String localFile) throws MalformedURLException {
        if (localFile == null) {
            return null;
        }
        MonitoredCheckContent res = loadImage(localFile, null);
        if (res != null) {
            return res.getContent();
        }
        return null;
    }

    /**
     * Load the file picture and return the Image object. If the specified
     * filename did not exist, the specified URL is used to download it.
     * 
     * @param localFile
     *          is the new card's picture to try first
     * @param remoteFile
     *          is the URL where this picture will be downloaded in case of the
     *          specified card name has not been found locally.
     * @return the Image object representing this fileName picture
     * @throws MalformedURLException
     */
    public static MonitoredCheckContent loadImage(String localFile, URL remoteFile) throws MalformedURLException {
        return loadImage(localFile, remoteFile, null);
    }

    /**
     * Load the file picture and return the Image object. If the specified
     * filename did not exist, the specified URL is used to download it.
     * 
     * @param localFile
     *          is the new card's picture to try first
     * @param remoteFile
     *          is the URL where this picture will be downloaded in case of the
     *          specified card name has not been found locally.
     * @param listener
     *          the component waiting for this picture.
     * @return the Image object representing this fileName picture
     * @throws MalformedURLException
     * @since 0.83 Empty file are deleted to force file to be downloaded.
     * @since 0.90 zipped files are managed.
     */
    public static MonitoredCheckContent loadImage(String localFile, URL remoteFile, MonitoredCheckContent listener)
            throws MalformedURLException {
        File toDownload = null;
        try {
            // First determines the file is within a zip file
            if (localFile.contains(STR_ZIP_PATH)) {
                final String zipName = localFile.substring(0, localFile.indexOf(STR_ZIP_PATH));
                toDownload = MToolKit.getFile(zipName);
                if (toDownload == null) {
                    // The zip file does not exist, we get it first
                    toDownload = MToolKit.getFile(zipName, false);
                    if (toDownload.exists() && toDownload.isFile() && toDownload.length() == 0) {
                        // delete previous null sized zip file
                        toDownload.delete();
                    }

                    if (!toDownload.exists()) {
                        // the file does not exist
                        if (remoteFile == null) {
                            return null;
                        }
                        download(zipName, new URL(
                                remoteFile.toString().substring(0, remoteFile.toString().indexOf(STR_ZIP_PATH))),
                                listener);
                    }
                }
                return new MonitoredCheckContent(
                        MToolKit.getLocalPicture("jar:" + MToolKit.getFile(zipName, false).toURI().toURL()
                                + localFile.substring(localFile.indexOf(STR_ZIP_PATH))));
            }

            toDownload = MToolKit.getFile(localFile);
            if (toDownload == null)
                toDownload = MToolKit.getFile(localFile, false);
            if (toDownload.exists() && toDownload.isFile() && toDownload.length() == 0) {
                // delete previous null sized picture file
                toDownload.delete();
            }

            if (!toDownload.exists()) {
                // the file does not exist
                if (remoteFile == null) {
                    return null;
                }
                download(localFile, remoteFile, listener);
            }
            // load picture from it's local location
            return new MonitoredCheckContent(MToolKit.getLocalPicture(localFile));
        } catch (InterruptedException e) {
            // IGNORED
            return null;
        }
    }

    @Override
    public void paint(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
        if (cardImage != null) {
            g.drawImage(cardImage, 0, 0, getWidth(), getHeight(), this);
        }
    }

    /**
     * Return a scaled picture of the given one suiting to card dimension.
     * 
     * @param image
     *          the original card picture.
     * @return a scaled picture of the given one suiting to card dimension.
     */
    public static BufferedImage getScaledImage(Image image) {
        BufferedImage buffImage = ((ToolkitImage) image).getBufferedImage();
        return ToolKit.getScaledInstance(buffImage, CardFactory.cardWidth, CardFactory.cardHeight,
                IdConst.BORDER_WIDTH, getBorderColor(buffImage));
    }

    /**
     * Return the border color of this card. If the picture of this card is not
     * <code>null</code> the returned color corresponds to the pixel placed on
     * the topmost leftmost pixel.
     * 
     * @return the border color of this card.
     */
    private static Color getBorderColor(BufferedImage image) {
        Color borderColor;
        // The border color is not yet cached
        if (CardFactory.borderColor != null) {
            // manual border
            borderColor = CardFactory.borderColor;
        } else {
            // auto border
            if (image != null) {
                borderColor = new Color(image.getRGB(0, 0));
                if (borderColor.getRed() > 175 && borderColor.getGreen() > 175 && borderColor.getBlue() > 175) {
                    borderColor = Color.WHITE.darker();
                } else {
                    borderColor = Color.BLACK.brighter();
                }
            } else {
                borderColor = CardFactory.borderColor;
            }
        }
        return borderColor;
    }

    /**
     * card's image
     */
    private Image cardImage;

    /**
     * card's name
     */
    private String cardName;

}