/* * This file is part of muCommander, http://www.mucommander.com * Copyright (C) 2002-2016 Maxence Bernard * * muCommander 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 3 of the License, or * (at your option) any later version. * * muCommander 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, see <http://www.gnu.org/licenses/>. */ package com.mucommander.ui.viewer.image; import java.awt.Color; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Image; import java.awt.MediaTracker; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.KeyStroke; import com.mucommander.commons.file.AbstractFile; import com.mucommander.text.Translator; import com.mucommander.ui.helper.MenuToolkit; import com.mucommander.ui.helper.MnemonicHelper; import com.mucommander.ui.theme.ColorChangedEvent; import com.mucommander.ui.theme.FontChangedEvent; import com.mucommander.ui.theme.Theme; import com.mucommander.ui.theme.ThemeListener; import com.mucommander.ui.theme.ThemeManager; import com.mucommander.ui.viewer.FileFrame; import com.mucommander.ui.viewer.FileViewer; /** * A simple image viewer, capable of displaying <code>PNG</code>, <code>GIF</code> and <code>JPEG</code> images. * * @author Maxence Bernard, Arik Hadas */ class ImageViewer extends FileViewer implements ActionListener { private Image image; private Image scaledImage; private double zoomFactor; /** Menu bar */ // Menus // private JMenu controlsMenu; // Items // // private JMenuItem prevImageItem; // private JMenuItem nextImageItem; private JMenuItem zoomInItem; private JMenuItem zoomOutItem; private ImageViewerImpl imageViewerImpl; public ImageViewer() { imageViewerImpl = new ImageViewerImpl(); setComponentToPresent(imageViewerImpl); // Create Go menu MnemonicHelper menuMnemonicHelper = new MnemonicHelper(); controlsMenu = MenuToolkit.addMenu(Translator.get("image_viewer.controls_menu"), menuMnemonicHelper, null); // nextImageItem = MenuToolkit.addMenuItem(controlsMenu, Translator.get("image_viewer.next_image"), menuItemMnemonicHelper, KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), this); // prevImageItem = MenuToolkit.addMenuItem(controlsMenu, Translator.get("image_viewer.previous_image"), menuItemMnemonicHelper, KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0), this); // controlsMenu.add(new JSeparator()); zoomInItem = MenuToolkit.addMenuItem(controlsMenu, Translator.get("image_viewer.zoom_in"), menuMnemonicHelper, KeyStroke.getKeyStroke(KeyEvent.VK_ADD, 0), this); zoomOutItem = MenuToolkit.addMenuItem(controlsMenu, Translator.get("image_viewer.zoom_out"), menuMnemonicHelper, KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, 0), this); } @Override public JMenuBar getMenuBar() { JMenuBar menuBar = super.getMenuBar(); menuBar.add(controlsMenu); return menuBar; } private synchronized void loadImage(AbstractFile file) throws IOException { FileFrame frame = getFrame(); frame.setCursor(new Cursor(Cursor.WAIT_CURSOR)); int read; byte buffer[] = new byte[1024]; ByteArrayOutputStream bout = new ByteArrayOutputStream(); InputStream in = file.getInputStream(); while ((read=in.read(buffer, 0, buffer.length))!=-1) bout.write(buffer, 0, read); byte imageBytes[] = bout.toByteArray(); bout.close(); in.close(); this.scaledImage = null; this.image = imageViewerImpl.getToolkit().createImage(imageBytes); waitForImage(image); int width = image.getWidth(null); int height = image.getHeight(null); this.zoomFactor = 1.0; Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); while(width>d.width || height>d.height) { width = width/2; height = height/2; zoomFactor = zoomFactor/2; } if(zoomFactor==1.0) this.scaledImage = image; else zoom(zoomFactor); checkZoom(); frame.setCursor(Cursor.getDefaultCursor()); } private void waitForImage(Image image) { //AppLogger.finest("Waiting for image to load "+image); MediaTracker tracker = new MediaTracker(imageViewerImpl); tracker.addImage(image, 0); try { tracker.waitForID(0); } catch(InterruptedException e) {} tracker.removeImage(image); //AppLogger.finest("Image loaded "+image); } private synchronized void zoom(double factor) { FileFrame frame = getFrame(); frame.setCursor(new Cursor(Cursor.WAIT_CURSOR)); this.scaledImage = image.getScaledInstance((int)(image.getWidth(null)*factor), (int)(image.getHeight(null)*factor), Image.SCALE_DEFAULT); waitForImage(scaledImage); frame.setCursor(Cursor.getDefaultCursor()); } private void updateFrame() { FileFrame frame = getFrame(); // Revalidate, pack and repaint should be called in this order frame.setTitle(this.getTitle()); imageViewerImpl.revalidate(); frame.pack(); frame.getContentPane().repaint(); } private void checkZoom() { Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); zoomInItem.setEnabled(zoomFactor<1.0 || (2*zoomFactor*image.getWidth(null) < d.width && 2*zoomFactor*image.getHeight(null) < d.height)); zoomOutItem.setEnabled(zoomFactor>1.0 || (zoomFactor/2*image.getWidth(null)>160 && zoomFactor/2*image.getHeight(null)>120)); } /////////////////////////////// // FileViewer implementation // /////////////////////////////// @Override public void show(AbstractFile file) throws IOException { loadImage(file); } /////////////////////////////////// // ActionListener implementation // /////////////////////////////////// @Override public String getTitle() { return super.getTitle()+" - "+image.getWidth(null)+"x"+image.getHeight(null)+" - "+((int)(zoomFactor*100))+"%"; } public void actionPerformed(ActionEvent e) { Object source = e.getSource(); // if(source == prevImageItem) // goToImage(false); // else if(source == nextImageItem) // goToImage(true); // else { if(source==zoomInItem && zoomInItem.isEnabled()) { zoomFactor = zoomFactor*2; zoom(zoomFactor); updateFrame(); } else if(source==zoomOutItem && zoomOutItem.isEnabled()) { zoomFactor = zoomFactor/2; zoom(zoomFactor); updateFrame(); } else { super.actionPerformed(e); return; } checkZoom(); // } } private class ImageViewerImpl extends JPanel implements ThemeListener { private Color backgroundColor; ImageViewerImpl() { backgroundColor = ThemeManager.getCurrentColor(Theme.EDITOR_BACKGROUND_COLOR); ThemeManager.addCurrentThemeListener(this); } //////////////////////// // Overridden methods // //////////////////////// @Override public void paint(Graphics g) { int width = getWidth(); int height = getHeight(); g.setColor(backgroundColor); g.fillRect(0, 0, width, height); if(scaledImage!=null) { int imageWidth = scaledImage.getWidth(null); int imageHeight = scaledImage.getHeight(null); g.drawImage(scaledImage, Math.max(0, (width-imageWidth)/2), Math.max(0, (height-imageHeight)/2), null); } } @Override public synchronized Dimension getPreferredSize() { return new Dimension(scaledImage.getWidth(null), scaledImage.getHeight(null)); } ////////////////////////////////// // ThemeListener implementation // ////////////////////////////////// /** * Receives theme color changes notifications. */ public void colorChanged(ColorChangedEvent event) { if(event.getColorId() == Theme.EDITOR_BACKGROUND_COLOR) { backgroundColor = event.getColor(); repaint(); } } /** * Not used, implemented as a no-op. */ public void fontChanged(FontChangedEvent event) {} } }