/*
* Copyright (C) 2008, 2011 IsmAvatar <IsmAvatar@gmail.com>
* Copyright (C) 2013 Robert B. Colton
* Copyright (C) 2007, 2008 Clam <clamisgood@gmail.com>
* Copyright (C) 2008, 2009 Quadduc <quadduc@gmail.com>
*
* This file is part of LateralGM.
* LateralGM is free software and comes with ABSOLUTELY NO WARRANTY.
* See LICENSE for details.
*/
package org.lateralgm.subframes;
import static javax.swing.GroupLayout.PREFERRED_SIZE;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.instrument.Instrumentation;
import java.net.URI;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.imageio.stream.ImageInputStream;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.DropMode;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButton;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.LayoutStyle;
import javax.swing.ListCellRenderer;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.TransferHandler;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.NumberFormatter;
import org.lateralgm.components.EffectsFrame;
import org.lateralgm.components.NumberField;
import org.lateralgm.components.EffectsFrame.EffectsFrameListener;
import org.lateralgm.components.NumberField.ValueChangeEvent;
import org.lateralgm.components.NumberField.ValueChangeListener;
import org.lateralgm.components.impl.IndexButtonGroup;
import org.lateralgm.components.impl.ResNode;
import org.lateralgm.components.impl.SpriteStripDialog;
import org.lateralgm.components.visual.SubimagePreview;
import org.lateralgm.file.FileChangeMonitor;
import org.lateralgm.file.FileChangeMonitor.FileUpdateEvent;
import org.lateralgm.main.FileChooser.FileDropHandler;
import org.lateralgm.main.LGM;
import org.lateralgm.main.Prefs;
import org.lateralgm.main.UpdateSource.UpdateEvent;
import org.lateralgm.main.UpdateSource.UpdateListener;
import org.lateralgm.main.Util;
import org.lateralgm.messages.Messages;
import org.lateralgm.resources.Sprite;
import org.lateralgm.resources.Sprite.BBMode;
import org.lateralgm.resources.Sprite.PSprite;
import org.lateralgm.ui.swing.util.SwingExecutor;
import org.lateralgm.util.PropertyMap.PropertyUpdateEvent;
import org.lateralgm.util.PropertyMap.PropertyUpdateListener;
public class SpriteFrame extends InstantiableResourceFrame<Sprite,PSprite> implements
MouseListener,UpdateListener,ValueChangeListener,ClipboardOwner, EffectsFrameListener
{
private static final long serialVersionUID = 1L;
private static final ImageIcon LOAD_ICON = LGM.getIconForKey("SpriteFrame.LOAD"); //$NON-NLS-1$
private static final ImageIcon SAVE_ICON = LGM.getIconForKey("SpriteFrame.SAVE"); //$NON-NLS-1$
private static final ImageIcon LOAD_SUBIMAGE_ICON = LGM.getIconForKey("SpriteFrame.LOAD_SUBIMAGE"); //$NON-NLS-1$
private static final ImageIcon LOAD_STRIP_ICON = LGM.getIconForKey("SpriteFrame.LOAD_STRIP"); //$NON-NLS-1$
private static final ImageIcon PLAY_ICON = LGM.getIconForKey("SpriteFrame.PLAY"); //$NON-NLS-1$
private static final ImageIcon STOP_ICON = LGM.getIconForKey("SpriteFrame.STOP"); //$NON-NLS-1$
private static final ImageIcon ZOOM_ICON = LGM.getIconForKey("SpriteFrame.ZOOM"); //$NON-NLS-1$
private static final ImageIcon ZOOM_IN_ICON = LGM.getIconForKey("SpriteFrame.ZOOM_IN"); //$NON-NLS-1$
private static final ImageIcon ZOOM_OUT_ICON = LGM.getIconForKey("SpriteFrame.ZOOM_OUT"); //$NON-NLS-1$
//toolbar
public JButton load, loadSubimages, loadStrip, saveSubimages, zoomIn, zoomOut;
public JToggleButton zoomButton;
//origin
public NumberField originX, originY;
public JButton centre;
//bbox
public IndexButtonGroup bboxGroup;
public NumberField bboxLeft, bboxRight;
public NumberField bboxTop, bboxBottom;
public JRadioButton auto, full, manual;
//properties
public JRadioButton rect, prec, disk, diam, poly;
public JCheckBox smooth, preload, transparent, separateMasks;
public JLabel statusLabel;
//subimages
public JList<ImageIcon> subList;
//preview
public JScrollPane previewScroll, subimagesScroll;
public SubimagePreview preview;
public NumberField show, speed;
public JButton subLeft, subRight, play, cut, copy, paste, undo, redo;
public JLabel showLab;
public int currSub;
public JCheckBox showBbox, showOrigin;
public JCheckBox wrapBox, shiftBox;
public boolean imageChanged = false;
public JSplitPane splitPane;
/** Used for animation, or null when not animating */
public Timer timer;
/** Prevents <code>show</code> from resetting when it changes */
private boolean updateSub = true;
private final SpritePropertyListener spl = new SpritePropertyListener();
private Map<BufferedImage,ImageEditor> editors;
private MouseListener mouseListener;
/** Zoom in, centering around a specific point, usually the mouse. */
public void zoomIn(Point point)
{
if (this.getZoom() >= 32) return;
this.setZoom(this.getZoom() * 2);
Dimension size = previewScroll.getViewport().getSize();
int newX = (int) (point.x * 2) - size.width / 2;
int newY = (int) (point.y * 2) - size.height / 2;
previewScroll.getViewport().setViewPosition(new Point(newX,newY));
previewScroll.revalidate();
previewScroll.repaint();
}
/** Zoom out, centering around a specific point, usually the mouse. */
public void zoomOut(Point point)
{
if (this.getZoom() <= 0.04) return;
this.setZoom(this.getZoom() / 2);
Dimension size = previewScroll.getViewport().getSize();
int newX = (int) (point.x / 2) - size.width / 2;
int newY = (int) (point.y / 2) - size.height / 2;
previewScroll.getViewport().setViewPosition(new Point(newX,newY));
previewScroll.revalidate();
previewScroll.repaint();
}
public void zoomIn()
{
Dimension size = previewScroll.getViewport().getViewSize();
zoomIn(new Point(size.width/2,size.height/2));
}
public void zoomOut()
{
Dimension size = previewScroll.getViewport().getViewSize();
zoomOut(new Point(size.width/2,size.height/2));
}
public SpriteFrame(Sprite res, ResNode node)
{
super(res,node);
res.properties.getUpdateSource(PSprite.BB_MODE).addListener(spl);
res.reference.updateSource.addListener(this);
setLayout(new BorderLayout());
final JSplitPane previewPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,makePreviewPane(),
makeSubimagesPane());
previewPane.setResizeWeight(1);
if (Prefs.rightOrientation) {
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
previewPane,makePropertiesPane());
splitPane.setResizeWeight(1d);
} else {
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
makePropertiesPane(),previewPane);
}
add(makeToolBar(),BorderLayout.NORTH);
add(splitPane,BorderLayout.CENTER);
add(makeStatusBar(),BorderLayout.SOUTH);
mouseListener = new MouseListener()
{
@Override
public void mouseClicked(MouseEvent ev)
{
//preview.setCursor(LGM.zoomCursor);
}
@Override
public void mouseEntered(MouseEvent ev)
{
//preview.setCursor(LGM.zoomCursor);
}
@Override
public void mouseExited(MouseEvent ev)
{
//preview.setCursor(Cursor.getDefaultCursor());
}
@Override
public void mousePressed(MouseEvent ev)
{
if (ev.getButton() == MouseEvent.BUTTON1)
{
preview.setCursor(LGM.zoomInCursor);
}
if (ev.getButton() == MouseEvent.BUTTON3)
{
preview.setCursor(LGM.zoomOutCursor);
}
}
@Override
public void mouseReleased(MouseEvent ev)
{
if (ev.getButton() == MouseEvent.BUTTON1)
{
zoomIn(ev.getPoint());
}
if (ev.getButton() == MouseEvent.BUTTON3)
{
zoomOut(ev.getPoint());
}
preview.setCursor(LGM.zoomCursor);
}
};
updateImageList();
updateStatusLabel();
pack();
// this must be here or for some reason l.setPreferredSize() for the subimage list
// will cause a height of 13,000 with some elongated subimages (8 x 56 as an example)
// setFixedCellWidth/Height is not an alternative because it does not work with subimages
// of varying dimensions
this.setSize(getWidth(),650);
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
previewPane.setDividerLocation(0.6d);
}
});
updateScrollBars();
}
private JToolBar makeToolBar()
{
JToolBar tool = new JToolBar();
tool.setFloatable(false);
tool.setAlignmentX(0);
tool.add(save);
tool.addSeparator();
load = new JButton(LOAD_ICON);
load.setToolTipText(Messages.getString("SpriteFrame.LOAD")); //$NON-NLS-1$
load.addActionListener(this);
tool.add(load);
loadSubimages = new JButton(LOAD_SUBIMAGE_ICON);
loadSubimages.setToolTipText(Messages.getString("SpriteFrame.LOAD_SUBIMAGE")); //$NON-NLS-1$
loadSubimages.addActionListener(this);
tool.add(loadSubimages);
loadStrip = new JButton(LOAD_STRIP_ICON);
loadStrip.setToolTipText(Messages.getString("SpriteFrame.LOAD_STRIP")); //$NON-NLS-1$
loadStrip.addActionListener(this);
tool.add(loadStrip);
saveSubimages = new JButton(SAVE_ICON);
saveSubimages.setToolTipText(Messages.getString("SpriteFrame.SAVE")); //$NON-NLS-1$
saveSubimages.addActionListener(this);
tool.add(saveSubimages);
tool.addSeparator();
zoomButton = new JToggleButton(ZOOM_ICON);
zoomButton.setToolTipText(Messages.getString("SpriteFrame.ZOOM"));
zoomButton.addActionListener(this);
tool.add(zoomButton);
zoomIn = new JButton(ZOOM_IN_ICON);
zoomIn.setToolTipText(Messages.getString("SpriteFrame.ZOOM_IN")); //$NON-NLS-1$
zoomIn.addActionListener(this);
tool.add(zoomIn);
zoomOut = new JButton(ZOOM_OUT_ICON);
zoomOut.setToolTipText(Messages.getString("SpriteFrame.ZOOM_OUT")); //$NON-NLS-1$
zoomOut.addActionListener(this);
tool.add(zoomOut);
tool.addSeparator();
showBbox = new JCheckBox(Messages.getString("SpriteFrame.SHOW_BBOX"),true);
showBbox.addActionListener(this);
showBbox.setOpaque(false);
tool.add(showBbox);
showOrigin = new JCheckBox(Messages.getString("SpriteFrame.SHOW_ORIGIN"),true);
showOrigin.addActionListener(this);
showOrigin.setOpaque(false);
tool.add(showOrigin);
tool.addSeparator();
JLabel lab2 = new JLabel(Messages.getString("SpriteFrame.ANIM_SPEED")); //$NON-NLS-1$
tool.add(lab2);
speed = new NumberField(1,Integer.MAX_VALUE,30);
speed.setColumns(10);
speed.setMaximumSize(speed.getPreferredSize());
speed.setToolTipText(Messages.getString("SpriteFrame.CALC_TIP")); //$NON-NLS-1$
speed.addValueChangeListener(this);
speed.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
//works for all mouse buttons
if ((e.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) != 0)
{
showSpeedDialog();
return;
}
}
});
tool.add(speed);
play = new JButton(PLAY_ICON);
play.addActionListener(this);
tool.add(play);
tool.addSeparator();
name.setColumns(13);
name.setMaximumSize(name.getPreferredSize());
tool.add(new JLabel(Messages.getString("SpriteFrame.NAME"))); //$NON-NLS-1$
tool.add(name);
return tool;
}
public static class ObjectSizeFetcher
{
private static Instrumentation instrumentation;
public static void premain(String args, Instrumentation inst)
{
instrumentation = inst;
}
public static long getObjectSize(Object o)
{
return instrumentation.getObjectSize(o);
}
}
private void updateStatusLabel()
{
String stat = " " + Messages.getString("SpriteFrame.WIDTH") + ": " + res.getWidth() + " | "
+ Messages.getString("SpriteFrame.HEIGHT") + ": " + res.getHeight() + " | "
+ Messages.getString("SpriteFrame.NO_OF_SUBIMAGES") + ": " + res.subImages.size() + " | "
+ Messages.getString("SpriteFrame.MEMORY") + ": ";
if (res.subImages != null)
{
stat += Util.formatDataSize(res.subImages.getSize());
}
else
{
stat += Util.formatDataSize(0);
}
String zoom = new DecimalFormat("#,##0.##").format(getZoom() * 100);
stat += " | " + Messages.getString("SpriteFrame.ZOOM") + ": " + zoom + "%";
statusLabel.setText(stat);
}
private JPanel makeStatusBar()
{
JPanel status = new JPanel(new FlowLayout());
BoxLayout layout = new BoxLayout(status,BoxLayout.X_AXIS);
status.setLayout(layout);
status.setMaximumSize(new Dimension(Integer.MAX_VALUE,11));
statusLabel = new JLabel();
status.add(statusLabel);
return status;
}
private JPanel makeOriginPane()
{
JPanel pane = new JPanel();
GroupLayout oLayout = new GroupLayout(pane);
oLayout.setAutoCreateGaps(true);
// oLayout.setAutoCreateContainerGaps(true);
pane.setLayout(oLayout);
pane.setBorder(BorderFactory.createTitledBorder(Messages.getString("SpriteFrame.ORIGIN"))); //$NON-NLS-1$
JLabel oxLab = new JLabel(Messages.getString("SpriteFrame.X")); //$NON-NLS-1$
oxLab.setHorizontalAlignment(SwingConstants.RIGHT);
originX = new NumberField(0);
originX.setColumns(4);
plf.make(originX,PSprite.ORIGIN_X);
JLabel oyLab = new JLabel(Messages.getString("SpriteFrame.Y")); //$NON-NLS-1$;
oyLab.setHorizontalAlignment(SwingConstants.RIGHT);
originY = new NumberField(0);
originY.setColumns(4);
plf.make(originY,PSprite.ORIGIN_Y);
centre = new JButton(Messages.getString("SpriteFrame.CENTER")); //$NON-NLS-1$
centre.addActionListener(this);
// The empty comments here prevent the formatter from messing up line wrapping and indentation.
oLayout.setHorizontalGroup(oLayout.createParallelGroup(Alignment.CENTER)
/**/.addGroup(oLayout.createSequentialGroup()
/* */.addGap(12)
/* */.addComponent(oxLab)
/* */.addGap(4)
/* */.addComponent(originX)
/* */.addGap(12)
/* */.addComponent(oyLab)
/* */.addGap(4)
/* */.addComponent(originY)
/* */.addGap(12))
/**/.addComponent(centre));
oLayout.setVerticalGroup(oLayout.createSequentialGroup()
/**/.addGroup(oLayout.createParallelGroup(Alignment.BASELINE)
/* */.addComponent(oxLab)
/* */.addComponent(originX)
/* */.addComponent(oyLab)
/* */.addComponent(originY))
/**/.addGap(8)
/**/.addComponent(centre)
/**/.addGap(8));
return pane;
}
private JPanel makeCollisionPane()
{
JPanel pane = new JPanel();
GroupLayout bLayout = new GroupLayout(pane);
pane.setLayout(bLayout);
pane.setBorder(BorderFactory.createTitledBorder(Messages.getString("SpriteFrame.COLLISION"))); //$NON-NLS-1$
ButtonGroup g = new ButtonGroup();
prec = new JRadioButton(Messages.getString("SpriteFrame.PRECISE")); //$NON-NLS-1$
g.add(prec);
rect = new JRadioButton(Messages.getString("SpriteFrame.RECTANGLE")); //$NON-NLS-1$
g.add(rect);
disk = new JRadioButton(Messages.getString("SpriteFrame.DISK")); //$NON-NLS-1$
g.add(disk);
diam = new JRadioButton(Messages.getString("SpriteFrame.DIAMOND")); //$NON-NLS-1$
g.add(diam);
poly = new JRadioButton(Messages.getString("SpriteFrame.POLYGON")); //$NON-NLS-1$
g.add(poly);
plf.make(g,PSprite.SHAPE,Sprite.MaskShape.class);
bLayout.setHorizontalGroup(bLayout.createSequentialGroup()
.addGroup(bLayout.createParallelGroup()
/**/.addComponent(prec)
/**/.addComponent(rect)
/**/.addComponent(disk)
/**/.addComponent(diam)
/**/.addComponent(poly))
/**/.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED, PREFERRED_SIZE, Short.MAX_VALUE));
bLayout.setVerticalGroup(bLayout.createSequentialGroup()
/**/.addComponent(prec)
/**/.addComponent(rect)
/**/.addComponent(disk)
/**/.addComponent(diam)
/**/.addComponent(poly));
return pane;
}
private JPanel makeBBoxPane()
{
JPanel pane = new JPanel();
GroupLayout bLayout = new GroupLayout(pane);
pane.setLayout(bLayout);
pane.setBorder(BorderFactory.createTitledBorder(
Messages.getString("SpriteFrame.BBOX"))); //$NON-NLS-1$
JLabel toleranceLabel = new JLabel(
Messages.getString("SpriteFrame.ALPHA_TOLERANCE")); //$NON-NLS-1$
NumberField tolerance = new NumberField(0, 255);
plf.make(tolerance, PSprite.ALPHA_TOLERANCE);
JSlider toleranceSlider = new JSlider(0, 255);
plf.make(toleranceSlider.getModel(),PSprite.ALPHA_TOLERANCE);
ButtonGroup g = new ButtonGroup();
auto = new JRadioButton(Messages.getString("SpriteFrame.AUTO")); //$NON-NLS-1$
g.add(auto);
full = new JRadioButton(Messages.getString("SpriteFrame.FULL")); //$NON-NLS-1$
g.add(full);
manual = new JRadioButton(Messages.getString("SpriteFrame.MANUAL")); //$NON-NLS-1$
g.add(manual);
plf.make(g,PSprite.BB_MODE,BBMode.class);
JLabel lLab = new JLabel(Messages.getString("SpriteFrame.LEFT")); //$NON-NLS-1$
lLab.setHorizontalAlignment(SwingConstants.RIGHT);
bboxLeft = new NumberField(0);
bboxLeft.setColumns(3);
plf.make(bboxLeft,PSprite.BB_LEFT);
JLabel rLab = new JLabel(Messages.getString("SpriteFrame.RIGHT")); //$NON-NLS-1$
rLab.setHorizontalAlignment(SwingConstants.RIGHT);
bboxRight = new NumberField(0);
bboxRight.setColumns(3);
plf.make(bboxRight,PSprite.BB_RIGHT);
JLabel tLab = new JLabel(Messages.getString("SpriteFrame.TOP")); //$NON-NLS-1$
tLab.setHorizontalAlignment(SwingConstants.RIGHT);
bboxTop = new NumberField(0);
bboxTop.setColumns(3);
plf.make(bboxTop,PSprite.BB_TOP);
JLabel bLab = new JLabel(Messages.getString("SpriteFrame.BOTTOM")); //$NON-NLS-1$
bLab.setHorizontalAlignment(SwingConstants.RIGHT);
bboxBottom = new NumberField(0);
bboxBottom.setColumns(3);
plf.make(bboxBottom,PSprite.BB_BOTTOM);
updateBoundingBoxEditors();
bLayout.setHorizontalGroup(bLayout.createParallelGroup()
/**/.addGroup(bLayout.createSequentialGroup()
/* */.addComponent(auto)
/* */.addComponent(full))
/**/.addComponent(manual)
/**/.addGroup(bLayout.createSequentialGroup()
/* */.addContainerGap(4,4)
/* */.addGroup(bLayout.createParallelGroup()
/* */.addGroup(bLayout.createSequentialGroup()
/* */.addGroup(bLayout.createParallelGroup(Alignment.TRAILING)
/* */.addComponent(lLab)
/* */.addComponent(tLab))
/* */.addGap(2)
/* */.addGroup(bLayout.createParallelGroup()
/* */.addComponent(bboxLeft)
/* */.addComponent(bboxTop))
/* */.addGap(8)
/* */.addGroup(bLayout.createParallelGroup(Alignment.TRAILING)
/* */.addComponent(rLab)
/* */.addComponent(bLab))
/* */.addGap(2)
/* */.addGroup(bLayout.createParallelGroup()
/* */.addComponent(bboxRight)
/* */.addComponent(bboxBottom)))
/* */.addGroup(bLayout.createSequentialGroup()
/* */.addComponent(toleranceLabel))
/* */.addGroup(bLayout.createSequentialGroup()
/* */.addComponent(toleranceSlider, 0, 0, Short.MAX_VALUE)
/* */.addGap(2)
/* */.addComponent(tolerance, PREFERRED_SIZE, PREFERRED_SIZE, PREFERRED_SIZE)))
/* */.addContainerGap(4,4)));
bLayout.setVerticalGroup(bLayout.createSequentialGroup()
/**/.addGroup(bLayout.createParallelGroup(Alignment.BASELINE)
/* */.addComponent(auto)
/* */.addComponent(full))
/**/.addComponent(manual)
/**/.addGap(4)
/**/.addGroup(bLayout.createParallelGroup(Alignment.BASELINE)
/* */.addComponent(toleranceLabel))
/**/.addGap(2)
/**/.addGroup(bLayout.createParallelGroup(Alignment.CENTER)
/* */.addComponent(toleranceSlider)
/* */.addComponent(tolerance, PREFERRED_SIZE, PREFERRED_SIZE, PREFERRED_SIZE))
/**/.addGap(4)
/**/.addGroup(bLayout.createParallelGroup(Alignment.BASELINE)
/* */.addComponent(lLab)
/* */.addComponent(bboxLeft)
/* */.addComponent(rLab)
/* */.addComponent(bboxRight))
/**/.addGap(4)
/**/.addGroup(bLayout.createParallelGroup(Alignment.BASELINE)
/* */.addComponent(tLab)
/* */.addComponent(bboxTop)
/* */.addComponent(bLab)
/* */.addComponent(bboxBottom))
/**/.addContainerGap(2,2));
return pane;
}
private JPanel makePropertiesPane()
{
JPanel pane = new JPanel();
GroupLayout layout = new GroupLayout(pane);
layout.setAutoCreateContainerGaps(true);
pane.setLayout(layout);
smooth = new JCheckBox(Messages.getString("SpriteFrame.SMOOTH")); //$NON-NLS-1$
plf.make(smooth,PSprite.SMOOTH_EDGES);
preload = new JCheckBox(Messages.getString("SpriteFrame.PRELOAD")); //$NON-NLS-1$
plf.make(preload,PSprite.PRELOAD);
transparent = new JCheckBox(Messages.getString("SpriteFrame.TRANSPARENT")); //$NON-NLS-1$
transparent.setToolTipText(Messages.getString("SpriteFrame.TRANSP_TIP")); //$NON-NLS-1$
plf.make(transparent,PSprite.TRANSPARENT);
separateMasks = new JCheckBox(Messages.getString("SpriteFrame.SEPARATE")); //$NON-NLS-1$
separateMasks.setToolTipText(Messages.getString("SpriteFrame.SEPARATE_TIP")); //$NON-NLS-1$
plf.make(separateMasks,PSprite.SEPARATE_MASK);
JPanel origin = makeOriginPane();
JPanel coll = makeCollisionPane();
JPanel bbox = makeBBoxPane();
layout.setHorizontalGroup(layout.createParallelGroup()
/**/.addComponent(smooth)
/**/.addComponent(preload)
/**/.addComponent(transparent)
/**/.addComponent(separateMasks)
/**/.addComponent(origin)
/**/.addComponent(coll)
/**/.addComponent(bbox));
layout.setVerticalGroup(layout.createSequentialGroup()
/**/.addComponent(smooth)
/**/.addComponent(preload)
/**/.addComponent(transparent)
/**/.addComponent(separateMasks)
/**/.addComponent(origin)
/**/.addComponent(coll)
/**/.addComponent(bbox));
return pane;
}
public class ImageLabel extends JLabel
{
/**
* NOTE: Default UID generated, change if necessary.
*/
private static final long serialVersionUID = 749151178684203437L;
BufferedImage img;
int index = -1;
JList<ImageIcon> list;
public void paintComponent(Graphics g)
{
g.drawImage(img,0,0,this.getWidth() - 1,this.getHeight() - 1,null);
if (list.isSelectedIndex(index))
{
g.setColor(list.getSelectionBackground());
g.drawRect(0,0,this.getWidth() - 1,this.getHeight() - 1);
}
g.dispose();
}
}
BufferedImage transparencyBackground = null;
public class ImageCellRenderer implements ListCellRenderer<ImageIcon>
{
private final JList<ImageIcon> list;
public ImageCellRenderer(JList<ImageIcon> l)
{
super();
this.list = l;
}
public Component getListCellRendererComponent(final JList<? extends ImageIcon> genericlist,
final ImageIcon value, final int index, final boolean isSelected, final boolean hasFocus)
{
//create panel
final JPanel p = new JPanel(new BorderLayout(0,0));
//p.setBorder(BorderFactory.createEmptyBorder(2,2,2,2));
final ImageLabel l = new ImageLabel(); //<-- this will be an icon instead of a text
BufferedImage img = res.subImages.get(index);
if (img == null)
{
return null;
}
int imgwidth = img.getWidth();
int imgheight = img.getHeight();
int width = 64, height = 64;
if (imgheight < imgwidth)
{
width = (int)(height / (float)imgheight * imgwidth);
}
else if (imgwidth < imgheight)
{
height = (int)(width / (float)imgwidth * imgheight);
}
//subList.setFixedCellWidth(width+1);
//subList.setFixedCellHeight(height+1);
l.setPreferredSize(new Dimension(width+1,height+1));
if ((Boolean) res.get(PSprite.TRANSPARENT))
{
img = Util.getTransparentImage(img);
}
int bwidth = (int)Math.ceil(width/10f);
int bheight = (int)Math.ceil(height/10f);
bwidth = bwidth < 1 ? 1 : bwidth;
bheight = bheight < 1 ? 1 : bheight;
if (transparencyBackground == null ||
transparencyBackground.getWidth() != bwidth ||
transparencyBackground.getHeight() != bheight)
{
transparencyBackground = Util.paintBackground(bwidth, bheight);
}
BufferedImage cimg = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
Graphics2D g = cimg.createGraphics();
g.drawImage(transparencyBackground,0,0,bwidth*10,bheight*10,null);
g.drawImage(img,0,0,width,height,null);
l.img = cimg;
l.index = index;
l.list = list;
p.add(l);
return p;
}
}
private JButton makeJButton(String key)
{
JButton but = new JButton(LGM.getIconForKey(key));
but.setToolTipText(Messages.getString(key));
but.addActionListener(this);
but.setActionCommand(key);
return but;
}
private JMenuItem makeJMenuItem(String key)
{
JMenuItem but = new JMenuItem(LGM.getIconForKey(key));
but.setText(Messages.getString(key));
but.addActionListener(this);
but.setActionCommand(key);
return but;
}
private JPanel makeSubimagesPane()
{
JPanel pane = new JPanel(new BorderLayout());
//prevents resizing on large subimages with size(1,1)
//pane.setPreferredSize(pane.getMinimumSize());
final JPopupMenu popup = new JPopupMenu();
popup.add(makeJMenuItem("SpriteFrame.EDIT"));
popup.addSeparator();
popup.add(makeJMenuItem("SpriteFrame.SELECT_ALL"));
JToolBar tool = new JToolBar();
tool.setFloatable(false);
pane.add(tool,BorderLayout.NORTH);
tool.add(makeJButton("SpriteFrame.ADD"));
tool.add(makeJButton("SpriteFrame.REMOVE"));
tool.addSeparator();
tool.add(makeJButton("SpriteFrame.EDIT"));
tool.add(makeJButton("SpriteFrame.EFFECT"));
tool.addSeparator();
tool.add(makeJButton("SpriteFrame.CUT"));
tool.add(makeJButton("SpriteFrame.COPY"));
tool.add(makeJButton("SpriteFrame.PASTE"));
tool.addSeparator();
// TODO: Implement undo/redo
//tool.add(makeJButton("SpriteFrame.UNDO"));
//tool.add(makeJButton("SpriteFrame.REDO"));
//tool.addSeparator();
subLeft = new JButton(LGM.getIconForKey("SpriteFrame.PREVIOUS")); //$NON-NLS-1$
subLeft.addActionListener(this);
tool.add(subLeft);
show = new NumberField(0,res.subImages.size() - 1);
show.setHorizontalAlignment(SwingConstants.CENTER);
show.addValueChangeListener(this);
show.setColumns(10);
show.setMaximumSize(show.getPreferredSize());
// show.setValue(0);
tool.add(show);
subRight = new JButton(LGM.getIconForKey("SpriteFrame.NEXT")); //$NON-NLS-1$
subRight.addActionListener(this);
tool.add(subRight);
//JLabel lab = new JLabel(Messages.getString("SpriteFrame.ANIM_SUBIMG")); //$NON-NLS-1$
shiftBox = new JCheckBox(Messages.getString("SpriteFrame.SHIFT"),true);
shiftBox.setSelected(false);
shiftBox.setOpaque(false);
tool.add(shiftBox);
wrapBox = new JCheckBox(Messages.getString("SpriteFrame.WRAP"),true);
wrapBox.setOpaque(false);
wrapBox.addItemListener(new ItemListener()
{
public void itemStateChanged(ItemEvent arg0)
{
if (!wrapBox.isSelected())
{
subLeft.setEnabled(timer == null && currSub > 0);
subRight.setEnabled(timer == null && currSub < res.subImages.size() - 1);
}
else
{
subLeft.setEnabled(true);
subRight.setEnabled(true);
}
}
});
tool.add(wrapBox);
subList = new JList<ImageIcon>();
subList.setLayoutOrientation(JList.HORIZONTAL_WRAP);
subList.setVisibleRowCount(-1);
subList.setBackground(Color.LIGHT_GRAY);
subList.setDragEnabled(true);
subList.setDropMode(DropMode.INSERT);
subList.setTransferHandler(new SubImageTransfer());
subList.addMouseListener(this);
subList.setDragEnabled(true);
subList.addListSelectionListener(new ListSelectionListener()
{
public void valueChanged(ListSelectionEvent ev)
{
EffectsFrame.getInstance(getSelectedImages()).setEffectsListener(SpriteFrame.this);
int ind = subList.getSelectedIndex();
if (ind < 0) return;
if (timer == null)
{
setSubIndex(ind);
}
}
});
subList.setCellRenderer(new ImageCellRenderer(subList));
subList.setComponentPopupMenu(popup);
subimagesScroll = new JScrollPane(subList);
subimagesScroll.getVerticalScrollBar().setUnitIncrement(0);
subimagesScroll.getHorizontalScrollBar().setUnitIncrement(0);
subimagesScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
pane.add(subimagesScroll,BorderLayout.CENTER);
return pane;
}
class SubImageTransfer extends FileDropHandler implements Transferable
{
private static final long serialVersionUID = 1L;
private final DataFlavor flavors[] = { DataFlavor.imageFlavor };
BufferedImage data;
public int getSourceActions(JComponent c)
{
return COPY_OR_MOVE;
}
public DataFlavor[] getTransferDataFlavors()
{
return flavors;
}
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException,IOException
{
if (flavor != DataFlavor.imageFlavor) throw new UnsupportedFlavorException(flavor);
return Util.cloneImage(data);
}
public Transferable createTransferable(JList<ImageIcon> c)
{
JList<ImageIcon> l = ((JList<ImageIcon>) c);
int index = l.getSelectedIndex();
if (index == -1) return null;
data = res.subImages.get(index);
return this;
}
public void exportDone(JComponent c, Transferable t, int action)
{
if (action == MOVE) res.subImages.remove(data);
}
public boolean isDataFlavorSupported(DataFlavor df)
{
if (super.isDataFlavorSupported(df)) return true;
return df == DataFlavor.imageFlavor;
}
public boolean importData(TransferHandler.TransferSupport evt)
{
List<BufferedImage> bi = new LinkedList<BufferedImage>();
try
{
if (evt.isDataFlavorSupported(DataFlavor.imageFlavor))
{
BufferedImage b = (BufferedImage) evt.getTransferable().getTransferData(
DataFlavor.imageFlavor);
if (b != null) bi.add(b);
//otherwise, we'll see if there's a list flavor
//(Yeah right, as if anybody else uses imageFlavor)
}
if (bi.isEmpty())
{
List<?> files = getDropList(evt);
if (files == null || files.isEmpty()) return false;
for (Object o : files)
{
ImageInputStream iis = null;
if (o instanceof File) iis = ImageIO.createImageInputStream(o);
if (o instanceof URI)
iis = ImageIO.createImageInputStream(((URI) o).toURL().openStream());
BufferedImage bia[] = Util.getValidImages(iis);
if (files.size() != 1 && bia.length > 1) return false;
Collections.addAll(bi,bia);
}
}
}
catch (Exception e)
{
//Bastard lied to us
e.printStackTrace();
}
if (bi.isEmpty()) return false;
int index = -1;
if (evt.isDrop())
{
JList.DropLocation loc = (JList.DropLocation) evt.getDropLocation();
index = loc.getIndex();
if (!loc.isInsert()) res.subImages.remove(index);
System.out.println(loc.isInsert());
}
if (index < 0) index = res.subImages.size();
for (BufferedImage b : bi)
res.subImages.add(index++,b);
return true;
}
}
private JPanel makePreviewPane()
{
JPanel pane = new JPanel(new BorderLayout());
preview = new SubimagePreview(res);
previewScroll = new JScrollPane(preview);
previewScroll.setPreferredSize(previewScroll.getSize());
pane.add(previewScroll,BorderLayout.CENTER);
return pane;
}
private void showSpeedDialog()
{
JPanel p = new JPanel();
GroupLayout layout = new GroupLayout(p);
layout.setAutoCreateGaps(false);
layout.setAutoCreateContainerGaps(false);
p.setLayout(layout);
JLabel caption = new JLabel(Messages.getString("SpriteFrame.CALC_CAPTION")); //$NON-NLS-1$
JLabel lrs = new JLabel(Messages.getString("SpriteFrame.CALC_ROOM_SPEED")); //$NON-NLS-1$
JLabel lis = new JLabel(Messages.getString("SpriteFrame.CALC_IMAGE_SPEED")); //$NON-NLS-1$
NumberField rs = new NumberField(1,Integer.MAX_VALUE,speed.getIntValue());
JTextField is = new JTextField("1.0"); //$NON-NLS-1$
layout.setHorizontalGroup(layout.createParallelGroup()
/**/.addComponent(caption,Alignment.CENTER)
/**/.addGroup(layout.createSequentialGroup()
/* */.addGroup(layout.createParallelGroup()
/* */.addComponent(lrs)
/* */.addComponent(lis))
/* */.addGap(5)
/* */.addGroup(layout.createParallelGroup()
/* */.addComponent(rs)
/* */.addComponent(is))));
layout.setVerticalGroup(layout.createSequentialGroup()
/* */.addComponent(caption)
/* */.addGap(5)
/* */.addGroup(layout.createParallelGroup()
/* */.addComponent(lrs)
/* */.addComponent(rs))
/* */.addGroup(layout.createParallelGroup()
/* */.addComponent(lis)
/* */.addComponent(is)));
JOptionPane.showMessageDialog(this,p);
int i = rs.getIntValue();
double d = 1.0;
try
{
d = Double.parseDouble(is.getText());
}
catch (NumberFormatException nfe)
{
//d = 1.0
}
speed.setValue((int) (i * d));
//triggers listener
}
protected boolean areResourceFieldsEqual()
{
return !imageChanged;
}
public void commitChanges()
{
res.setName(name.getText());
}
public void updateResource(boolean commit)
{
super.updateResource(commit);
imageChanged = false;
}
public void valueChange(ValueChangeEvent e)
{
if (e.getSource() == show)
{
subList.setSelectedIndex(show.getIntValue());
return;
}
if (e.getSource() == speed)
{
if (timer != null) timer.setDelay(1000 / speed.getIntValue());
return;
}
}
private static class ClipboardImages
{
List<BufferedImage> bi;
public ClipboardImages(List<BufferedImage> images)
{
bi = images;
}
}
private static DataFlavor imgClipFlavor = new DataFlavor(ClipboardImages.class,
"Buffered Images Clipboard");
private static class TransferableImages implements Transferable
{
ClipboardImages ci;
public TransferableImages(ClipboardImages images)
{
this.ci = images;
}
public DataFlavor[] getTransferDataFlavors()
{
DataFlavor[] ret = { imgClipFlavor };
return ret;
}
public boolean isDataFlavorSupported(DataFlavor flavor)
{
return imgClipFlavor.equals(flavor);
}
public synchronized Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException
{
if (isDataFlavorSupported(flavor))
{
return this.ci;
}
else
{
throw new UnsupportedFlavorException(imgClipFlavor);
}
}
}
private BufferedImage createNewImage(boolean askforsize)
{
int width = res.getWidth();
int height = res.getHeight();
if (width == 0 || height == 0)
{
width = 32;
height = 32;
}
if (askforsize)
{
NumberFormatter nf = new NumberFormatter();
nf.setMinimum(new Integer(1));
JFormattedTextField wField = new JFormattedTextField(nf);
wField.setValue(new Integer(width));
JFormattedTextField hField = new JFormattedTextField(nf);
hField.setValue(new Integer(height));
JPanel myPanel = new JPanel();
GridLayout layout = new GridLayout(0,2,0,3);
myPanel.setLayout(layout);
myPanel.add(new JLabel(Messages.getString("SpriteFrame.NEW_WIDTH")));
myPanel.add(wField);
myPanel.add(new JLabel(Messages.getString("SpriteFrame.NEW_HEIGHT")));
myPanel.add(hField);
int result = JOptionPane.showConfirmDialog(this,
myPanel,Messages.getString("SpriteFrame.NEW_TITLE"),JOptionPane.OK_CANCEL_OPTION,
JOptionPane.PLAIN_MESSAGE);
if (result == JOptionPane.CANCEL_OPTION)
{
return null;
}
width = (Integer) wField.getValue();
height = (Integer) hField.getValue();
}
BufferedImage bi = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
imageChanged = true;
return bi;
}
public void editActionsPerformed(String cmd)
{
int pos = subList.getSelectedIndex();
if (cmd.endsWith(".UNDO"))
{
return;
}
else if (cmd.endsWith(".REDO"))
{
return;
}
else if (cmd.endsWith(".SELECT_ALL"))
{
subList.setSelectionInterval(0,res.subImages.size() - 1);
return;
}
else if (cmd.endsWith(".CUT"))
{
int[] selections = subList.getSelectedIndices();
if (selections.length == 0)
{
return;
}
List<BufferedImage> images = new ArrayList<BufferedImage>(selections.length);
for (int i = 0; i < selections.length; i++)
{
images.add(res.subImages.get(selections[i] - i));
res.subImages.remove(selections[i] - i);
}
Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard();
clip.setContents(new TransferableImages(new ClipboardImages(images)),this);
imageChanged = true;
subList.setSelectedIndex(pos - 1);
return;
}
else if (cmd.endsWith(".COPY"))
{
int[] selections = subList.getSelectedIndices();
List<BufferedImage> images = new ArrayList<BufferedImage>(selections.length);
for (int i = 0; i < selections.length; i++)
{
images.add(res.subImages.get(selections[i]));
}
Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard();
clip.setContents(new TransferableImages(new ClipboardImages(images)),this);
return;
}
else if (cmd.endsWith(".PASTE"))
{
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
Transferable content = clipboard.getContents(this);
if (content.isDataFlavorSupported(imgClipFlavor))
{
ClipboardImages images = null;
try
{
images = (ClipboardImages) content.getTransferData(imgClipFlavor);
}
catch (UnsupportedFlavorException e)
{
LGM.showDefaultExceptionHandler(e);
}
catch (IOException e)
{
LGM.showDefaultExceptionHandler(e);
}
imageChanged = true;
res.subImages.addAll(pos + 1,images.bi);
subList.setSelectionInterval(pos + 1,pos + images.bi.size());
}
return;
}
else if (cmd.endsWith(".ADD")) //$NON-NLS-1$
{
BufferedImage bi = createNewImage(res.subImages.size() == 0);
if (bi != null)
{
pos = pos >= 0 ? pos + 1 : res.subImages.size();
imageChanged = true;
res.subImages.add(pos,bi);
subList.setSelectedIndex(pos);
setSubIndex(pos);
}
return;
}
else if (cmd.endsWith(".EDIT")) //$NON-NLS-1$
{
int[] selections = subList.getSelectedIndices();
for (int i = 0; i < selections.length; i++)
{
editSubimage(res.subImages.get(selections[i]));
}
return;
}
else if (cmd.endsWith(".EFFECT")) {
EffectsFrame ef = EffectsFrame.getInstance(getSelectedImages());
ef.setEffectsListener(this);
ef.setVisible(true);
}
else if (cmd.endsWith(".REMOVE")) //$NON-NLS-1$
{
int[] selections = subList.getSelectedIndices();
for (int i = 0; i < selections.length; i++)
{
ImageEditor ie = editors == null ? null : editors.get(res.subImages.get(selections[i] - i));
imageChanged = true;
res.subImages.remove(selections[i] - i);
if (ie != null) ie.stop();
}
subList.setSelectedIndex(Math.min(res.subImages.size() - 1,pos));
return;
}
}
public double getZoom()
{
return preview.getZoom();
}
public void setZoom(double nzoom)
{
preview.setZoom(nzoom);
updateStatusLabel();
updateScrollBars();
}
private class AnimThread extends Thread
{
public boolean freeze = false;
public void run()
{
while (!freeze && subList != null && preview != null)
{
// TODO: Shit throws all kinds of NPE's
// These two are threaded because updating the Swing controls
// Slows down the animation so its best to thread them to within a
// 60 frame per second quality playback.
//subList.setSelectedIndex(preview.getIndex());
//updateImageControls();
try
{
Thread.sleep(25);
}
catch (InterruptedException e)
{
LGM.showDefaultExceptionHandler(e);
}
}
}
}
public ArrayList<BufferedImage> getSelectedImages() {
int[] selected = subList.getSelectedIndices();
if (selected.length <= 0) {
return res.subImages;
} else {
if (res.subImages.getSize() <= 0) return null;
ArrayList<BufferedImage> subimages = new ArrayList<BufferedImage>(selected.length);
for (int id : selected) {
subimages.add(res.subImages.get(id));
}
return subimages;
}
}
private AnimThread animThread = null;
public void actionPerformed(ActionEvent e)
{
String cmd = e.getActionCommand();
if (cmd != null)
{
editActionsPerformed(cmd);
}
int pos = subList.getSelectedIndex();
if (e.getSource() == load)
{
BufferedImage[] img = Util.getValidImages();
if (img != null) addSubimages(img,true);
return;
}
else if (e.getSource() == loadStrip)
{
addFromStrip(true);
return;
}
else if (e.getSource() == loadSubimages)
{
BufferedImage[] img = Util.getValidImages();
if (img != null) addSubimages(img,false);
return;
}
else if (e.getSource() == saveSubimages)
{
ArrayList<BufferedImage> imgs = getSelectedImages();
if (imgs != null) {
Util.saveImages(imgs);
}
return;
}
else if (e.getSource() == showBbox)
{
preview.setShowBbox(showBbox.isSelected());
preview.updateUI();
return;
}
else if (e.getSource() == showOrigin)
{
preview.setShowOrigin(showOrigin.isSelected());
preview.updateUI();
return;
}
else if (e.getSource() == subLeft)
{
if (pos <= 0 && !wrapBox.isSelected())
{
subList.setSelectedIndex(currSub - 1);
return;
}
if (shiftBox.isSelected())
{
//TODO: This may be firing an event causing you not to be able
//to shift multiple images at a time.
int[] selections = subList.getSelectedIndices();
for (int i = 0; i < selections.length; i++)
{
pos = selections[i];
BufferedImage bi = res.subImages.remove(pos);
if (pos <= 0 && wrapBox.isSelected())
{
pos = res.subImages.size() + 1;
}
res.subImages.add(pos - 1,bi);
}
imageChanged = true;
subList.setSelectedIndex(pos - 1);
}
else
{
if (currSub > 0)
subList.setSelectedIndex(currSub - 1);
else if (wrapBox.isSelected()) subList.setSelectedIndex(res.subImages.size() - 1);
}
return;
}
else if (e.getSource() == subRight)
{
if (pos >= res.subImages.size() - 1 && !wrapBox.isSelected())
{
subList.setSelectedIndex(res.subImages.size());
return;
}
if (shiftBox.isSelected())
{
//TODO: This may be firing an event causing you not to be able
//to shift multiple images at a time.
int[] selections = subList.getSelectedIndices();
for (int i = 0; i < selections.length; i++)
{
pos = selections[i];
preview.setIndex(pos);
BufferedImage bi = res.subImages.remove(pos);
if (pos > res.subImages.size() - 1 && wrapBox.isSelected())
{
pos = -1;
}
res.subImages.add(pos + 1,bi);
}
imageChanged = true;
subList.setSelectedIndex(pos + 1);
}
else
{
if (currSub < res.subImages.size() - 1)
subList.setSelectedIndex(currSub + 1);
else if (wrapBox.isSelected()) subList.setSelectedIndex(0);
}
return;
}
else if (e.getSource() == zoomButton)
{
if (zoomButton.isSelected())
{
preview.enablemouse = false;
preview.setCursor(LGM.zoomCursor);
preview.addMouseListener(mouseListener);
}
else
{
preview.enablemouse = true;
preview.removeMouseListener(mouseListener);
preview.setCursor(Cursor.getDefaultCursor());
}
}
else if (e.getSource() == zoomIn)
{
zoomIn();
return;
}
else if (e.getSource() == zoomOut)
{
zoomOut();
return;
}
else if (e.getSource() == play)
{
if (timer != null)
{
play.setIcon(PLAY_ICON);
animThread = null;
timer.stop();
timer = null; //used to indicate that this is not animating, and frees memory
updateImageControls();
}
else if (res.subImages.size() > 1)
{
if (animThread == null)
{
animThread = new AnimThread();
animThread.start();
}
animThread.freeze = false;
play.setIcon(STOP_ICON);
timer = new Timer(1000 / speed.getIntValue(),this);
timer.start();
updateImageControls();
}
return;
}
else if (e.getSource() == timer)
{
int s = res.subImages.size();
if (s > 0) setSubIndex((currSub + 1) % s);
return;
}
else if (e.getSource() == centre)
{
res.put(PSprite.ORIGIN_X,res.getWidth() / 2);
res.put(PSprite.ORIGIN_Y,res.getHeight() / 2);
return;
}
super.actionPerformed(e);
}
private void realizeScrollBarIncrement(JScrollPane scroll)
{
JScrollBar vertical = scroll.getVerticalScrollBar();
JScrollBar horizontal = scroll.getHorizontalScrollBar();
if (vertical != null)
{
vertical.setUnitIncrement((int) getZoom());
}
if (horizontal != null)
{
horizontal.setUnitIncrement((int) getZoom());
}
}
private void updateScrollBars()
{
realizeScrollBarIncrement(previewScroll);
realizeScrollBarIncrement(subimagesScroll);
}
public void addSubimages(BufferedImage img[], boolean clear)
{
if (img.length == 0) return;
if (clear)
{
cleanup();
res.subImages.clear();
}
clear = res.subImages.isEmpty();
imageChanged = true;
for (BufferedImage i : img)
res.subImages.add(i);
show.setRange(0,res.subImages.size());
if (clear) setSubIndex(0);
updateStatusLabel();
updateScrollBars();
}
public void addFromStrip(boolean clear)
{
//ask for an image first
BufferedImage bi = Util.getValidImage();
if (bi == null) return;
//create the strip dialog
SpriteStripDialog d = new SpriteStripDialog(LGM.frame,bi);
d.setLocationRelativeTo(LGM.frame);
d.setVisible(true); //modal at this point
//add images
BufferedImage[] img = d.getStrip();
if (img == null) return; //cancelled/closed
addSubimages(img,clear);
}
private void updateImageControls()
{
int s = res.subImages.size();
if (s > 0)
{
if (subList.getSelectedIndex() > s)
{
setSubIndex(s - 1);
return;
}
if (!wrapBox.isSelected())
{
subLeft.setEnabled(timer == null && subList.getSelectedIndex() > 0);
subRight.setEnabled(timer == null && subList.getSelectedIndex() < s - 1);
}
else
{
subLeft.setEnabled(timer == null);
subRight.setEnabled(timer == null);
}
play.setEnabled(s > 1);
if (updateSub)
{
try
{
show.setRange(0,s - 1);
}
catch (Exception e)
{
e.printStackTrace();
}
show.setEnabled(timer == null);
//show.setValue(subList.getSelectedIndex());
}
}
else
{
subLeft.setEnabled(false);
subRight.setEnabled(false);
play.setEnabled(false);
if (updateSub)
{
show.setValue(0);
show.setEnabled(false);
}
}
}
private void updateImageList()
{
ImageIcon ii[] = new ImageIcon[res.subImages.size()];
int maxWidth = -1;
for (int i = 0; i < res.subImages.size(); i++)
{
ii[i] = new ImageIcon(res.subImages.get(i));
maxWidth = Math.max(maxWidth,ii[i].getIconWidth());
}
subList.setListData(ii);
updateImageControls();
}
private void setSubIndex(int i)
{
if (currSub == i)
{
return;
}
currSub = i;
preview.setIndex(i);
if (timer == null)
{
updateImageControls();
}
}
private void updateBoundingBoxEditors()
{
boolean m = res.get(PSprite.BB_MODE) == BBMode.MANUAL;
bboxLeft.setEnabled(m);
bboxRight.setEnabled(m);
bboxTop.setEnabled(m);
bboxBottom.setEnabled(m);
}
@Override
public Dimension getMinimumSize()
{
Dimension p = getContentPane().getSize();
Dimension l = getContentPane().getMinimumSize();
Dimension s = getSize();
l.width += s.width - p.width;
l.height += s.height - p.height;
return l;
}
public void editSubimage(BufferedImage img)
{
if (img == null) return;
try
{
ImageEditor ie = editors == null ? null : editors.get(img);
if (ie == null)
new ImageEditor(img);
else
ie.start();
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
public void mousePressed(MouseEvent e)
{
Object s = e.getSource();
if (e.getClickCount() == 2 && s == subList)
{
int i = subList.getSelectedIndex();
if (i == -1 || i >= res.subImages.size()) return;
editSubimage(res.subImages.get(i));
}
}
public void updated(UpdateEvent e)
{
updateStatusLabel();
updateImageList();
}
private class SpritePropertyListener extends PropertyUpdateListener<PSprite>
{
@Override
public void updated(PropertyUpdateEvent<PSprite> e)
{
// BB_MODE
updateBoundingBoxEditors();
}
}
private class ImageEditor implements UpdateListener
{
private BufferedImage image;
public final FileChangeMonitor monitor;
private final File f;
public ImageEditor(BufferedImage i) throws IOException,UnsupportedOperationException
{
image = i;
f = File.createTempFile(res.getName(),"." + Prefs.externalSpriteExtension,LGM.tempDir); //$NON-NLS-1$
f.deleteOnExit();
monitor = new FileChangeMonitor(f,SwingExecutor.INSTANCE);
monitor.updateSource.addListener(this,true);
if (editors == null) editors = new HashMap<BufferedImage,ImageEditor>();
editors.put(i,this);
start();
}
public void start() throws IOException,UnsupportedOperationException
{
FileOutputStream out = null;
try
{
out = new FileOutputStream(f);
ImageIO.write(image,Prefs.externalSpriteExtension,out); //$NON-NLS-1$
}
finally
{
if (out != null) {
out.close();
}
}
if (!Prefs.useExternalSpriteEditor || Prefs.externalSpriteEditorCommand == null)
try
{
Desktop.getDesktop().edit(monitor.file);
}
catch (UnsupportedOperationException e)
{
throw new UnsupportedOperationException("no internal or system sprite editor",e);
}
else
Runtime.getRuntime().exec(
String.format(Prefs.externalSpriteEditorCommand,monitor.file.getAbsolutePath()));
}
public void stop()
{
monitor.stop();
monitor.file.delete();
if (editors != null) editors.remove(image);
}
public void updated(UpdateEvent e)
{
if (!(e instanceof FileUpdateEvent)) return;
switch (((FileUpdateEvent) e).flag)
{
case CHANGED:
BufferedImage img;
FileInputStream stream = null;
try
{
stream = new FileInputStream(monitor.file);
img = ImageIO.read(stream);
}
catch (IOException ioe)
{
LGM.showDefaultExceptionHandler(ioe);
return;
}
finally
{
if (stream != null)
{
try
{
stream.close();
}
catch (IOException ex)
{
LGM.showDefaultExceptionHandler(ex);
}
}
}
res.subImages.replace(image,img);
editors.remove(image);
editors.put(img,this);
image = img;
imageChanged = true;
break;
case DELETED:
editors.remove(image);
}
}
}
@Override
public void dispose()
{
super.dispose();
cleanup();
}
/** Stops file monitors, detaching any open editors. */
protected void cleanup()
{
if (editors != null)
for (ImageEditor ie : editors.values().toArray(new ImageEditor[editors.size()]))
ie.stop();
}
//unused
public void mouseClicked(MouseEvent e)
{ //unused
}
public void mouseEntered(MouseEvent e)
{ //unused
}
public void mouseExited(MouseEvent e)
{ //unused
}
public void mouseReleased(MouseEvent e)
{ //unused
}
public void lostOwnership(Clipboard arg0, Transferable arg1)
{
// TODO Auto-generated method stub
System.out.println("Sprite editor has lost clipboard ownership.");
}
@Override
public void applyEffects(List<BufferedImage> imgs)
{
int[] selection = subList.getSelectedIndices();
for (int i = 0; i < selection.length; i++) {
res.subImages.set(selection[i],imgs.get(i));
}
imageChanged = true;
subList.setSelectedIndices(selection);
preview.repaint();
}
}