package org.csdgn.cddatse;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.ListCellRenderer;
import javax.swing.ScrollPaneConstants;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
import javax.swing.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileFilter;
import org.csdgn.cddatse.data.AsciiEntry;
import org.csdgn.cddatse.data.BaseTile;
import org.csdgn.cddatse.data.GFX;
import org.csdgn.cddatse.data.InternalTile;
import org.csdgn.cddatse.data.TileInfo;
import org.csdgn.maru.io.EqualsFileFilter;
import org.csdgn.maru.swing.ArrayListModel;
import org.csdgn.maru.util.Tuple;
public class EditorFrame extends JFrame {
private static class InternalTileCellRenderer extends JLabel implements ListCellRenderer<InternalTile> {
private static final long serialVersionUID = 468626665326983329L;
public InternalTileCellRenderer() {
setOpaque(true);
setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 0));
}
@Override
public Component getListCellRendererComponent(JList<? extends InternalTile> list, InternalTile value, int index,
boolean isSelected, boolean cellHasFocus) {
setText(value.toString());
if(!list.isDisplayable()) {
setBackground(UIManager.getColor("List.background"));
setForeground(UIManager.getColor("List.disabledForeground"));
return this;
}
if(isSelected) {
setBackground(UIManager.getColor("List.selectionBackground"));
setForeground(UIManager.getColor("List.selectionForeground"));
} else {
setBackground(UIManager.getColor("List.background"));
if(value.isImageless()) {
setForeground(Color.decode("#AA0000"));
} else {
setForeground(UIManager.getColor("List.foreground"));
}
}
return this;
}
}
private static final long serialVersionUID = -4047187015916073121L;
private AboutDialog about = new AboutDialog();
private JPanel blankPanel = new JPanel();
private JButton btnAdd;
private JButton btnSort;
private JButton btnSub;
private JPanel contentPane;
private CreateDialog create;
private JList<InternalTile> list;
private ArrayListModel<InternalTile> listModel;
private JPanel listPanel;
private MergeConflictDialog mcDialog;
private JMenuItem miAddMissingTiles;
private JMenuItem miCombineIdenticalImages;
private JMenuItem miGenerateAsciiTiles;
private JMenuItem miGenerateAsciiTilesBG;
private JMenuItem miImport;
private JMenuItem miOptimize;
private JMenuItem miSave;
private JMenuItem miSaveAs;
private JMenuItem mntmOptions;
private HashMap<String, Integer> nameMap = new HashMap<String, Integer>();
private boolean opened = false;
private OptionsDialog options;
private File outputFolder;
private JFileChooser saveChooser = null;
private boolean savedBefore = false;
private boolean searching = false;
private ArrayListModel<InternalTile> searchListModel;
private String searchString = "";
private TilePanel tilePanel;
private JScrollPane tileScrollPane;
private JFileChooser tilesetChooser = null;
private JTextField txtSearch;
/**
* Create the frame.
*/
public EditorFrame() {
setTitle(Version.getVersionString());
setIconImages(AppToolkit.getAppIconImages());
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
setBounds(100, 100, 629, 382);
setJMenuBar(createMenu());
addComponentListener(new ComponentAdapter() {
@Override
public void componentShown(ComponentEvent e) {
doPostShow();
removeComponentListener(this);
}
});
create = new CreateDialog(this);
mcDialog = new MergeConflictDialog(this);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
contentPane.setLayout(new BorderLayout(0, 0));
setContentPane(contentPane);
JSplitPane splitPane = new JSplitPane();
splitPane.setResizeWeight(0.15);
contentPane.add(splitPane, BorderLayout.CENTER);
listPanel = new JPanel();
listPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
splitPane.setLeftComponent(listPanel);
listPanel.setLayout(new BorderLayout(5, 2));
listModel = new ArrayListModel<InternalTile>();
searchListModel = new ArrayListModel<InternalTile>();
list = new JList<InternalTile>(listModel);
list.setCellRenderer(new InternalTileCellRenderer());
list.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
onListSelected(list.getSelectedIndex());
}
});
JScrollPane scrollPane = new JScrollPane(list);
scrollPane.setPreferredSize(new Dimension(140, 130));
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
listPanel.add(scrollPane, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
listPanel.add(buttonPanel, BorderLayout.NORTH);
buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
btnAdd = new JButton("+");
btnAdd.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
listModel.add(new InternalTile());
int index = listModel.getSize() - 1;
list.setSelectedIndex(index);
list.ensureIndexIsVisible(index);
}
});
buttonPanel.add(btnAdd);
btnSub = new JButton("-");
btnSub.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int[] data = list.getSelectedIndices();
ArrayList<InternalTile> dalist = listModel.getList();
if(searching) {
for(int i = data.length - 1; i >= 0; --i) {
InternalTile tile = searchListModel.get(data[i]);
decNameMap(tile.id);
dalist.remove(tile);
}
listModel.fireUpdate();
doSearch(searchString);
} else {
for(int i = data.length - 1; i >= 0; --i) {
decNameMap(dalist.get(data[i]).id);
dalist.remove(data[i]);
}
listModel.fireUpdate();
}
}
});
buttonPanel.add(btnSub);
btnSort = new JButton("Sort");
btnSort.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
sortList();
}
});
buttonPanel.add(btnSort);
JPanel searchPanel = new JPanel();
listPanel.add(searchPanel, BorderLayout.SOUTH);
searchPanel.setLayout(new BorderLayout(5, 2));
JLabel lblSearch = new JLabel("Search:");
searchPanel.add(lblSearch, BorderLayout.WEST);
txtSearch = new JTextField();
txtSearch.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void changedUpdate(DocumentEvent e) {
}
@Override
public void insertUpdate(DocumentEvent e) {
doSearch(txtSearch.getText());
}
@Override
public void removeUpdate(DocumentEvent e) {
doSearch(txtSearch.getText());
}
});
searchPanel.add(txtSearch);
txtSearch.setColumns(10);
tileScrollPane = new JScrollPane();
tileScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
tileScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
splitPane.setRightComponent(tileScrollPane);
tileScrollPane.setViewportView(blankPanel);
tilePanel = new TilePanel(this);
disableInterface();
}
private void browseForSaveLocation() {
if(saveChooser == null) {
AppToolkit.setFileChooserReadOnly(false);
saveChooser = new JFileChooser();
saveChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
}
saveChooser.setCurrentDirectory(Options.lastBrowsedDirectory);
if(saveChooser.showOpenDialog(this) != JFileChooser.APPROVE_OPTION) {
return;
}
Options.lastBrowsedDirectory = saveChooser.getCurrentDirectory();
File file = saveChooser.getSelectedFile();
if(!file.exists()) {
return;
}
if(!file.isDirectory()) {
file = file.getParentFile();
}
outputFolder = file;
}
private File browseForTileset() {
if(tilesetChooser == null) {
AppToolkit.setFileChooserReadOnly(true);
tilesetChooser = new JFileChooser();
FileFilter filter = new EqualsFileFilter("tileset.txt", "Tileset Definition");
tilesetChooser.addChoosableFileFilter(filter);
tilesetChooser.setFileFilter(filter);
}
tilesetChooser.setCurrentDirectory(Options.lastBrowsedDirectory);
if(tilesetChooser.showOpenDialog(this) != JFileChooser.APPROVE_OPTION) {
return null;
}
Options.lastBrowsedDirectory = tilesetChooser.getCurrentDirectory();
return tilesetChooser.getSelectedFile();
}
private JMenuBar createMenu() {
JMenuBar menuBar = new JMenuBar();
JMenu mnFile = new JMenu("File");
mnFile.setMnemonic(KeyEvent.VK_F);
menuBar.add(mnFile);
JMenuItem miNew = new JMenuItem("New");
miNew.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if(create.showDialog() == CreateDialog.OPTION_CREATE) {
reset();
if(GFX.instance != null) {
GFX.instance.dispose();
}
GFX.instance = create.createGFX();
enableInterface();
}
}
});
miNew.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_MASK));
miNew.setMnemonic(KeyEvent.VK_N);
mnFile.add(miNew);
JMenuItem miOpen = new JMenuItem("Open");
miOpen.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doOpen();
}
});
miOpen.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_MASK));
miOpen.setMnemonic(KeyEvent.VK_O);
mnFile.add(miOpen);
mnFile.addSeparator();
miImport = new JMenuItem("Import...");
miImport.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doImport();
}
});
miImport.setMnemonic(KeyEvent.VK_I);
mnFile.add(miImport);
mnFile.addSeparator();
miSave = new JMenuItem("Save");
miSave.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doSave();
}
});
miSave.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_MASK));
miSave.setMnemonic(KeyEvent.VK_S);
mnFile.add(miSave);
miSaveAs = new JMenuItem("Save As");
miSaveAs.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
browseForSaveLocation();
opened = false;
doSave();
}
});
miSaveAs.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_MASK | InputEvent.ALT_MASK));
miSaveAs.setMnemonic(KeyEvent.VK_A);
mnFile.add(miSaveAs);
mnFile.addSeparator();
JMenuItem miExit = new JMenuItem("Exit");
miExit.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
dispose();
}
});
miExit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4, InputEvent.ALT_MASK));
miExit.setMnemonic(KeyEvent.VK_X);
mnFile.add(miExit);
JMenu mnEdit = new JMenu("Edit");
mnEdit.setMnemonic(KeyEvent.VK_E);
menuBar.add(mnEdit);
miOptimize = new JMenuItem("Optimize");
miOptimize.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK));
miOptimize.setMnemonic(KeyEvent.VK_P);
miOptimize.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doOptimize();
}
});
mnEdit.add(miOptimize);
miCombineIdenticalImages = new JMenuItem("Merge identical tiles");
miCombineIdenticalImages.setMnemonic(KeyEvent.VK_M);
miCombineIdenticalImages.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doMergeTiles();
}
});
mnEdit.add(miCombineIdenticalImages);
miAddMissingTiles = new JMenuItem("Add missing tiles");
miAddMissingTiles.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doAddMissingIds();
}
});
miAddMissingTiles.setMnemonic(KeyEvent.VK_A);
mnEdit.add(miAddMissingTiles);
mnEdit.addSeparator();
miGenerateAsciiTiles = new JMenuItem("Generate ascii tiles");
miGenerateAsciiTiles.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doGenerateAsciiTiles(false);
}
});
miGenerateAsciiTiles.setToolTipText("Generates ascii tiles for entries that have no tiles at all. NOT PERFECT!");
mnEdit.add(miGenerateAsciiTiles);
miGenerateAsciiTilesBG = new JMenuItem("Generate ascii tiles /w bg");
miGenerateAsciiTilesBG.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doGenerateAsciiTiles(true);
}
});
miGenerateAsciiTilesBG
.setToolTipText("Generates ascii tiles for entries that have no tiles at all. Generates a black background for these tiles as well. NOT PERFECT!");
mnEdit.add(miGenerateAsciiTilesBG);
mnEdit.addSeparator();
mntmOptions = new JMenuItem("Options");
mntmOptions.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doShowOptions();
}
});
mnEdit.add(mntmOptions);
JMenu mnHelp = new JMenu("Help");
mnHelp.setMnemonic(KeyEvent.VK_H);
menuBar.add(mnHelp);
JMenuItem miAbout = new JMenuItem("About");
miAbout.setMnemonic(KeyEvent.VK_A);
miAbout.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doShowAbout();
}
});
mnHelp.add(miAbout);
return menuBar;
}
private int decNameMap(String id) {
Integer count = nameMap.get(id);
if(count == null) {
return 0;
}
--count;
if(count <= 0) {
nameMap.remove(id);
return 0;
}
nameMap.put(id, count);
return count;
}
private void disableInterface() {
btnAdd.setEnabled(false);
btnSub.setEnabled(false);
btnSort.setEnabled(false);
txtSearch.setEnabled(false);
list.setEnabled(false);
miOptimize.setEnabled(false);
miImport.setEnabled(false);
miSave.setEnabled(false);
miSaveAs.setEnabled(false);
miCombineIdenticalImages.setEnabled(false);
miAddMissingTiles.setEnabled(false);
miGenerateAsciiTiles.setEnabled(false);
miGenerateAsciiTilesBG.setEnabled(false);
}
@Override
public void dispose() {
// TODO WARNING ARE YOU SURE YOU WANT TO CLOSE WITHOUT SAVING?
Options.save();
super.dispose();
}
private void doAddMissingIds() {
HashMap<String, AsciiEntry> entries = getAsciiEntries();
ArrayList<InternalTile> list = listModel.getList();
for(InternalTile tile : list) {
entries.remove(tile.id);
}
for(AsciiEntry e : entries.values()) {
InternalTile tile = new InternalTile();
tile.id = e.id;
incNameMap(e.id);
list.add(tile);
}
listModel.fireUpdate();
}
private void doGenerateAsciiTiles(boolean withBG) {
HashMap<String, AsciiEntry> entries = getAsciiEntries();
ArrayList<InternalTile> list = listModel.getList();
GFX gfx = GFX.instance;
BufferedImage tileset = null;
TileInfo info = GFX.instance.getTileInfo();
if(AppToolkit.showYesNoOption(this, "Would you like to use your own ascii tileset?", "Use Custom Tileset")
== JOptionPane.YES_OPTION) {
tileset = AppToolkit.browseForImage(this, "Select Ascii Tileset");
if(tileset != null) {
int w = tileset.getWidth();
int h = tileset.getHeight();
if (w % info.width != 0) {
AppToolkit.showError(this, "Image width must be a multiple of " + info.width + ".");
return;
}
if (h % info.height != 0) {
AppToolkit.showError(this, "Image height must be a multiple of " + info.height + ".");
return;
}
//a more complex error
int cols = w / info.width;
int rows = h / info.height;
if(rows * cols < 256) {
AppToolkit.showError(this, "The number of tileset entries must be at least 256.");
return;
}
} else return;
}
BufferedImage bg = new BufferedImage(info.width, info.height, BufferedImage.TYPE_INT_RGB);
if(withBG) {
Graphics2D gx = bg.createGraphics();
gx.setColor(Color.BLACK);
gx.fillRect(0, 0, info.width, info.height);
gx.dispose();
gfx.images.add(bg);
}
for(InternalTile tile : list) {
if(tile.isImageless()) {
AsciiEntry e = entries.get(tile.id);
if(e != null) {
BufferedImage img = e.createAsciiTile(info.width, info.height, tileset);
gfx.images.add(img);
tile.image.first = img;
if(withBG && !e.isOverlay()) {
tile.image.second = bg;
}
if(e.isMultitile()) {
// let'sa go!
// center
img = e.createAsciiTile(info.width, info.height, 197, tileset);
gfx.images.add(img);
tile.center = true;
tile.centerImage.first = img;
if(withBG) {
tile.centerImage.second = bg;
}
// corner
img = e.createAsciiTile(info.width, info.height, 218, tileset);
gfx.images.add(img);
tile.corner = true;
tile.cornerImage.first = img;
if(withBG) {
tile.cornerImage.second = bg;
}
// edge
img = e.createAsciiTile(info.width, info.height, 179, tileset);
gfx.images.add(img);
tile.edge = true;
tile.edgeImage.first = img;
if(withBG) {
tile.edgeImage.second = bg;
}
// tConnection
img = e.createAsciiTile(info.width, info.height, 194, tileset);
gfx.images.add(img);
tile.tConnection = true;
tile.tConnectionImage.first = img;
if(withBG) {
tile.tConnectionImage.second = bg;
}
// end_piece
img = e.createAsciiTile(info.width, info.height, 179, tileset);
gfx.images.add(img);
tile.endPiece = true;
tile.endPieceImage.first = img;
if(withBG) {
tile.endPieceImage.second = bg;
}
// unconnected
img = e.createAsciiTile(info.width, info.height, 254, tileset);
gfx.images.add(img);
tile.unconnected = true;
tile.unconnectedImage.first = img;
if(withBG) {
tile.unconnectedImage.second = bg;
}
}
if(e.id.startsWith("vp_")) {
tile.rotates = true;
}
if(e.hasBrokenTile()) {
img = e.createBrokenAsciiTile(info.width, info.height, tileset);
gfx.images.add(img);
tile.broken = true;
tile.brokenImage.first = img;
if(withBG) {
tile.brokenImage.second = bg;
}
}
}
} else if(entries.get(tile.id) == null) {
System.out.println("Nothing for '" + tile.id + "'.");
}
}
listModel.fireUpdate();
}
private void doImport() {
File file = browseForTileset();
if(file == null) {
return;
}
if(!file.exists()) {
AppToolkit.showError(this, "File not found!");
return;
}
GFX theirs = GFX.load(file);
if(theirs == null) {
AppToolkit.showError(this, "Failed to load Tileset!");
return;
}
GFX ours = GFX.instance;
if(theirs.getTileInfo().width != ours.getTileInfo().width || theirs.getTileInfo().height != ours.getTileInfo().height) {
AppToolkit.showError(this, "Targeted tileset does not have the same tilesize!");
return;
}
// check for duplicate id's
ArrayList<Tuple<BaseTile>> duplicates = new ArrayList<Tuple<BaseTile>>();
// first is ours, second is theirs
// for every entry, check every entry... >.<
HashMap<String, BaseTile> map = new HashMap<String, BaseTile>();
updateGFXTiles();
for(BaseTile tile : ours.tileset.tiles) {
map.put(tile.id, tile);
}
for(BaseTile tile : theirs.tileset.tiles) {
if(map.containsKey(tile.id)) {
// add it to the duplicate list
duplicates.add(new Tuple<BaseTile>(map.get(tile.id), tile));
} else {
// just add the tile if we don't already have it
ours.addTile(theirs, tile);
}
}
map.clear();
if(duplicates.size() > 0) {
int option = mcDialog.showDialog();
switch(option) {
case MergeConflictDialog.OPTION_OURS:
// we're done!
break;
case MergeConflictDialog.OPTION_THEIRS:
// for every item in the duplicate map
for(Tuple<BaseTile> dup : duplicates) {
ours.replaceTile(dup.first, theirs, dup.second);
}
break;
case MergeConflictDialog.OPTION_MANUAL:
// TODO
break;
}
}
updateInternalTiles();
}
private void doMergeTiles() {
if(AppToolkit.confirmWarning(this, "Merging tiles is a time consuming process, are you sure you want to continue?") != JOptionPane.YES_OPTION) {
return;
}
disableInterface();
updateGFXTiles();
final ProgressDialog dialog = new ProgressDialog(this);
dialog.execute(new Runnable() {
@Override
public void run() {
// build a list of identical tiles
HashMap<Integer, Integer> shiftMap = new HashMap<Integer, Integer>();
ArrayList<BufferedImage> ref = GFX.instance.images;
dialog.setLabelText("Locating duplicate images.");
dialog.setProgressMaximum(ref.size());
for(int i = 0; i < ref.size() - 1; ++i) {
if(shiftMap.containsKey(i)) {
continue;
}
for(int j = i + 1; j < ref.size(); ++j) {
if(AppToolkit.isEqual(ref.get(i), ref.get(j))) {
shiftMap.put(j, i);
}
}
dialog.setProgress(i);
}
// update indexes
dialog.setLabelText("Updating tile indexes (1st pass).");
dialog.setProgress(0);
dialog.setProgressMaximum(1);
for(BaseTile tile : GFX.instance.tileset.tiles) {
tile.replaceIndexes(shiftMap);
}
dialog.setProgress(1);
// rebuild tileset, removing duplicates
dialog.setLabelText("Rebuilding tileset.");
dialog.setProgress(0);
dialog.setProgressMaximum(1);
HashSet<Integer> dupSet = new HashSet<Integer>(shiftMap.keySet());
shiftMap.clear();
ArrayList<BufferedImage> keep = new ArrayList<BufferedImage>();
for(int i = 0; i < ref.size(); ++i) {
int index = keep.size();
if(!dupSet.contains(i)) {
keep.add(ref.get(i));
}
shiftMap.put(i, index);
}
dialog.setProgress(1);
ref.clear();
GFX.instance.images = keep;
// update indexes again for the rebuilt tileset
dialog.setLabelText("Updating tile indexes (2nd pass).");
dialog.setProgress(0);
dialog.setProgressMaximum(1);
for(BaseTile tile : GFX.instance.tileset.tiles) {
tile.replaceIndexes(shiftMap);
}
dialog.setProgress(1);
dialog.setVisible(false);
}
});
updateInternalTiles();
enableInterface();
}
private void doOpen() {
File file = browseForTileset();
if(file == null) {
return;
}
if(!file.exists()) {
AppToolkit.showError(this, "File not found!");
return;
}
GFX tmp = GFX.load(file);
if(tmp == null) {
AppToolkit.showError(this, "Failed to load Tileset!");
return;
}
reset();
GFX.instance = tmp;
opened = true;
outputFolder = file.getParentFile();
updateInternalTiles();
updateIDList();
enableInterface();
}
private void doOptimize() {
updateGFXTiles();
GFX.instance.optimize();
updateInternalTiles();
}
private void doPostShow() {
if(Options.cataclysmDirectory == null) {
doShowOptions();
}
}
private void doSave() {
if(opened && !savedBefore) {
if(JOptionPane.showConfirmDialog(this, "Are you sure you want to overwrite?", "Confirm Overwrite", JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
return;
}
}
if(outputFolder == null) {
browseForSaveLocation();
if(outputFolder == null) {
return;
}
}
updateGFXTiles();
try {
GFX.instance.save(outputFolder);
} catch(IOException e) {
e.printStackTrace();
}
savedBefore = true;
}
private void doSearch(String search) {
int minIndex = list.getMinSelectionIndex();
InternalTile lastValue = null;
if(minIndex >= 0 && minIndex < list.getModel().getSize()) {
lastValue = list.getModel().getElementAt(minIndex);
}
if(search.length() == 0) {
// no search
if(searching) {
searching = false;
list.setModel(listModel);
list.setSelectedValue(lastValue, true);
// enable Add and Sort
btnAdd.setEnabled(true);
btnSort.setEnabled(true);
}
} else {
if(!searching) {
searching = true;
list.setModel(searchListModel);
// disable Add and Sort
btnAdd.setEnabled(false);
btnSort.setEnabled(false);
}
ArrayListModel<InternalTile> model = listModel;
// search the old searchList instead (small optimization)
if(searchString.length() > 0 && !searchString.equals(search) && search.contains(searchString)) {
model = searchListModel;
}
// TODO create a hashmap for every character and combine a list of
// those and then search on that list
// since these lists can get pretty long otherwise
ArrayList<InternalTile> tiles = new ArrayList<InternalTile>();
for(InternalTile tile : model.getList()) {
if(tile.id.contains(search)) {
tiles.add(tile);
}
}
searchListModel.getList().clear();
searchListModel.getList().addAll(tiles);
searchListModel.fireUpdate();
// check if list contains lastValue
if(lastValue != null && tiles.contains(lastValue)) {
list.setSelectedValue(lastValue, true);
}
// TODO otherwise deselect/select first
}
searchString = search;
}
private void doShowAbout() {
about.showAboutDialog(this);
}
private void doShowOptions() {
if(options == null) {
options = new OptionsDialog(this);
}
options.setLocationRelativeTo(this);
options.setVisible(true);
if(Options.cataclysmDirectory != null) {
miAddMissingTiles.setEnabled(true);
} else {
miAddMissingTiles.setEnabled(false);
}
}
private void enableInterface() {
btnAdd.setEnabled(true);
btnSub.setEnabled(true);
btnSort.setEnabled(true);
txtSearch.setEnabled(true);
list.setEnabled(true);
miOptimize.setEnabled(true);
miImport.setEnabled(true);
miSave.setEnabled(true);
miSaveAs.setEnabled(true);
miCombineIdenticalImages.setEnabled(true);
if(Options.cataclysmDirectory != null) {
miAddMissingTiles.setEnabled(true);
miGenerateAsciiTiles.setEnabled(true);
miGenerateAsciiTilesBG.setEnabled(true);
}
}
private HashMap<String, AsciiEntry> getAsciiEntries() {
File dataFolder = new File(Options.cataclysmDirectory, "data");
File jsonFolder = new File(dataFolder, "json");
HashMap<String, AsciiEntry> entries = new HashMap<String, AsciiEntry>();
AsciiEntry.getAllAsciiTiles(jsonFolder, entries);
return entries;
}
private int incNameMap(String id) {
Integer count = nameMap.get(id);
if(count == null) {
count = 0;
}
++count;
nameMap.put(id, count);
return count;
}
private void onListSelected(int index) {
ArrayListModel<InternalTile> model = listModel;
if(searching) {
model = searchListModel;
}
if(index >= 0) {
tilePanel.setTilesetEntry(model.get(index));
tileScrollPane.setViewportView(tilePanel);
} else {
tileScrollPane.setViewportView(blankPanel);
}
}
private void reset() {
if(GFX.instance != null) {
GFX.instance.dispose();
}
GFX.instance = null;
opened = false;
savedBefore = false;
outputFolder = null;
listModel.getList().clear();
listModel.fireUpdate();
if(searching && txtSearch.isEnabled()) {
txtSearch.setText("");
doSearch("");
}
}
private void sortList() {
Collections.sort(listModel.getList(), new Comparator<InternalTile>() {
@Override
public int compare(InternalTile t1, InternalTile t2) {
return t1.id.compareToIgnoreCase(t2.id);
}
});
updateIDList();
}
private void updateGFXTiles() {
GFX.instance.tileset.tiles.clear();
for(InternalTile tile : listModel.getList()) {
GFX.instance.tileset.tiles.add(tile.toBaseTile());
}
}
protected void updateIDList() {
list.repaint();
}
private void updateInternalTiles() {
// convert basetiles to internal tiles
ArrayList<InternalTile> list = listModel.getList();
list.clear();
nameMap.clear();
for(BaseTile tile : GFX.instance.tileset.tiles) {
// add these base tiles to list
InternalTile it = new InternalTile(tile);
incNameMap(it.id);
list.add(it);
}
// add all sortList to listModel
listModel.fireUpdate();
}
protected boolean updateTileId(String oldId, String newId) {
if(oldId != null) {
decNameMap(oldId);
return incNameMap(newId) == 1;
}
Integer count = nameMap.get(newId);
if(count == null || count <= 1) {
return true;
}
return false;
}
}