/*
* Copyright (C) 2008, 2011 IsmAvatar <IsmAvatar@gmail.com>
* 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 java.awt.BorderLayout;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
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.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.DropMode;
import javax.swing.GroupLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.SwingConstants;
import javax.swing.Timer;
import javax.swing.TransferHandler;
import javax.swing.GroupLayout.Alignment;
import org.lateralgm.components.NumberField;
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.LGM;
import org.lateralgm.main.Prefs;
import org.lateralgm.main.Util;
import org.lateralgm.main.UpdateSource.UpdateEvent;
import org.lateralgm.main.UpdateSource.UpdateListener;
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 ResourceFrame<Sprite,PSprite> implements ActionListener,
MouseListener,UpdateListener,ValueChangeListener
{
private static final long serialVersionUID = 1L;
private static final ImageIcon LOAD_ICON = LGM.getIconForKey("SpriteFrame.LOAD"); //$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$
//toolbar
public JButton load, loadStrip;
public JCheckBox showBbox;
//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;
public JCheckBox preciseCC, smooth, preload, transparent;
public JLabel subCount, width, height;
//subimages
public JList subList;
//preview
public SubimagePreview preview;
public NumberField show, speed;
public JButton subLeft, subRight, play;
public JLabel showLab;
public int currSub;
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;
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());
JTabbedPane tabs = new JTabbedPane();
tabs.addTab(Messages.getString("SpriteFrame.PROPERTIES"),makePropertiesPane()); //$NON-NLS-1$
tabs.addTab(Messages.getString("SpriteFrame.MASK"),makeMaskPane()); //$NON-NLS-1$
tabs.addTab(Messages.getString("SpriteFrame.SUBIMAGES"),makeSubimagesPane()); //$NON-NLS-1$
splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,tabs,makePreviewPane());
splitPane.setOneTouchExpandable(true);
add(makeToolBar(),BorderLayout.NORTH);
add(splitPane,BorderLayout.CENTER);
updateImageList();
updateInfo();
pack();
}
private JToolBar makeToolBar()
{
JToolBar tool = new JToolBar();
tool.setFloatable(false);
tool.setAlignmentX(0);
tool.add(save);
load = new JButton(LOAD_ICON);
load.setToolTipText(Messages.getString("SpriteFrame.LOAD")); //$NON-NLS-1$
load.addActionListener(this);
tool.add(load);
loadStrip = new JButton(LOAD_STRIP_ICON);
loadStrip.setToolTipText(Messages.getString("SpriteFrame.LOAD_STRIP")); //$NON-NLS-1$
loadStrip.addActionListener(this);
tool.add(loadStrip);
tool.addSeparator();
name.setColumns(13);
name.setMaximumSize(name.getPreferredSize());
tool.add(new JLabel(Messages.getString("SpriteFrame.NAME"))); //$NON-NLS-1$
tool.add(name);
tool.addSeparator();
showBbox = new JCheckBox(Messages.getString("SpriteFrame.SHOW_BBOX"),true);
showBbox.setOpaque(false); //so it doesn't hide the toolbar gradient...
showBbox.addActionListener(this);
tool.add(showBbox);
return tool;
}
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);
plf.make(g,PSprite.SHAPE,Sprite.MaskShape.class);
bLayout.setHorizontalGroup(bLayout.createParallelGroup()
/**/.addComponent(prec)
/**/.addComponent(rect)
/**/.addComponent(disk)
/**/.addComponent(diam));
bLayout.setVerticalGroup(bLayout.createSequentialGroup()
/**/.addComponent(prec)
/**/.addComponent(rect)
/**/.addComponent(disk)
/**/.addComponent(diam));
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$
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()
/* */.addComponent(lLab)
/* */.addComponent(tLab))
/* */.addGap(2)
/* */.addGroup(bLayout.createParallelGroup()
/* */.addComponent(bboxLeft)
/* */.addComponent(bboxTop))
/* */.addGap(8)
/* */.addGroup(bLayout.createParallelGroup()
/* */.addComponent(rLab)
/* */.addComponent(bLab))
/* */.addGap(2)
/* */.addGroup(bLayout.createParallelGroup()
/* */.addComponent(bboxRight)
/* */.addComponent(bboxBottom))
/* */.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(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);
subCount = new JLabel();
width = new JLabel();
height = new JLabel();
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);
JPanel origin = makeOriginPane();
layout.setHorizontalGroup(layout.createParallelGroup()
/**/.addComponent(subCount,Alignment.CENTER)
/**/.addGroup(Alignment.CENTER,layout.createSequentialGroup()
/* */.addComponent(width)
/* */.addGap(12)
/* */.addComponent(height))
/**/.addComponent(smooth)
/**/.addComponent(preload)
/**/.addComponent(transparent)
/**/.addComponent(origin));
layout.setVerticalGroup(layout.createSequentialGroup()
/**/.addComponent(subCount)
/**/.addGap(4)
/**/.addGroup(layout.createParallelGroup()
/* */.addComponent(width)
/* */.addComponent(height))
/**/.addComponent(smooth)
/**/.addComponent(preload)
/**/.addComponent(transparent)
/**/.addComponent(origin));
return pane;
}
private JPanel makeMaskPane()
{
JPanel pane = new JPanel();
GroupLayout layout = new GroupLayout(pane);
layout.setAutoCreateContainerGaps(true);
pane.setLayout(layout);
JPanel coll = makeCollisionPane();
JPanel bbox = makeBBoxPane();
layout.setHorizontalGroup(layout.createParallelGroup()
/**/.addComponent(coll)
/**/.addComponent(bbox));
layout.setVerticalGroup(layout.createSequentialGroup()
/**/.addComponent(coll)
/**/.addComponent(bbox));
return pane;
}
private JPanel makeSubimagesPane()
{
JPanel pane = new JPanel(new BorderLayout());
//prevents resizing on large subimages with size(1,1)
pane.setPreferredSize(new Dimension(1,1));
JToolBar tool = new JToolBar();
tool.setFloatable(false);
pane.add(tool,BorderLayout.NORTH);
makeToolButton(tool,"SpriteFrame.ADD"); //$NON-NLS-1$
makeToolButton(tool,"SpriteFrame.REMOVE"); //$NON-NLS-1$
tool.addSeparator();
makeToolButton(tool,"SpriteFrame.PREVIOUS"); //$NON-NLS-1$
makeToolButton(tool,"SpriteFrame.NEXT"); //$NON-NLS-1$
subList = new SubImageList();
subList.addMouseListener(this);
subList.setDragEnabled(true);
pane.add(new JScrollPane(subList),BorderLayout.CENTER);
return pane;
}
class SubImageList extends JList
{
private static final long serialVersionUID = 1L;
public SubImageList()
{
setDragEnabled(true);
setDropMode(DropMode.INSERT);
setTransferHandler(new SubImageTransfer());
}
}
class SubImageTransfer extends TransferHandler
{
private static final long serialVersionUID = 1L;
public int getSourceActions(JComponent c)
{
return COPY_OR_MOVE;
}
public Transferable createTransferable(JComponent c)
{
return null;
}
public void exportDone(JComponent c, Transferable t, int action)
{
if (action == MOVE)
{
}
}
public boolean canImport(TransferHandler.TransferSupport s)
{
System.out.println(s.getTransferable());
DataFlavor fl[] = s.getDataFlavors();
// System.out.println(fl);
for (DataFlavor f : fl)
{
if (f.equals(DataFlavor.javaFileListFlavor)) System.out.println("Oh hi");
if (f.equals(DataFlavor.stringFlavor))
{
Transferable t = s.getTransferable();
if (t instanceof StringSelection)
{
}
try
{
System.out.println(s.getTransferable().getTransferData(f));
}
catch (UnsupportedFlavorException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
// System.out.println(f);
}
// System.out.println(" " + f.getRepresentationClass());
}
return false;
}
public boolean importData(TransferHandler.TransferSupport s)
{
return false;
}
}
private void makeToolButton(JToolBar tool, String icon)
{
ImageIcon ii = LGM.getIconForKey(icon);
JButton but = new JButton(ii);
but.setActionCommand(icon);
but.addActionListener(this);
tool.add(but);
}
private JPanel makePreviewPane()
{
JPanel pane = new JPanel(new BorderLayout());
preview = new SubimagePreview(res);
JScrollPane scroll = new JScrollPane(preview);
scroll.setPreferredSize(scroll.getSize());
JPanel controls = new JPanel();
GroupLayout layout = new GroupLayout(controls);
layout.setAutoCreateGaps(false);
layout.setAutoCreateContainerGaps(false);
controls.setLayout(layout);
subLeft = new JButton(LGM.getIconForKey("SpriteFrame.PREVIOUS")); //$NON-NLS-1$
subLeft.addActionListener(this);
show = new NumberField(0,res.subImages.size() - 1);
show.setHorizontalAlignment(SwingConstants.CENTER);
show.addValueChangeListener(this);
// show.setValue(0);
subRight = new JButton(LGM.getIconForKey("SpriteFrame.NEXT")); //$NON-NLS-1$
subRight.addActionListener(this);
JLabel lab = new JLabel(Messages.getString("SpriteFrame.ANIM_SUBIMG")); //$NON-NLS-1$
JLabel lab2 = new JLabel(Messages.getString("SpriteFrame.ANIM_SPEED")); //$NON-NLS-1$
lab2.setHorizontalAlignment(SwingConstants.CENTER);
speed = new NumberField(1,Integer.MAX_VALUE,30);
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;
}
}
});
play = new JButton(PLAY_ICON);
play.addActionListener(this);
layout.setHorizontalGroup(layout.createSequentialGroup()
/**/.addGap(5)
/**/.addGroup(layout.createParallelGroup()
/* */.addComponent(lab,Alignment.CENTER)
/* */.addGroup(layout.createSequentialGroup()
/* */.addComponent(subLeft,20,20,20)
/* */.addComponent(show)
/* */.addComponent(subRight,20,20,20)))
/**/.addGap(10,10,Integer.MAX_VALUE)
/**/.addGroup(layout.createParallelGroup()
/* */.addComponent(lab2,Alignment.CENTER)
/* */.addGroup(layout.createSequentialGroup()
/* */.addComponent(speed)
/* */.addComponent(play,20,20,20)))
/**/.addGap(5));
layout.setVerticalGroup(layout.createSequentialGroup()
/**/.addGroup(layout.createParallelGroup()
/* */.addComponent(lab,Alignment.CENTER)
/* */.addComponent(lab2,Alignment.CENTER))
/**/.addGroup(layout.createParallelGroup()
/* */.addComponent(subLeft,20,20,20)
/* */.addComponent(show,21,21,21)
/* */.addComponent(subRight,20,20,20)
/* */.addComponent(speed,21,21,21)
/* */.addComponent(play,20,20,20))
/**/.addGap(5));
pane.add(scroll,BorderLayout.CENTER);
pane.add(controls,BorderLayout.SOUTH);
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)
{
}
speed.setValue((int) (i * d));
//triggers listener
}
protected boolean areResourceFieldsEqual()
{
return !imageChanged;
}
public void commitChanges()
{
res.setName(name.getText());
}
public void updateResource()
{
super.updateResource();
imageChanged = false;
}
public void valueChange(ValueChangeEvent e)
{
if (e.getSource() == show)
{
updateSub = false;
setSubIndex(show.getIntValue());
updateSub = true;
return;
}
if (e.getSource() == speed)
{
if (timer != null) timer.setDelay(1000 / speed.getIntValue());
return;
}
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == load)
{
BufferedImage[] img = Util.getValidImages();
if (img != null) addSubimages(img,true);
return;
}
if (e.getSource() == loadStrip)
{
addFromStrip(true);
return;
}
if (e.getSource() == showBbox)
{
preview.setShowBbox(showBbox.isSelected());
preview.updateUI();
return;
}
if (e.getSource() == subLeft)
{
if (currSub > 0) setSubIndex(currSub - 1);
return;
}
if (e.getSource() == subRight)
{
if (currSub < res.subImages.size() - 1) setSubIndex(currSub + 1);
return;
}
if (e.getSource() == play)
{
if (timer != null)
{
play.setIcon(PLAY_ICON);
timer.stop();
timer = null; //used to indicate that this is not animating, and frees memory
updateImageControls();
}
else if (res.subImages.size() > 1)
{
play.setIcon(STOP_ICON);
timer = new Timer(1000 / speed.getIntValue(),this);
timer.start();
updateImageControls();
}
return;
}
if (e.getSource() == timer)
{
int s = res.subImages.size();
if (s > 0) setSubIndex((currSub + 1) % s);
return;
}
if (e.getSource() == centre)
{
res.put(PSprite.ORIGIN_X,res.subImages.getWidth() / 2);
res.put(PSprite.ORIGIN_Y,res.subImages.getHeight() / 2);
return;
}
String cmd = e.getActionCommand();
if (cmd != null && cmd.startsWith("SpriteFrame.")) //$NON-NLS-1$
handleToolbarEvent(cmd.substring(12));
super.actionPerformed(e);
}
private void handleToolbarEvent(String cmd)
{
int pos = subList.getSelectedIndex();
if (cmd.equals("ADD")) //$NON-NLS-1$
{
BufferedImage bi = res.addSubImage();
pos = pos >= 0 ? pos + 1 : res.subImages.size();
imageChanged = true;
res.subImages.add(pos,bi);
subList.setSelectedIndex(pos);
editSubimage(bi);
return;
}
if (pos == -1) return;
if (cmd.equals("REMOVE")) //$NON-NLS-1$
{
ImageEditor ie = editors == null ? null : editors.get(res.subImages.get(pos));
imageChanged = true;
res.subImages.remove(pos);
if (ie != null) ie.stop();
subList.setSelectedIndex(Math.min(res.subImages.size() - 1,pos));
return;
}
if (cmd.equals("PREVIOUS")) //$NON-NLS-1$
{
if (pos == 0) return;
imageChanged = true;
BufferedImage bi = res.subImages.remove(pos);
res.subImages.add(pos - 1,bi);
subList.setSelectedIndex(pos - 1);
return;
}
if (cmd.equals("NEXT")) //$NON-NLS-1$
{
if (pos == res.subImages.size() - 1) return;
imageChanged = true;
BufferedImage bi = res.subImages.remove(pos);
res.subImages.add(pos + 1,bi);
subList.setSelectedIndex(pos + 1);
return;
}
}
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);
updateInfo();
}
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);
}
public void updateInfo()
{
width.setText(Messages.getString("SpriteFrame.WIDTH") + res.subImages.getWidth()); //$NON-NLS-1$
height.setText(Messages.getString("SpriteFrame.HEIGHT") + res.subImages.getHeight()); //$NON-NLS-1$
subCount.setText(Messages.getString("SpriteFrame.NO_OF_SUBIMAGES") //$NON-NLS-1$
+ res.subImages.size());
}
private void updateImageControls()
{
int s = res.subImages.size();
if (s > 0)
{
if (currSub > s)
{
setSubIndex(s - 1);
return;
}
subLeft.setEnabled(timer == null && currSub > 0);
subRight.setEnabled(timer == null && currSub < s - 1);
play.setEnabled(s > 1);
if (updateSub)
{
show.setRange(0,s - 1);
show.setEnabled(timer == null);
show.setValue(currSub);
}
}
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()];
for (int i = 0; i < res.subImages.size(); i++)
ii[i] = new ImageIcon(res.subImages.get(i));
subList.setListData(ii);
updateImageControls();
}
private void setSubIndex(int i)
{
currSub = i;
preview.setIndex(i);
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));
}
}
//unused
public void mouseClicked(MouseEvent e)
{
}
public void mouseEntered(MouseEvent e)
{
}
public void mouseExited(MouseEvent e)
{
}
public void mouseReleased(MouseEvent e)
{
}
public void updated(UpdateEvent e)
{
updateInfo();
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;
public ImageEditor(BufferedImage i) throws IOException,UnsupportedOperationException
{
image = i;
File f = File.createTempFile(res.getName(),"." + Prefs.externalSpriteExtension,LGM.tempDir); //$NON-NLS-1$
f.deleteOnExit();
FileOutputStream out = new FileOutputStream(f);
ImageIO.write(i,Prefs.externalSpriteExtension,out); //$NON-NLS-1$
out.close();
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
{
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;
try
{
img = ImageIO.read(new FileInputStream(monitor.file));
}
catch (IOException ioe)
{
ioe.printStackTrace();
return;
}
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();
}
}