/* * PortraitInfoPane.java * Copyright 2011 Connor Petty <cpmeister@users.sourceforge.net> * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Created on Jul 9, 2011, 3:34:09 PM */ package pcgen.gui2.tabs.bio; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import javax.swing.AbstractAction; import javax.swing.DefaultBoundedRangeModel; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSlider; import javax.swing.SwingConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import pcgen.cdom.base.Constants; import pcgen.facade.core.CharacterFacade; import pcgen.facade.util.event.ReferenceEvent; import pcgen.facade.util.event.ReferenceListener; import pcgen.core.utils.MessageType; import pcgen.core.utils.ShowMessageDelegate; import pcgen.gui2.tabs.CharacterInfoTab; import pcgen.gui2.tabs.TabTitle; import pcgen.gui2.tools.Icons; import pcgen.gui2.tools.Utility; import pcgen.system.LanguageBundle; import pcgen.system.PCGenSettings; import pcgen.util.Logging; /** * * @author Connor Petty <cpmeister@users.sourceforge.net> */ @SuppressWarnings("serial") public class PortraitInfoPane extends JScrollPane implements CharacterInfoTab { private static final TabTitle tabTitle = new TabTitle("in_portrait", null); private static final int MAX_PORTRAIT_HEIGHT = 400; private final PortraitPane portraitPane; private final ThumbnailPane tnPane; private final JButton loadButton; private final JButton clearButton; private final JButton purchaseButton; private final JSlider zoomSlider; private JFileChooser chooser = null; public PortraitInfoPane() { super(); this.tnPane = new ThumbnailPane(); this.portraitPane = new PortraitPane(); this.loadButton = new JButton(); this.clearButton = new JButton(); this.purchaseButton = new JButton(); this.zoomSlider = new JSlider(SwingConstants.VERTICAL); initComponents(); } private void initComponents() { JPanel panel = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); Utility.buildConstraints(gbc, 0, 0, 2, 1, 0, 0); panel.add(new JLabel(LanguageBundle.getString("in_largePortrait")), gbc); Utility.buildConstraints(gbc, 0, 1, 2, 1, 0, 0); panel.add(portraitPane, gbc); Utility.buildConstraints(gbc, 0, 2, 1, 1, 0, 0, GridBagConstraints.NONE, GridBagConstraints.EAST); panel.add(loadButton, gbc); Utility.buildConstraints(gbc, 1, 2, 1, 1, 0, 0, GridBagConstraints.NONE, GridBagConstraints.WEST); panel.add(clearButton, gbc); Utility.buildConstraints(gbc, 2, 1, 1, 1, 0, 0, GridBagConstraints.VERTICAL, GridBagConstraints.CENTER); zoomSlider.setInverted(true); zoomSlider.setPreferredSize(new Dimension(20, 10)); panel.add(zoomSlider, gbc); Utility.buildConstraints(gbc, 3, 0, 1, 1, 0, 0, GridBagConstraints.HORIZONTAL, GridBagConstraints.CENTER); panel.add(new JLabel(LanguageBundle.getString("in_thumbnailPortrait")), gbc); Utility.buildConstraints(gbc, 3, 1, 1, 1, 0, 0, GridBagConstraints.NONE, GridBagConstraints.NORTH); panel.add(tnPane, gbc); this.setViewportView(panel); } private BufferedImage createDefaultPortrait() { ImageIcon defaultPortrait = Icons.DefaultPortrait.getImageIcon(); BufferedImage bufImage = new BufferedImage(defaultPortrait.getIconWidth(), defaultPortrait.getIconHeight(), BufferedImage.TYPE_INT_ARGB); defaultPortrait.paintIcon(this, bufImage.createGraphics(), 0, 0); return bufImage; } @Override public ModelMap createModels(CharacterFacade character) { ModelMap models = new ModelMap(); models.put(PortraitHandler.class, new PortraitHandler(character)); models.put(LoadAction.class, new LoadAction(character)); models.put(ClearAction.class, new ClearAction(character)); //models.put(PurchaseAction.class, new PurchaseAction(character)); return models; } @Override public void restoreModels(ModelMap models) { loadButton.setAction(models.get(LoadAction.class)); clearButton.setAction(models.get(ClearAction.class)); //purchaseButton.setAction((Action) models.get(PurchaseAction.class)); models.get(PortraitHandler.class).install(); } @Override public void storeModels(ModelMap models) { models.get(PortraitHandler.class).uninstall(); } @Override public TabTitle getTabTitle() { return tabTitle; } private class LoadAction extends AbstractAction implements PropertyChangeListener { private final CharacterFacade character; private final ImagePreviewer previewer = new ImagePreviewer(); public LoadAction(CharacterFacade character) { super(LanguageBundle.getString("in_loadPortrait")); this.character = character; } @Override public void actionPerformed(ActionEvent e) { if (chooser == null) { chooser = new JFileChooser(PCGenSettings.getPortraitsDir()); } chooser.setAccessory(previewer); chooser.addPropertyChangeListener(this); int ret = chooser.showOpenDialog(PortraitInfoPane.this); chooser.removePropertyChangeListener(this); BufferedImage image = previewer.getImage(); if (ret == JFileChooser.APPROVE_OPTION && image != null) { character.setPortrait(chooser.getSelectedFile()); } } @Override public void propertyChange(PropertyChangeEvent evt) { previewer.setImage(chooser.getSelectedFile()); } } private class ClearAction extends AbstractAction { private final CharacterFacade character; public ClearAction(CharacterFacade character) { super(LanguageBundle.getString("in_clearPortrait")); //$NON-NLS-1$ this.character = character; } @Override public void actionPerformed(ActionEvent e) { character.setPortrait(null); } } private class PurchaseAction extends AbstractAction { private final CharacterFacade character; public PurchaseAction(CharacterFacade character) { super(LanguageBundle.getString("in_buyPortrait")); //$NON-NLS-1$ this.character = character; putValue(SHORT_DESCRIPTION, LanguageBundle.getString("in_buyPortraitTipString")); //$NON-NLS-1$ } @Override public void actionPerformed(ActionEvent e) { try { Utility .viewInBrowser("https://www.e-junkie.com/ecom/gb.php?cl=154598&c=ib&aff=154875"); //$NON-NLS-1$ } catch (IOException ex) { Logging.errorPrint( "Could not open affiliate site in external browser.", ex); ShowMessageDelegate.showMessageDialog( "Could not open affiliate site in external browser.", Constants.APPLICATION_NAME, MessageType.ERROR); } } } private class PortraitHandler extends MouseAdapter implements ReferenceListener<Object>, ChangeListener { private final CharacterFacade character; private Rectangle cropRect; // private final PortraitPane.MouseHandler mouseHandler; private BufferedImage portrait; private float scale; private boolean movingRect = false; private Point cropOffset = null; // private BoundedRangeModel boundedRangeModel; public PortraitHandler(CharacterFacade character) { this.character = character; cropRect = character.getThumbnailCropRef().get(); if (cropRect == null) { cropRect = new Rectangle(0, 0, 100, 100); } // this.mouseHandler = portraitPane.createMouseHandler(character); } public void install() { setPortrait(character.getPortraitRef().get()); portraitPane.setCropRectangle(cropRect); tnPane.setCropRectangle(cropRect); character.getPortraitRef().addReferenceListener(this); character.getThumbnailCropRef().addReferenceListener(this); portraitPane.addMouseListener(this); portraitPane.addMouseMotionListener(this); portraitPane.addMouseWheelListener(this); zoomSlider.addChangeListener(this); } public void uninstall() { character.getPortraitRef().removeReferenceListener(this); character.getThumbnailCropRef().removeReferenceListener(this); portraitPane.removeMouseListener(this); portraitPane.removeMouseMotionListener(this); portraitPane.removeMouseWheelListener(this); zoomSlider.removeChangeListener(this); } private void setPortrait(File file) { portrait = null; try { if (file != null) { if (file.exists() && file.canRead()) { portrait = ImageIO.read(file); } else { Logging.errorPrint("Unable to read portrait file " //$NON-NLS-1$ + file.getAbsolutePath()); } } } catch (IOException ex) { Logging.errorPrint("Could not load image", ex); //$NON-NLS-1$ } if (portrait == null) { portrait = createDefaultPortrait(); } portraitPane.setPortraitImage(portrait); scale = Math.min(MAX_PORTRAIT_HEIGHT / (float) portrait.getHeight(), 1); int min = Constants.THUMBNAIL_SIZE; int max = Math.min(portrait.getHeight(), portrait.getWidth()); int value = Math.min(cropRect.width, max); value = Math.max(value, min); zoomSlider.setModel(new DefaultBoundedRangeModel(value, 0, min, max)); portraitPane.revalidate(); tnPane.setPortraitImage(portrait); tnPane.revalidate(); } @Override public void referenceChanged(ReferenceEvent<Object> e) { Object obj = e.getNewReference(); if (obj == null || obj instanceof File) { setPortrait((File) obj); Rectangle cropRect = new Rectangle(1, 1, 100, 100); Utility.adjustRectToFitImage(portrait, cropRect); character.setThumbnailCrop(cropRect); } else if (obj instanceof Rectangle) { Rectangle rect = (Rectangle) obj; Rectangle cropRect = new Rectangle(rect); Utility.adjustRectToFitImage(portrait, cropRect); if (!rect.equals(cropRect)) { character.setThumbnailCrop(new Rectangle(cropRect)); } portraitPane.setCropRectangle(rect); tnPane.setCropRectangle(rect); zoomSlider.setValue(rect.width); } } @Override public void mouseDragged(MouseEvent e) { if (movingRect) { int x, y; if (scale < 1) { x = (int) (e.getX() / scale - cropOffset.x); y = (int) (e.getY() / scale - cropOffset.y); } else { x = e.getX() - cropOffset.x; y = e.getY() - cropOffset.y; } x = Math.max(x, 0); y = Math.max(y, 0); cropRect.setLocation(x, y); Utility.adjustRectToFitImage(portrait, cropRect); character.setThumbnailCrop(new Rectangle(cropRect)); } } @Override public void mousePressed(MouseEvent e) { if (e.getButton() != MouseEvent.BUTTON1) { return; } Point mousepoint = e.getPoint(); if (scale < 1) { mousepoint.x = (int) (mousepoint.x / scale); mousepoint.y = (int) (mousepoint.y / scale); } movingRect = cropRect.contains(mousepoint); cropOffset = new Point(mousepoint.x - cropRect.x, mousepoint.y - cropRect.y); } @Override public void mouseReleased(MouseEvent e) { movingRect = false; } @Override public void mouseWheelMoved(MouseWheelEvent e) { if (e.getScrollType() != MouseWheelEvent.WHEEL_UNIT_SCROLL) { return; } Point mousepoint = e.getPoint(); if (scale < 1) { mousepoint.x = (int) (mousepoint.x / scale); mousepoint.y = (int) (mousepoint.y / scale); } if (!cropRect.contains(mousepoint)) { return; } int units = e.getUnitsToScroll(); int size = cropRect.width + units; size = Math.max(size, 100); size = Math.min(size, portrait.getWidth()); size = Math.min(size, portrait.getHeight()); cropRect.width = size; cropRect.height = size; int x = cropRect.x; int y = cropRect.y; x = Math.max(x, 0); y = Math.max(y, 0); cropRect.setLocation(x, y); Utility.adjustRectToFitImage(portrait, cropRect); character.setThumbnailCrop(new Rectangle(cropRect)); } @Override public void stateChanged(ChangeEvent e) { int size = zoomSlider.getValue(); cropRect.width = size; cropRect.height = size; int x = cropRect.x; int y = cropRect.y; x = Math.max(x, 0); y = Math.max(y, 0); cropRect.setLocation(x, y); Utility.adjustRectToFitImage(portrait, cropRect); character.setThumbnailCrop(new Rectangle(cropRect)); } } }