/*
* 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 java.lang.Integer.MAX_VALUE;
import static javax.swing.GroupLayout.DEFAULT_SIZE;
import static javax.swing.GroupLayout.PREFERRED_SIZE;
import java.awt.Container;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingConstants;
import javax.swing.GroupLayout.Alignment;
import org.lateralgm.components.NumberField;
import org.lateralgm.components.impl.ResNode;
import org.lateralgm.components.visual.BackgroundPreview;
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.Background;
import org.lateralgm.resources.Background.PBackground;
import org.lateralgm.ui.swing.util.SwingExecutor;
import org.lateralgm.util.PropertyMap.PropertyUpdateEvent;
import org.lateralgm.util.PropertyMap.PropertyUpdateListener;
public class BackgroundFrame extends ResourceFrame<Background,PBackground> implements
UpdateListener
{
private static final long serialVersionUID = 1L;
private static final ImageIcon LOAD_ICON = LGM.getIconForKey("BackgroundFrame.LOAD"); //$NON-NLS-1$
public JButton load;
public JLabel width;
public JLabel height;
public JCheckBox transparent;
public JButton edit;
public JCheckBox smooth;
public JCheckBox preload;
public JCheckBox tileset;
public JPanel side2;
public NumberField tWidth;
public NumberField tHeight;
public NumberField hOffset;
public NumberField vOffset;
public NumberField hSep;
public NumberField vSep;
public BackgroundPreview preview;
public boolean imageChanged = false;
private BackgroundEditor editor;
private final BackgroundPropertyListener bpl = new BackgroundPropertyListener();
public BackgroundFrame(Background res, ResNode node)
{
super(res,node);
res.properties.getUpdateSource(PBackground.USE_AS_TILESET).addListener(bpl);
res.reference.updateSource.addListener(this);
GroupLayout layout = new GroupLayout(getContentPane())
{
@Override
public void layoutContainer(Container parent)
{
Dimension m = BackgroundFrame.this.getMinimumSize();
Dimension s = BackgroundFrame.this.getSize();
Dimension r = new Dimension(Math.max(m.width,s.width),Math.max(m.height,s.height));
if (!r.equals(s))
BackgroundFrame.this.setSize(r);
else
super.layoutContainer(parent);
}
};
setLayout(layout);
JPanel side1 = new JPanel();
makeSide1(side1);
side2 = new JPanel();
makeSide2(side2);
preview = new BackgroundPreview(res);
preview.setVerticalAlignment(SwingConstants.TOP);
JScrollPane scroll = new JScrollPane(preview);
layout.setHorizontalGroup(layout.createSequentialGroup()
/**/.addComponent(side1,DEFAULT_SIZE,PREFERRED_SIZE,PREFERRED_SIZE)
/**/.addComponent(side2,DEFAULT_SIZE,PREFERRED_SIZE,PREFERRED_SIZE)
/**/.addComponent(scroll,120,320,DEFAULT_SIZE));
layout.setVerticalGroup(layout.createParallelGroup()
/**/.addComponent(side1)
/**/.addComponent(side2)
/**/.addComponent(scroll,120,240,DEFAULT_SIZE));
pack();
}
private void makeSide1(JPanel side1)
{
GroupLayout s1Layout = new GroupLayout(side1);
s1Layout.setAutoCreateContainerGaps(true);
s1Layout.setAutoCreateGaps(true);
side1.setLayout(s1Layout);
JLabel nLabel = new JLabel(Messages.getString("BackgroundFrame.NAME"));
load = new JButton(Messages.getString("SpriteFrame.LOAD")); //$NON-NLS-1$
load.setIcon(LOAD_ICON);
load.addActionListener(this);
width = new JLabel(Messages.getString("BackgroundFrame.WIDTH") + res.getWidth()); //$NON-NLS-1$
height = new JLabel(Messages.getString("BackgroundFrame.HEIGHT") + res.getHeight()); //$NON-NLS-1$
edit = new JButton(Messages.getString("BackgroundFrame.EDIT")); //$NON-NLS-1$
edit.addActionListener(this);
transparent = new JCheckBox(Messages.getString("BackgroundFrame.TRANSPARENT")); //$NON-NLS-1$
plf.make(transparent,PBackground.TRANSPARENT);
smooth = new JCheckBox(Messages.getString("BackgroundFrame.SMOOTH")); //$NON-NLS-1$
plf.make(smooth,PBackground.SMOOTH_EDGES);
preload = new JCheckBox(Messages.getString("BackgroundFrame.PRELOAD")); //$NON-NLS-1$
plf.make(preload,PBackground.PRELOAD);
tileset = new JCheckBox(Messages.getString("BackgroundFrame.USE_AS_TILESET")); //$NON-NLS-1$
plf.make(tileset,PBackground.USE_AS_TILESET);
save.setText(Messages.getString("BackgroundFrame.SAVE")); //$NON-NLS-1$
s1Layout.setHorizontalGroup(s1Layout.createParallelGroup()
/**/.addGroup(s1Layout.createSequentialGroup()
/* */.addComponent(nLabel)
/* */.addComponent(name,DEFAULT_SIZE,120,MAX_VALUE))
/**/.addComponent(load,DEFAULT_SIZE,DEFAULT_SIZE,MAX_VALUE)
/**/.addGroup(s1Layout.createSequentialGroup()
/* */.addComponent(width)
/* */.addComponent(height))
/**/.addComponent(edit,DEFAULT_SIZE,DEFAULT_SIZE,MAX_VALUE)
/**/.addComponent(transparent)
/**/.addComponent(smooth)
/**/.addComponent(preload)
/**/.addComponent(tileset)
/**/.addComponent(save,DEFAULT_SIZE,DEFAULT_SIZE,MAX_VALUE));
s1Layout.setVerticalGroup(s1Layout.createSequentialGroup()
/**/.addGroup(s1Layout.createParallelGroup(Alignment.BASELINE)
/* */.addComponent(nLabel)
/* */.addComponent(name))
/**/.addComponent(load)
/**/.addGroup(s1Layout.createParallelGroup(Alignment.BASELINE)
/* */.addComponent(width)
/* */.addComponent(height))
/**/.addComponent(edit)
/**/.addComponent(transparent)
/**/.addComponent(smooth)
/**/.addComponent(preload)
/**/.addComponent(tileset)
/**/.addGap(8,8,MAX_VALUE)
/**/.addComponent(save));
}
private void makeSide2(JPanel side2)
{
GroupLayout s2Layout = new GroupLayout(side2);
side2.setLayout(s2Layout);
String tileProps = Messages.getString("BackgroundFrame.TILE_PROPERTIES"); //$NON-NLS-1$
side2.setBorder(BorderFactory.createTitledBorder(tileProps));
JLabel twLabel = new JLabel(Messages.getString("BackgroundFrame.TILE_WIDTH")); //$NON-NLS-1$
twLabel.setHorizontalAlignment(SwingConstants.RIGHT);
tWidth = new NumberField(0,Integer.MAX_VALUE);
plf.make(tWidth,PBackground.TILE_WIDTH);
tWidth.setColumns(3);
JLabel thLabel = new JLabel(Messages.getString("BackgroundFrame.TILE_HEIGHT")); //$NON-NLS-1$
thLabel.setHorizontalAlignment(SwingConstants.RIGHT);
tHeight = new NumberField(0,Integer.MAX_VALUE);
plf.make(tHeight,PBackground.TILE_HEIGHT);
tHeight.setColumns(3);
JLabel hoLabel = new JLabel(Messages.getString("BackgroundFrame.H_OFFSET")); //$NON-NLS-1$
hoLabel.setHorizontalAlignment(SwingConstants.RIGHT);
hOffset = new NumberField(0,Integer.MAX_VALUE);
plf.make(hOffset,PBackground.H_OFFSET);
hOffset.setColumns(3);
JLabel voLabel = new JLabel(Messages.getString("BackgroundFrame.V_OFFSET")); //$NON-NLS-1$
voLabel.setHorizontalAlignment(SwingConstants.RIGHT);
vOffset = new NumberField(0,Integer.MAX_VALUE);
plf.make(vOffset,PBackground.V_OFFSET);
vOffset.setColumns(3);
JLabel hsLabel = new JLabel(Messages.getString("BackgroundFrame.H_SEP")); //$NON-NLS-1$
hsLabel.setHorizontalAlignment(SwingConstants.RIGHT);
hSep = new NumberField(0,Integer.MAX_VALUE);
plf.make(hSep,PBackground.H_SEP);
hSep.setColumns(3);
JLabel vsLabel = new JLabel(Messages.getString("BackgroundFrame.V_SEP")); //$NON-NLS-1$
vsLabel.setHorizontalAlignment(SwingConstants.RIGHT);
vSep = new NumberField(0,Integer.MAX_VALUE);
plf.make(vSep,PBackground.V_SEP);
vSep.setColumns(3);
s2Layout.setHorizontalGroup(s2Layout.createSequentialGroup()
/**/.addContainerGap(4,4)
/**/.addGroup(s2Layout.createParallelGroup(Alignment.TRAILING)
/* */.addComponent(twLabel)
/* */.addComponent(thLabel)
/* */.addComponent(hoLabel)
/* */.addComponent(voLabel)
/* */.addComponent(hsLabel)
/* */.addComponent(vsLabel))
/**/.addGap(4)
/**/.addGroup(s2Layout.createParallelGroup()
/* */.addComponent(tWidth,PREFERRED_SIZE,DEFAULT_SIZE,DEFAULT_SIZE)
/* */.addComponent(tHeight,PREFERRED_SIZE,DEFAULT_SIZE,DEFAULT_SIZE)
/* */.addComponent(hOffset,PREFERRED_SIZE,DEFAULT_SIZE,DEFAULT_SIZE)
/* */.addComponent(vOffset,PREFERRED_SIZE,DEFAULT_SIZE,DEFAULT_SIZE)
/* */.addComponent(hSep,PREFERRED_SIZE,DEFAULT_SIZE,DEFAULT_SIZE)
/* */.addComponent(vSep,PREFERRED_SIZE,DEFAULT_SIZE,DEFAULT_SIZE))
/**/.addContainerGap(4,4));
s2Layout.setVerticalGroup(s2Layout.createSequentialGroup()
/**/.addGroup(s2Layout.createParallelGroup(Alignment.BASELINE)
/* */.addComponent(twLabel)
/* */.addComponent(tWidth))
/**/.addGap(2)
/**/.addGroup(s2Layout.createParallelGroup(Alignment.BASELINE)
/* */.addComponent(thLabel)
/* */.addComponent(tHeight))
/**/.addGap(8)
/**/.addGroup(s2Layout.createParallelGroup(Alignment.BASELINE)
/* */.addComponent(hoLabel)
/* */.addComponent(hOffset))
/**/.addGap(2)
/**/.addGroup(s2Layout.createParallelGroup(Alignment.BASELINE)
/* */.addComponent(voLabel)
/* */.addComponent(vOffset))
/**/.addGap(8)
/**/.addGroup(s2Layout.createParallelGroup(Alignment.BASELINE)
/* */.addComponent(hsLabel)
/* */.addComponent(hSep))
/**/.addGap(2)
/**/.addGroup(s2Layout.createParallelGroup(Alignment.BASELINE)
/* */.addComponent(vsLabel)
/* */.addComponent(vSep))
/**/.addContainerGap(8,8));
side2.setVisible(tileset.isSelected());
}
protected boolean areResourceFieldsEqual()
{
return !imageChanged;
}
public void commitChanges()
{
res.setName(name.getText());
}
@Override
public void updateResource()
{
super.updateResource();
imageChanged = false;
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource() == load)
{
BufferedImage img = Util.getValidImage();
if (img != null)
{
res.setBackgroundImage(img);
imageChanged = true;
cleanup();
}
return;
}
if (e.getSource() == edit)
{
try
{
if (editor == null)
new BackgroundEditor();
else
editor.start();
}
catch (IOException ex)
{
ex.printStackTrace();
}
return;
}
super.actionPerformed(e);
}
@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;
}
private class BackgroundEditor implements UpdateListener
{
public final FileChangeMonitor monitor;
public BackgroundEditor() throws IOException
{
BufferedImage bi = res.getBackgroundImage();
if (bi == null)
{
bi = new BufferedImage(640,480,BufferedImage.TYPE_3BYTE_BGR);
res.setBackgroundImage(bi);
imageChanged = true;
}
File f = File.createTempFile(res.getName(),
"." + Prefs.externalBackgroundExtension,LGM.tempDir); //$NON-NLS-1$
f.deleteOnExit();
FileOutputStream out = new FileOutputStream(f);
ImageIO.write(bi,Prefs.externalBackgroundExtension,out);
out.close();
monitor = new FileChangeMonitor(f,SwingExecutor.INSTANCE);
monitor.updateSource.addListener(this);
editor = this;
start();
}
public void start() throws IOException
{
if (!Prefs.useExternalBackgroundEditor || Prefs.externalBackgroundEditorCommand == null)
try
{
Desktop.getDesktop().edit(monitor.file);
}
catch (UnsupportedOperationException e)
{
throw new UnsupportedOperationException("no internal or system background editor",e);
}
else
Runtime.getRuntime().exec(
String.format(Prefs.externalBackgroundEditorCommand,monitor.file.getAbsolutePath()));
}
public void stop()
{
monitor.stop();
monitor.file.delete();
editor = null;
}
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.setBackgroundImage(img);
imageChanged = true;
break;
case DELETED:
editor = null;
}
}
}
public void dispose()
{
cleanup();
super.dispose();
}
protected void cleanup()
{
if (editor != null) editor.stop();
}
public void updated(UpdateEvent e)
{
width.setText(Messages.getString("BackgroundFrame.WIDTH") + res.getWidth()); //$NON-NLS-1$
height.setText(Messages.getString("BackgroundFrame.HEIGHT") + res.getHeight()); //$NON-NLS-1$
}
private class BackgroundPropertyListener extends PropertyUpdateListener<PBackground>
{
public void updated(PropertyUpdateEvent<PBackground> e)
{
//USE_AS_TILESET
side2.setVisible((Boolean) res.get(PBackground.USE_AS_TILESET));
}
}
}