+package net.jcornell.tile_draw;
+
+import java.awt.Dialog;
+import java.awt.Dimension;
+import javax.swing.JTextField;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Image;
+import java.io.File;
+import java.awt.event.FocusEvent;
+import java.io.IOException;
+import java.util.Arrays;
+import javax.swing.JDialog;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.util.List;
+import java.util.stream.Collectors;
+import javax.swing.BorderFactory;
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.table.TableCellEditor;
+import javax.swing.ImageIcon;
+import java.awt.Insets;
+import javax.swing.JButton;
+import javax.swing.JFileChooser;
+import javax.swing.JComponent;
+import java.awt.Component;
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSpinner;
+import javax.swing.JTable;
+import javax.swing.SpinnerNumberModel;
+import javax.swing.table.AbstractTableModel;
+
+import net.jcornell.tile_draw.TileSetsController.TileSet;
+import net.jcornell.tile_draw.util.JSpinnerTableCellEditor;
+import net.jcornell.tile_draw.util.Util;
+
+
+public class TileSetEditController extends AbstractTableModel {
+ public static final int DEFAULT_MULTIPLICITY = 1;
+ public static final int THUMBNAIL_HEIGHT = 50;
+ protected static final Class<?>[] COL_CLASSES = new Class<?>[] {Boolean.class, ImageIcon.class, Integer.class};
+ protected static final String[] COL_NAMES = new String[] {"", "Tile", "Multiplicity"};
+
+ public static interface EventListener {
+ public void onSubmitTileSet(TileSet value);
+ }
+
+ public final TileSet tileSet;
+ public TileSetEditSwingView view;
+ public final TileSetsController parent;
+ public final EventListener listener;
+ protected final TileSet originalValue;
+
+ public TileSetEditController(TileSet tileSet, EventListener listener, TileSetsController parent) {
+ this.tileSet = tileSet.copy();
+ this.parent = parent;
+ this.listener = listener;
+ originalValue = tileSet;
+ }
+
+ public void addTiles(List<TileConfigModel> toAdd) {
+ int oldSize = tileSet.tileModels.size();
+ tileSet.tileModels.addAll(toAdd);
+ super.fireTableRowsInserted(oldSize, tileSet.tileModels.size());
+ }
+
+ public void dropTiles(List<TileConfigModel> toDrop) {
+ for (TileConfigModel m : toDrop) {
+ int row = tileSet.tileModels.indexOf(m);
+ tileSet.tileModels.remove(m);
+ super.fireTableRowsDeleted(row, row);
+ }
+ }
+
+ public List<TileConfigModel> getSelectedTiles() {
+ return
+ tileSet.tileModels.stream()
+ .filter(m -> m.selected)
+ .collect(Collectors.toList())
+ ;
+ }
+
+ public File getChooserDir() {
+ return parent.fileChooserDir;
+ }
+
+ public void setChooserDir(File newDir) {
+ parent.setFileChooserDir(newDir);
+ }
+
+ public void submitChanges() {
+ listener.onSubmitTileSet(tileSet);
+ }
+
+ public boolean isClean() {
+ return tileSet.equals(originalValue);
+ }
+
+ @Override
+ public int getRowCount() {
+ return tileSet.tileModels.size();
+ }
+
+ @Override
+ public int getColumnCount() {
+ return COL_CLASSES.length;
+ }
+
+ @Override
+ public String getColumnName(int col) {
+ return COL_NAMES[col];
+ }
+
+ @Override
+ public Object getValueAt(int row, int col) {
+ TileConfigModel m = tileSet.tileModels.get(row);
+ return new Object[] {m.selected, m.imageView, m.multiplicity}[col];
+ }
+
+ @Override
+ public void setValueAt(Object value, int row, int col) {
+ TileConfigModel m = tileSet.tileModels.get(row);
+ if (col == 0) {
+ m.selected = (boolean) value;
+ } else if (col == 2) {
+ m.multiplicity = (int) value;
+ } else {
+ throw new AssertionError();
+ }
+ }
+
+ @Override
+ public Class<?> getColumnClass(int col) {
+ return COL_CLASSES[col];
+ }
+
+ @Override
+ public boolean isCellEditable(int row, int col) {
+ return new boolean[] {true, false, true}[col];
+ }
+}
+
+
+class TileSetEditSwingView {
+ protected final TileSetEditController controller;
+ protected JTextField nameField;
+ protected JTable grid;
+ protected TableCellEditor multColumnEditor;
+ public JOptionPane optionPane;
+ public JDialog dialog;
+
+ public TileSetEditSwingView(TileSetEditController controller, Component dialogParent) {
+ this.controller = controller;
+ populateUi(dialogParent);
+ }
+
+ private static TileConfigModel tryTileBuild(File imageFile) {
+ try {
+ return new TileConfigModel(imageFile, TileSetEditController.DEFAULT_MULTIPLICITY);
+ } catch (Util.ImageLoadException e) {
+ return null;
+ }
+ }
+
+ protected void populateUi(Component dialogParent) {
+ grid = new JTable(controller);
+ grid.setRowHeight(TileSetEditController.THUMBNAIL_HEIGHT);
+ JSpinner spinner = new JSpinner(
+ new SpinnerNumberModel(1, 1, Integer.MAX_VALUE, 1)
+ );
+ multColumnEditor = new JSpinnerTableCellEditor(spinner);
+ grid.getColumnModel().getColumn(2).setCellEditor(multColumnEditor);
+
+ JPanel panel = new JPanel(new GridBagLayout());
+
+ nameField = new JTextField(controller.originalValue.name);
+ GridBagConstraints nameFieldC = new GridBagConstraints();
+ nameFieldC.gridx = 0;
+ nameFieldC.gridy = GridBagConstraints.RELATIVE;
+ nameFieldC.fill = GridBagConstraints.HORIZONTAL;
+ nameFieldC.insets = new Insets(0, 0, 10, 0);
+ nameFieldC.ipady = 5;
+ panel.add(nameField, nameFieldC);
+
+ JScrollPane tilesScrollPane = new JScrollPane(grid);
+ tilesScrollPane.setPreferredSize(new Dimension(400, 150));
+
+ GridBagConstraints tilesPaneC = new GridBagConstraints();
+ tilesPaneC.gridx = 0;
+ tilesPaneC.gridy = GridBagConstraints.RELATIVE;
+ tilesPaneC.fill = GridBagConstraints.BOTH;
+ tilesPaneC.weightx = 1;
+ tilesPaneC.weighty = 1;
+ panel.add(tilesScrollPane, tilesPaneC);
+
+ JButton deleteBtn = new JButton("Delete selected");
+ deleteBtn.addActionListener(e -> {
+ controller.dropTiles(controller.getSelectedTiles());
+ });
+
+ JButton addBtn = new JButton("Add…");
+ addBtn.addActionListener(e -> {
+ JFileChooser chooser = new JFileChooser(controller.getChooserDir());
+ chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
+ chooser.setMultiSelectionEnabled(true);
+ int result = chooser.showOpenDialog(addBtn);
+ controller.setChooserDir(chooser.getCurrentDirectory());
+ if (result == JFileChooser.APPROVE_OPTION) {
+ File[] files = chooser.getSelectedFiles();
+ List<TileConfigModel> newModels = Arrays.stream(files)
+ .map(TileSetEditSwingView::tryTileBuild)
+ .collect(Collectors.toList())
+ ;
+ if (newModels.contains(null)) {
+ JOptionPane.showMessageDialog(
+ panel, "Failed to load one or more files as images", "Image load error", JOptionPane.WARNING_MESSAGE
+ );
+ }
+ List<TileConfigModel> okay =
+ newModels.stream()
+ .filter(m -> m != null)
+ .collect(Collectors.toList())
+ ;
+ controller.addTiles(okay);
+ }
+ });
+
+ JPanel buttonRow = new JPanel();
+ buttonRow.setLayout(new BoxLayout(buttonRow, BoxLayout.LINE_AXIS));
+ buttonRow.add(deleteBtn);
+ buttonRow.add(Box.createRigidArea(new Dimension(5, 0)));
+ buttonRow.add(addBtn);
+ buttonRow.add(Box.createHorizontalGlue());
+
+ GridBagConstraints btnBarC = new GridBagConstraints();
+ btnBarC.gridx = 0;
+ btnBarC.gridy = GridBagConstraints.RELATIVE;
+ btnBarC.fill = GridBagConstraints.HORIZONTAL;
+ btnBarC.insets = new Insets(10, 0, 5, 0);
+ panel.add(buttonRow, btnBarC);
+
+ optionPane = new JOptionPane(panel, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
+ dialog = optionPane.createDialog(dialogParent, "Tile set editor");
+ dialog.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
+ }
+
+ public void finishEditing() {
+ multColumnEditor.stopCellEditing();
+ }
+
+ public void submitChanges() {
+ controller.tileSet.name = nameField.getText();
+ controller.submitChanges();
+ }
+}