Java tutorial
/* * $Id$ * * Copyright (c) 2000-2003 by Rodney Kinney * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License (LGPL) as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, copies are available * at http://www.opensource.org. */ package VASL.build.module.map; import java.awt.CardLayout; import java.awt.Component; import java.awt.Container; import java.awt.Dialog; import java.awt.Dimension; import java.awt.Frame; import java.awt.GridLayout; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import java.text.Collator; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.StringTokenizer; import java.util.Vector; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.DefaultComboBoxModel; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JDialog; 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.JTextField; import javax.swing.ListModel; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionListener; import org.apache.commons.codec.digest.DigestUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import VASL.build.module.map.boardPicker.ASLBoard; import VASL.build.module.map.boardPicker.ASLBoardSlot; import VASL.build.module.map.boardPicker.BoardException; import VASL.build.module.map.boardPicker.Overlay; import VASL.build.module.map.boardPicker.SSROverlay; import VASSAL.Info; import VASSAL.build.BadDataReport; import VASSAL.build.Buildable; import VASSAL.build.Builder; import VASSAL.build.Configurable; import VASSAL.build.GameModule; import VASSAL.build.module.map.BoardPicker; import VASSAL.build.module.map.GlobalMap; import VASSAL.build.module.map.boardPicker.Board; import VASSAL.build.module.map.boardPicker.BoardSlot; import VASSAL.build.module.map.boardPicker.board.HexGrid; import VASSAL.command.Command; import VASSAL.command.NullCommand; import VASSAL.configure.DirectoryConfigurer; import VASSAL.configure.ValidationReport; import VASSAL.tools.ErrorDialog; import VASSAL.tools.ReadErrorDialog; import VASSAL.tools.io.IOUtils; public class ASLBoardPicker extends BoardPicker implements ActionListener { private static final Logger logger = LoggerFactory.getLogger(ASLBoardPicker.class); /** The key for the preferences setting giving the board directory */ public static final String BOARD_DIR = "boardURL"; private File boardDir; protected TerrainEditor terrain; private SetupControls setupControls; private boolean enableDeluxe; public ASLBoardPicker() { } protected void initComponents() { initTerrainEditor(); super.initComponents(); addButton("Add overlays"); addButton("Crop boards"); addButton("Terrain SSR"); } public Command decode(String command) { if (command.startsWith("bd\t")) { List<Board> v = new ArrayList<Board>(); Command comm = new NullCommand(); if (command.length() > 3) { command = command.substring(3); for (int index = command.indexOf("bd\t"); index > 0; index = command.indexOf("bd\t")) { ASLBoard b = new ASLBoard(); String boardDesc = command.substring(0, index); try { buildBoard(b, boardDesc); v.add(b); } catch (final BoardException e) { ErrorDialog.dataError(new BadDataReport("Board not found", boardDesc, e)); } command = command.substring(index + 3); } ASLBoard b = new ASLBoard(); try { buildBoard(b, command); v.add(b); } catch (final BoardException e) { ErrorDialog.dataError(new BadDataReport("Unable to build board", command, e)); } } comm = comm.append(new SetBoards(this, v)); return comm; } else { return null; } } public String encode(Command c) { if (c instanceof SetBoards && map != null) { String s = "bd\t"; for (Iterator it = getSelectedBoards().iterator(); it.hasNext();) { ASLBoard b = (ASLBoard) it.next(); s += b.getState(); if (it.hasNext()) { s += "bd\t"; } } return s; } else { return null; } } public void addTo(Buildable b) { DirectoryConfigurer config = new VASSAL.configure.DirectoryConfigurer(BOARD_DIR, "Board Directory"); final GameModule g = GameModule.getGameModule(); g.getPrefs().addOption(config); String storedValue = g.getPrefs().getStoredValue(BOARD_DIR); if (storedValue == null || !new File(storedValue).exists()) { File archive = new File(g.getDataArchive().getName()); File dir = archive.getParentFile(); File defaultDir = new File(dir, "boards"); if (!defaultDir.exists()) { defaultDir.mkdir(); } config.setValue(defaultDir); } setBoardDir((File) g.getPrefs().getValue(BOARD_DIR)); g.getPrefs().getOption(BOARD_DIR).addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { setBoardDir((File) evt.getNewValue()); } }); super.addTo(b); } public void setBoardDir(File dir) { boardDir = dir; refreshPossibleBoards(); reset(); } @Override public void setup(boolean show) { super.setup(show); if (show) { setGlobalMapScale(); } } public void setGlobalMapScale() { Collection<Board> bds = getSelectedBoards(); if (bds.iterator().hasNext()) { double mag = bds.iterator().next().getMagnification(); double globalScale = 0.19444444; if (mag > 1.0) { globalScale /= mag; } map.getComponentsOf(GlobalMap.class).get(0).setAttribute("scale", globalScale); } } public File getBoardDir() { return boardDir; } public void initTerrainEditor() { if (terrain == null) { terrain = new TerrainEditor(); InputStream in = null; try { in = GameModule.getGameModule().getDataArchive().getInputStream("boardData/SSRControls"); terrain.readOptions(in); } catch (IOException ignore) { } finally { IOUtils.closeQuietly(in); } } } /** * Reads the current board directory and constructs the list of available boards */ public void refreshPossibleBoards() { String files[] = boardDir == null ? new String[0] : boardDir.list(); List<String> sorted = new ArrayList<String>(); for (int i = 0; i < files.length; ++i) { if (files[i].startsWith("bd") && !(new File(boardDir, files[i])).isDirectory()) { String name = files[i].substring(2); if (name.endsWith(".gif")) { name = name.substring(0, name.indexOf(".gif")); } else if (name.indexOf(".") >= 0) { name = null; } if (name != null && !sorted.contains(name)) { sorted.add(name); } } } // // * Strings with leading zeros sort ahead of those without. // * Strings with leading integer parts sort ahead of those without. // * Strings with lesser leading integer parts sort ahead of those with // greater leading integer parts. // * Strings which are otherwise equal are sorted lexicographically by // their trailing noninteger parts. // final Comparator<Object> alpha = Collator.getInstance(); final Pattern pat = Pattern.compile("((0*)\\d*)(.*)"); Comparator<String> comp = new Comparator<String>() { public int compare(String o1, String o2) { final Matcher m1 = pat.matcher(o1); final Matcher m2 = pat.matcher(o2); if (!m1.matches()) { // impossible throw new IllegalStateException(); } if (!m2.matches()) { // impossible throw new IllegalStateException(); } // count leading zeros final int z1 = m1.group(2).length(); final int z2 = m2.group(2).length(); // more leading zeros comes first if (z1 < z2) { return 1; } else if (z1 > z2) { return -1; } // same number of leading zeros final String o1IntStr = m1.group(1); final String o2IntStr = m2.group(1); if (o1IntStr.length() > 0) { if (o2IntStr.length() > 0) { try { // both strings have integer parts final BigInteger o1Int = new BigInteger(o1IntStr); final BigInteger o2Int = new BigInteger(o2IntStr); if (!o1Int.equals(o2Int)) { // one integer part is smaller than the other return o1Int.compareTo(o2Int); } } catch (NumberFormatException e) { // impossible throw new IllegalStateException(e); } } else { // only o1 has an integer part return -1; } } else if (o2IntStr.length() > 0) { // only o2 has an integer part return 1; } // the traling string part is decisive return alpha.compare(m1.group(3), m2.group(3)); } }; Collections.sort(sorted, comp); possibleBoards.clear(); for (int i = 0; i < sorted.size(); ++i) { addBoard((String) sorted.get(i)); } } public void build(Element e) { allowMultiple = true; } public void addBoard(String name) { /// final GameModule g = GameModule.getGameModule(); final String hstr = DigestUtils.shaHex(g.getGameName() + "_" + g.getGameVersion()); final File fpath = new File(boardDir, "bd" + name); final ASLTilingHandler th = new ASLTilingHandler(fpath.getAbsolutePath(), new File(Info.getConfDir(), "tiles/" + hstr), new Dimension(256, 256), 1024, 42); try { th.sliceTiles(); } catch (IOException e) { ReadErrorDialog.error(e, fpath); } /// final ASLBoard b = new ASLBoard(); b.setCommonName(name); possibleBoards.add(b); } public void validate(Buildable target, ValidationReport report) { } public String[] getAllowableBoardNames() { String s[] = new String[possibleBoards.size()]; for (int i = 0; i < s.length; ++i) { s[i] = ((ASLBoard) possibleBoards.get(i)).getCommonName(); } return s; } public Configurable[] getConfigureComponents() { return new Configurable[0]; } public Board getBoard(String name, boolean localized) { ASLBoard b = new ASLBoard(); if (name != null) { if (name.length() == 1 && name.charAt(0) <= '9' && name.charAt(0) >= '0') { name = '0' + name; } else if (name.length() == 1 && name.charAt(0) <= 'H' && name.charAt(0) >= 'A') { name = "dx" + name.toLowerCase(); } try { buildBoard(b, "0\t0\t" + name); } catch (BoardException e) { ErrorDialog.dataError(new BadDataReport("Unable to build board", name, e)); } } if (enableDeluxe) { b.setMagnification(3.0); ((HexGrid) b.getGrid()).setSnapScale(2); } return b; } public void buildBoard(ASLBoard b, String bd) throws BoardException { StringTokenizer st2 = new StringTokenizer(bd, "\t\n"); try { b.relativePosition().move(Integer.parseInt(st2.nextToken()), Integer.parseInt(st2.nextToken())); String baseName = st2.nextToken(); File f; if ((f = new File(boardDir, "bd" + baseName)).exists() && !"rb".equals(baseName)) { // Kludge to get around // case-insensitive name // conflict between RB and // reversed b b.setCommonName(baseName); b.setBaseImageFileName("bd" + baseName + ".gif", f); } else if (baseName.startsWith("0") && (f = new File(boardDir, "bd" + baseName.substring(1))).exists()) { b.setCommonName(baseName.substring(1)); b.setBaseImageFileName("bd" + baseName + ".gif", f); } else if ((f = new File(boardDir, "bd" + baseName + ".gif")).exists()) { b.setCommonName(baseName); b.setBaseImageFileName("bd" + baseName + ".gif", f); } else if (baseName.startsWith("dx") || baseName.startsWith("rdx")) { int prefix = baseName.startsWith("dx") ? 2 : 3; if ((f = new File(boardDir, "bd" + baseName.substring(prefix))).exists()) { b.setCommonName(baseName.substring(prefix)); b.setBaseImageFileName("bd" + baseName.substring(prefix) + ".gif", f); } else if ((f = new File(boardDir, "bd" + baseName + ".gif")).exists()) { b.setCommonName(baseName.substring(prefix)); b.setBaseImageFileName("bd" + baseName + ".gif", f); } b.setReversed(prefix == 3); } else if (baseName.startsWith("r")) { baseName = baseName.substring(1); if ((f = new File(boardDir, "bd" + baseName)).exists()) { b.setCommonName(baseName); b.setBaseImageFileName("bd" + baseName + ".gif", f); } else if (baseName.startsWith("0") && (f = new File(boardDir, "bd" + baseName.substring(1))).exists()) { b.setCommonName(baseName.substring(1)); b.setBaseImageFileName("bd" + baseName + ".gif", f); } else if ((f = new File(boardDir, "bd" + baseName + ".gif")).exists()) { b.setCommonName(baseName); b.setBaseImageFileName("bd" + baseName + ".gif", f); } else { throw new BoardException("Unable to find board " + baseName); } b.setReversed(true); } else { throw new BoardException("Unable to find board " + baseName); } b.readData(); } catch (Exception eParse) { // eParse.printStackTrace(); throw new BoardException(eParse.getMessage(), eParse); } try { int x1 = Integer.parseInt(st2.nextToken()); int y1 = Integer.parseInt(st2.nextToken()); int wid = Integer.parseInt(st2.nextToken()); int hgt = Integer.parseInt(st2.nextToken()); b.setCropBounds(new Rectangle(x1, y1, wid, hgt)); } catch (Exception e) { b.setCropBounds(new Rectangle(0, 0, -1, -1)); } if (bd.indexOf("VER") >= 0) { StringTokenizer st = new StringTokenizer(bd.substring(bd.indexOf("VER") + 4), "\t"); if (st.countTokens() >= 1) { String reqver = st.nextToken(); if (reqver.compareTo(b.getVersion()) != 0) GameModule.getGameModule().warn("This game was saved with board " + b.getName() + " v" + reqver + ". You are using v" + b.getVersion()); } } while (bd.indexOf("OVR") >= 0) { bd = bd.substring(bd.indexOf("OVR") + 4); try { b.addOverlay(new Overlay(bd, b, new File(getBoardDir(), "overlays"))); } catch (IOException e) { e.printStackTrace(); } } if (bd.indexOf("SSR") >= 0) { b.setTerrain(bd.substring(bd.indexOf("SSR") + 4)); } if (bd.indexOf("ZOOM") >= 0) { b.setMagnification(Double.parseDouble(bd.substring(bd.indexOf("ZOOM") + 5))); } } protected void addColumn() { slotPanel.setLayout(new GridLayout(ny, ++nx)); for (int j = 0; j < ny; ++j) { slotPanel.add(new ASLBoardSlot(this), (j + 1) * nx - 1); } slotPanel.revalidate(); } protected void addRow() { slotPanel.setLayout(new GridLayout(++ny, nx)); for (int i = 0; i < nx; ++i) { slotPanel.add(new ASLBoardSlot(this), -1); } slotPanel.revalidate(); } public void reset() { super.reset(); removeAllBoards(); slotPanel.add(new ASLBoardSlot(this), 0); terrain.reset(); if (setupControls == null) { setupControls = new SetupControls(); } setupControls.reset(); } public void actionPerformed(ActionEvent e) { String label = e.getActionCommand(); if ("Add overlays".equals(label)) { Overlayer o; Dialog d = getDialogContainer(); o = d == null ? new Overlayer(getFrameContainer()) : new Overlayer(d); o.setLocationRelativeTo(controls.getTopLevelAncestor()); o.setVisible(true); } else if ("Crop boards".equals(label)) { Cropper crop; Dialog d = getDialogContainer(); crop = d == null ? new Cropper(getFrameContainer()) : new Cropper(d); crop.setLocationRelativeTo(controls.getTopLevelAncestor()); crop.setVisible(true); } else if ("Terrain SSR".equals(label)) { currentBoards = getBoardsFromControls(); terrain.setup(currentBoards); } else { super.actionPerformed(e); } } protected Dialog getDialogContainer() { Container top = controls.getTopLevelAncestor(); return (Dialog) (top instanceof Dialog ? top : null); } protected Frame getFrameContainer() { Container top = controls.getTopLevelAncestor(); return (Frame) (top instanceof Frame ? top : null); } public BoardSlot match(String s) throws BoardException { if ("".equals(s)) { if (slotPanel.getComponentCount() == 1) return (BoardSlot) slotPanel.getComponent(0); else throw new BoardException("No Such Board"); } for (int i = 0; i < nx; ++i) for (int j = 0; j < ny; ++j) { BoardSlot b = getSlot(i + nx * j); if (b.getBoard() == null) continue; if (b.getBoard().getName().equalsIgnoreCase(s)) return (b); } throw new BoardException("No Such Board"); } public org.w3c.dom.Element getBuildElement(org.w3c.dom.Document doc) { org.w3c.dom.Element el = doc.createElement(getClass().getName()); return el; } public Component getControls() { reset(); return setupControls; } private class SetupControls extends JPanel { private DirectoryConfigurer dirConfig; public SetupControls() { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); final DirectoryConfigurer pref = (DirectoryConfigurer) GameModule.getGameModule().getPrefs() .getOption(BOARD_DIR); dirConfig = new DirectoryConfigurer(null, pref.getName()); dirConfig.setValue(pref.getFileValue()); add(dirConfig.getControls()); JCheckBox deluxe = new JCheckBox("Deluxe-size hexes"); deluxe.setAlignmentX(0.0F); deluxe.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { enableDeluxe = e.getStateChange() == ItemEvent.SELECTED; int n = 0; ASLBoardSlot slot; while ((slot = (ASLBoardSlot) getSlot(n++)) != null) { if (slot.getBoard() != null) { slot.getBoard().setMagnification(enableDeluxe ? 3.0 : 1.0); slot.setSize(slot.getPreferredSize()); slot.revalidate(); slot.repaint(); } } } }); add(deluxe); add(controls); dirConfig.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { pref.setFrozen(true); pref.setValue(evt.getNewValue()); pref.setFrozen(false); setBoardDir((File) evt.getNewValue()); } }); pref.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { pref.setFrozen(true); dirConfig.setValue(evt.getNewValue()); pref.setFrozen(false); } }); } public void reset() { } } protected class Cropper extends JDialog implements ActionListener { private JTextField row1, row2, coord1, coord2, bdName; private JRadioButton halfrow, fullrow; protected Cropper(Frame owner) { super(owner, true); init(); } protected Cropper(Dialog owner) { super(owner, true); init(); } private void init() { row1 = new JTextField(2); row1.addActionListener(this); row2 = new JTextField(2); row2.addActionListener(this); coord1 = new JTextField(2); coord1.addActionListener(this); coord2 = new JTextField(2); coord2.addActionListener(this); bdName = new JTextField(2); bdName.addActionListener(this); halfrow = new JRadioButton("Crop to middle of hex row"); fullrow = new JRadioButton("Crop to nearest full hex row"); fullrow.setSelected(true); ButtonGroup bg = new ButtonGroup(); bg.add(halfrow); bg.add(fullrow); getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); Box box = Box.createHorizontalBox(); JLabel l = new JLabel("Board:"); box.add(l); box.add(bdName); getContentPane().add(box); box = Box.createHorizontalBox(); box.add(new JLabel("Hexrows:")); box.add(row1); box.add(new JLabel("-")); box.add(row2); getContentPane().add(box); box = Box.createHorizontalBox(); box.add(new JLabel("Coords:")); box.add(coord1); box.add(new JLabel("-")); box.add(coord2); getContentPane().add(box); box = Box.createVerticalBox(); box.add(halfrow); box.add(fullrow); getContentPane().add(box); box = Box.createHorizontalBox(); JButton b = new JButton("Crop"); b.addActionListener(this); box.add(b); b = new JButton("Done"); b.addActionListener(this); box.add(b); getContentPane().add(box); pack(); setLocation(Toolkit.getDefaultToolkit().getScreenSize().width / 2 - getWidth() / 2, Toolkit.getDefaultToolkit().getScreenSize().height / 2 - getHeight() / 2); } public void clear() { row1.setText(""); row2.setText(""); coord1.setText(""); coord2.setText(""); bdName.setText(""); } public void actionPerformed(ActionEvent e) { if ("Done".equals(e.getActionCommand())) { setVisible(false); return; } try { BoardSlot b = match(bdName.getText()); ((ASLBoard) b.getBoard()).crop(row1.getText().toLowerCase().trim(), row2.getText().toLowerCase().trim(), coord1.getText().toLowerCase().trim(), coord2.getText().toLowerCase().trim(), fullrow.isSelected()); b.invalidate(); b.repaint(); bdName.setText(""); row1.setText(""); row2.setText(""); coord1.setText(""); coord2.setText(""); } catch (Exception ex) { } } } protected class Overlayer extends JDialog implements ActionListener { private JTextField hex1, hex2, ovrName, bdName; private JLabel status; protected Overlayer(Frame f) { super(f, true); setTitle("Overlays"); init(); } protected Overlayer(Dialog d) { super(d, true); setTitle("Overlays"); init(); } private void init() { hex1 = new JTextField(2); hex1.addActionListener(this); hex2 = new JTextField(2); hex2.addActionListener(this); ovrName = new JTextField(2); ovrName.addActionListener(this); bdName = new JTextField(2); bdName.addActionListener(this); status = new JLabel("Enter blank hexes to delete.", JLabel.CENTER); getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); getContentPane().add(status); Box box = Box.createHorizontalBox(); Box vBox = Box.createVerticalBox(); vBox.add(new JLabel("Overlay")); vBox.add(ovrName); box.add(vBox); vBox = Box.createVerticalBox(); vBox.add(new JLabel("Board")); vBox.add(bdName); box.add(vBox); vBox = Box.createVerticalBox(); vBox.add(new JLabel("Hex 1")); vBox.add(hex1); box.add(vBox); vBox = Box.createVerticalBox(); vBox.add(new JLabel("Hex 2")); vBox.add(hex2); box.add(vBox); getContentPane().add(box); box = Box.createHorizontalBox(); JButton b = new JButton("Add"); b.addActionListener(this); box.add(b); b = new JButton("Done"); b.addActionListener(this); box.add(b); getContentPane().add(box); pack(); setLocation(Toolkit.getDefaultToolkit().getScreenSize().width / 2 - getSize().width / 2, Toolkit.getDefaultToolkit().getScreenSize().height / 2 - getSize().height / 2); } public void clear() { status.setText("Enter blank hexes to delete."); hex1.setText(""); hex2.setText(""); ovrName.setText(""); bdName.setText(""); } public void actionPerformed(ActionEvent e) { if ("Done".equals(e.getActionCommand())) { setVisible(false); return; } try { status.setText( ((ASLBoardSlot) (match(bdName.getText()))).addOverlay(ovrName.getText().toLowerCase(), hex1.getText().toLowerCase(), hex2.getText().toLowerCase())); bdName.setText(""); ovrName.setText(""); hex1.setText(""); hex2.setText(""); if (false) throw new BoardException("Now that's weird"); } catch (BoardException excep) { status.setText(excep.getMessage()); } } } protected class TerrainEditor extends JDialog implements ActionListener { private Vector optionGroup = new Vector(); private JList optionList; private JTextField status; private JPanel options; private CardLayout card; private Vector basicOptions = new Vector(); private JTextField board; private TerrainMediator mediator = new TerrainMediator(); private Vector boards; protected JButton apply, reset, done; protected TerrainEditor() { super((Frame) null, true); setTitle("Terrain Transformations"); boards = new Vector(); getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); status = new JTextField("Leave board number blank to apply to all boards: "); status.setMaximumSize(new Dimension(status.getMaximumSize().width, status.getPreferredSize().height)); status.setEditable(false); card = new CardLayout(); options = new JPanel(); options.setLayout(card); optionList = new JList(new DefaultListModel()); optionList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); optionList.setVisibleRowCount(4); optionList.addListSelectionListener(new javax.swing.event.ListSelectionListener() { public void valueChanged(javax.swing.event.ListSelectionEvent e) { showOption(); } }); getContentPane().add(status); Box box = Box.createHorizontalBox(); JPanel p = new JPanel(); p.setLayout(new GridLayout(4, 1)); JPanel pp = new JPanel(); pp.setLayout(new GridLayout(1, 2)); pp.add(new JLabel("Board ")); board = new JTextField(2); board.setMaximumSize(new Dimension(board.getMaximumSize().width, board.getPreferredSize().height)); pp.add(board); p.add(pp); apply = new JButton("Apply"); apply.addActionListener(this); reset = new JButton("Reset"); reset.addActionListener(this); done = new JButton("Done"); done.addActionListener(this); p.add(apply); p.add(reset); p.add(done); box.add(p); box.add(new JScrollPane(optionList)); box.add(options); getContentPane().add(box, -1); pack(); } private void showOption() { card.show(options, (String) optionList.getSelectedValue()); } public ASLBoardPicker getBoardPicker() { return ASLBoardPicker.this; } public void setup(Collection<Board> boardList) { Box box = Box.createVerticalBox(); for (int i = 0; i < 4; ++i) { TransformOption opt = new TransformOption(); box.add(opt.getComponent()); optionGroup.addElement(opt); } addOption("Transformations", box); boards.removeAllElements(); String version = ""; int nboards = 0; for (Board board : boardList) { ASLBoard b = (ASLBoard) board; boards.addElement(b); if (b != null) { if (b.getBoardArchive() != null) { InputStream in = null; try { in = b.getBoardArchive().getInputStream("SSRControls"); readOptions(in); } catch (IOException ignore) { } finally { IOUtils.closeQuietly(in); } } for (Enumeration oEnum = b.getOverlays(); oEnum.hasMoreElements();) { Overlay o = (Overlay) oEnum.nextElement(); if (!(o instanceof SSROverlay)) { InputStream in = null; try { in = o.getDataArchive().getInputStream("SSRControls"); readOptions(in); } catch (IOException ignore) { } finally { IOUtils.closeQuietly(in); } } } nboards++; version = version.concat(b.getName() + " (ver " + b.version + ") "); } } switch (nboards) { case 0: warn("No boards loaded"); break; case 1: warn("Loaded board " + version); break; default: warn("Loaded boards " + version + " (leave board number blank to apply to all)"); } pack(); setVisible(true); } public void warn(String s) { status.setText(s); Container c = this; while (c.getParent() != null) c = c.getParent(); c.invalidate(); c.validate(); c.repaint(); } public void readOptions(InputStream in) { if (in != null) { try { Document doc = Builder.createDocument(in); NodeList n = doc.getElementsByTagName("Basic"); if (n.getLength() > 0) { n = (n.item(0)).getChildNodes(); Box basicPanel = Box.createHorizontalBox(); for (int j = 0; j < n.getLength(); ++j) { if (n.item(j).getNodeType() == Node.ELEMENT_NODE) { Element el2 = (Element) n.item(j); TerrainOption opt = new TerrainOption(mediator, el2); ((Container) opt.getComponent()) .setLayout(new BoxLayout((Container) opt.getComponent(), BoxLayout.Y_AXIS)); basicPanel.add(opt.getComponent()); basicOptions.addElement(opt); } } getContentPane().add(basicPanel, 0); } n = doc.getElementsByTagName("Option"); for (int i = 0; i < n.getLength(); ++i) { Element el = (Element) n.item(i); Box box = Box.createVerticalBox(); NodeList nl = el.getChildNodes(); for (int j = 0; j < nl.getLength(); ++j) { if (nl.item(j).getNodeType() == Node.ELEMENT_NODE) { Element el2 = (Element) nl.item(j); TerrainOption opt = new TerrainOption(mediator, el2); box.add(opt.getComponent()); optionGroup.addElement(opt); } } addOption(el.getAttribute("name"), box); } } catch (Exception e) { e.printStackTrace(); } } } private void addOption(String name, Component c) { for (int i = 0; i < optionList.getModel().getSize(); ++i) { if (optionList.getModel().getElementAt(i).equals(name)) return; } ((DefaultListModel) optionList.getModel()).addElement(name); options.add(c, name); card.addLayoutComponent(c, name); } private void reset(Vector opts) { for (int i = 0; i < opts.size(); ++i) { ((TerrainOption) opts.elementAt(i)).reset(); } } public void reset() { board.setText(""); reset(basicOptions); reset(optionGroup); } public void actionPerformed(ActionEvent e) { if (e.getSource() instanceof JButton) { if ("Apply".equals(e.getActionCommand())) { warn("Working ..."); String opText = optionText(); String bText = basicText(); try { String boardName = board.getText().trim(); int n = 0; ASLBoardSlot slot; while ((slot = (ASLBoardSlot) getSlot(n++)) != null) { if (boardName.length() == 0 || match(boardName) == slot) { slot.setTerrain(slot.getTerrain() + '\t' + optionRules()); if (slot.getBoard() == null) continue; ((ASLBoard) slot.getBoard()).setTerrain(basicRules() + slot.getTerrain()); slot.repaint(); } } if (opText.length() > 0) { bText = bText.length() == 0 ? opText : bText + ", " + opText; } warn((boardName.length() == 0 ? "All boards" : "Board " + board.getText()) + ": " + bText); } catch (BoardException e1) { e1.printStackTrace(); warn(e1.getMessage()); } reset(optionGroup); } else if ("Reset".equals(e.getActionCommand())) { reset(); try { int n = 0; while (true) { ASLBoardSlot slot = null; try { slot = (ASLBoardSlot) getSlot(n++); } catch (Exception noSuchSlot) { break; } slot.setTerrain(""); try { ((ASLBoard) slot.getBoard()).setTerrain(""); } catch (Exception e2) { } slot.repaint(); } warn("Back to normal"); } catch (Exception resetError) { warn(resetError.getMessage()); } } else if ("Done".equals(e.getActionCommand())) { reset(); setVisible(false); } } } public String basicRules() { String s = ""; for (int i = 0; i < basicOptions.size(); ++i) { s = s.concat(((TerrainOption) basicOptions.elementAt(i)).getRule()); } return s; } public String basicText() { String s = ""; for (int i = 0; i < basicOptions.size(); ++i) { s = s.concat(((TerrainOption) basicOptions.elementAt(i)).getText()); } return s; } public String optionRules() { String s = ""; for (int i = 0; i < optionGroup.size(); ++i) { s = s.concat(((TerrainOption) optionGroup.elementAt(i)).getRule()); } return s; } public String optionText() { String s = ""; for (int i = 0; i < optionGroup.size(); ++i) { s = s.concat(((TerrainOption) optionGroup.elementAt(i)).getText()); } return s.endsWith(", ") ? s.substring(0, s.length() - 2) : s; } protected class TerrainOption { protected Component comp; protected JPanel panel = new JPanel(); private Vector active; private String text; private String rule; private String name; protected Hashtable rules = new Hashtable(); protected Hashtable texts = new Hashtable(); protected Vector defaults = new Vector(); protected PropertyChangeSupport propChange = new PropertyChangeSupport(this); protected TerrainOption() { } public TerrainOption(TerrainMediator mediator, Element e) { panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); name = e.getAttribute("name"); if (e.getElementsByTagName("Source").getLength() > 0) mediator.addSource(this); if (e.getTagName().equals("Menu")) { comp = new JComboBox(); ((JComboBox) comp).addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent evt) { invalidate(); propChange.firePropertyChange("active", null, getActive()); } }); comp.setMaximumSize(new Dimension(comp.getMaximumSize().width, comp.getPreferredSize().height)); } else if (e.getTagName().equals("ScrollList")) { comp = new JList(new DefaultListModel()); ((JList) comp).addListSelectionListener(new ListSelectionListener() { public void valueChanged(javax.swing.event.ListSelectionEvent evt) { invalidate(); propChange.firePropertyChange("active", null, getActive()); } }); } else if (e.getTagName().equals("Checkbox")) { comp = new JCheckBox(); ((JCheckBox) comp).addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent evt) { invalidate(); propChange.firePropertyChange("active", null, getActive()); } }); } else { throw new RuntimeException("Unrecognized SSR component type " + e.getTagName()); } NodeList n = e.getElementsByTagName("entry"); for (int i = 0; i < n.getLength(); ++i) { Element entry = (Element) n.item(i); String entryName = entry.getAttribute("name"); rules.put(entryName, entry.getAttribute("rule").replace(',', '\t')); texts.put(entryName, entry.getAttribute("text")); if (comp instanceof JCheckBox) { ((JCheckBox) comp).setText(entryName); } else if (comp instanceof JList) { ((DefaultListModel) ((JList) comp).getModel()).addElement(entryName); } else if (comp instanceof JComboBox) { ((DefaultComboBoxModel) ((JComboBox) comp).getModel()).addElement(entryName); if (entry.getAttribute("default").length() > 0) { defaults.addElement(entryName); } } NodeList targList = entry.getElementsByTagName("Target"); for (int targIndex = 0; targIndex < targList.getLength(); ++targIndex) { Vector activate = null; Vector deactivate = null; String sourceName = null; Element targ = (Element) targList.item(targIndex); sourceName = targ.getAttribute("sourceName"); NodeList nl = targ.getElementsByTagName("activate"); if (nl.getLength() > 0) { activate = new Vector(); for (int j = 0; j < nl.getLength(); ++j) { activate.addElement(((Element) nl.item(j)).getAttribute("sourceProperty")); } } nl = targ.getElementsByTagName("deactivate"); if (nl.getLength() > 0) { deactivate = new Vector(); for (int j = 0; j < nl.getLength(); ++j) { deactivate.addElement(((Element) nl.item(j)).getAttribute("sourceProperty")); } } if (activate != null || deactivate != null) { mediator.addTarget(this, entryName, sourceName, activate, deactivate); } } } if (!(comp instanceof JCheckBox) || !getName().equals(((JCheckBox) comp).getText())) { panel.add(new JLabel(getName())); } if (comp instanceof JList) { panel.add(new JScrollPane(comp)); } else { panel.add(comp); } panel.setVisible(e.getElementsByTagName("invisible").getLength() == 0); } public Component getComponent() { return panel; } public void reset() { for (Enumeration e = getActive().elements(); e.hasMoreElements();) { activate((String) e.nextElement(), false); } for (Enumeration e = defaults.elements(); e.hasMoreElements();) { activate((String) e.nextElement(), true); } } public void activate(String val, boolean isActive) { invalidate(); if (comp instanceof JCheckBox) { ((JCheckBox) comp).setSelected(isActive && ((JCheckBox) comp).getText().equals(val)); } else if (comp instanceof JComboBox) { if (val == null || !isActive) { if (defaults.size() > 0) { ((JComboBox) comp).setSelectedItem(defaults.elementAt(0)); } else { ((JComboBox) comp).setSelectedIndex(0); } } else { ((JComboBox) comp).setSelectedItem(val); } } else if (comp instanceof JList) { ListModel model = ((JList) comp).getModel(); for (int j = 0; j < model.getSize(); ++j) { if (model.getElementAt(j).equals(val)) { if (isActive) { ((JList) comp).addSelectionInterval(j, j); } else { ((JList) comp).removeSelectionInterval(j, j); } break; } } } propChange.firePropertyChange("active", null, getActive()); } public String getName() { return name; } private void invalidate() { active = null; rule = null; text = null; } public Vector getActive() { if (active == null) { active = new Vector(); if (comp instanceof JCheckBox) { active.addElement(((JCheckBox) comp).isSelected() ? ((JCheckBox) comp).getText() : ""); } else if (comp instanceof JComboBox) { active.addElement(((JComboBox) comp).getSelectedItem()); } else if (comp instanceof JList) { Object val[] = ((JList) comp).getSelectedValues(); for (int i = 0; i < val.length; ++i) { active.addElement(val[i]); } } } return active; } public String getRule() { if (rule == null) { rule = ""; for (Enumeration e = getActive().elements(); e.hasMoreElements();) { String s = (String) rules.get(e.nextElement()); if (s != null && s.length() > 0) rule = rule.concat(s + '\t'); } } return rule; } public String getText() { if (text == null) { text = ""; for (Enumeration e = getActive().elements(); e.hasMoreElements();) { String s = (String) texts.get(e.nextElement()); if (s != null && s.length() > 0) text = text.concat(s + ", "); } } return text; } public void addPropertyChangeListener(PropertyChangeListener l) { propChange.addPropertyChangeListener(l); } } protected abstract class TerrainOptions extends JPanel { protected Vector choices = new Vector(); protected Vector checkboxes = new Vector(); protected Vector lists = new Vector(); protected Hashtable translations; protected Hashtable plain; TerrainOptions() { translations = new Hashtable(); plain = new Hashtable(); translations.put("Normal", ""); plain.put("Normal", ""); setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); } void addComponent(String name, Component c) { Box box = Box.createHorizontalBox(); box.add(new JLabel(name)); box.add(c); add(box); if (c instanceof JCheckBox) { checkboxes.addElement(c); } else if (c instanceof JComboBox) { choices.addElement(c); } else if (c instanceof JList) { lists.addElement(c); } } void addToChoice(JComboBox c, String text, String translation, String plainName) { ((DefaultComboBoxModel) c.getModel()).addElement(text); setTranslation(text, translation, plainName); } void setTranslation(String text, String translation, String plainName) { if (translation.length() > 0) { StringTokenizer st = new StringTokenizer(translation, ","); while (st.hasMoreTokens()) { translations.put(text, st.nextToken() + "\t"); } } else translations.put(text, ""); if (plainName.length() > 0) plain.put(text, plainName + ", "); else plain.put(text, ""); } public void reset() { JCheckBox cb; JComboBox c; JList l; for (int i = 0; i < checkboxes.size(); ++i) { cb = (JCheckBox) checkboxes.elementAt(i); cb.setSelected(false); } for (int i = 0; i < choices.size(); ++i) { c = (JComboBox) choices.elementAt(i); c.setSelectedIndex(0); } for (int i = 0; i < lists.size(); ++i) { l = (JList) lists.elementAt(i); l.setSelectedIndex(-1); } } public String toString() { String s = ""; JCheckBox cb; JComboBox c; JList l; for (int i = 0; i < checkboxes.size(); ++i) { cb = (JCheckBox) checkboxes.elementAt(i); if (cb.isSelected()) s = s.concat((String) translations.get(cb.getText())); } for (int i = 0; i < choices.size(); ++i) { c = (JComboBox) choices.elementAt(i); s = s.concat((String) translations.get(c.getSelectedItem())); } for (int i = 0; i < lists.size(); ++i) { l = (JList) lists.elementAt(i); for (int n = 0; n < l.getModel().getSize(); ++n) { if (l.isSelectedIndex(n)) { s = s.concat((String) translations.get(l.getModel().getElementAt(n))); } } } return s; } public String plainText() { String s = ""; JCheckBox cb; JComboBox c; JList l; for (int i = 0; i < checkboxes.size(); ++i) { cb = (JCheckBox) checkboxes.elementAt(i); if (cb.isSelected()) s = s.concat((String) plain.get(cb.getText())); } for (int i = 0; i < choices.size(); ++i) { c = (JComboBox) choices.elementAt(i); s = s.concat((String) plain.get(c.getSelectedItem())); } for (int i = 0; i < lists.size(); ++i) { l = (JList) lists.elementAt(i); for (int n = 0; n < l.getModel().getSize(); ++n) { if (l.isSelectedIndex(n)) { s = s.concat((String) plain.get(l.getModel().getElementAt(n))); } } } return s; } } protected class TerrainMediator implements PropertyChangeListener { private Hashtable targets = new Hashtable(); private Hashtable sources = new Hashtable(); TerrainMediator() { } public void addSource(TerrainOption opt) { opt.addPropertyChangeListener(this); sources.put(opt.getName(), opt); } public void addTarget(TerrainOption targ, String targetProp, String sourceName, Vector activate, Vector deactivate) { getTargets(sourceName).addElement(new Target(targ, targetProp, activate, deactivate)); } public void propertyChange(PropertyChangeEvent evt) { Vector v = getTargets(((TerrainOption) evt.getSource()).getName()); for (Enumeration e = v.elements(); e.hasMoreElements();) { ((Target) e.nextElement()).sourceStateChanged((Vector) evt.getNewValue()); } } public void itemStateChanged(ItemEvent e) { sourceStateChanged((String) sources.get(e.getSource()), getSourceSelection((Component) e.getSource())); } public void valueChanged(javax.swing.event.ListSelectionEvent e) { sourceStateChanged((String) sources.get(e.getSource()), getSourceSelection((Component) e.getSource())); } private Vector getTargets(String srcName) { Vector v = (Vector) targets.get(srcName); if (v == null) { v = new Vector(); targets.put(srcName, v); } return v; } private Vector getSourceSelection(Component source) { Vector v = new Vector(); if (source instanceof JCheckBox) { if (((JCheckBox) source).isSelected()) { v.addElement(((JCheckBox) source).getText()); } } else if (source instanceof JComboBox) { Object o = ((JComboBox) source).getSelectedItem(); if (o != null) v.addElement(o); } else if (source instanceof JList) { Object o[] = ((JList) source).getSelectedValues(); for (int i = 0; i < o.length; ++i) { v.addElement(o); } } else { throw new RuntimeException("Illegal source component " + source); } return v; } private void sourceStateChanged(String srcName, Vector active) { Vector v = getTargets(srcName); if (v != null) { for (int i = 0; i < v.size(); ++i) { ((Target) v.elementAt(i)).sourceStateChanged(active); } } } } protected class Target { private TerrainOption target; private String targetProperty; private Vector activators; private Vector deactivators; Target(TerrainOption opt, String prop, Vector activate, Vector deactivate) { activators = activate; deactivators = deactivate; targetProperty = prop; target = opt; } public void sourceStateChanged(Vector active) { if (activators != null) { for (int i = 0; i < activators.size(); ++i) { if (active.contains(activators.elementAt(i))) { target.activate(targetProperty, true); return; } } } if (deactivators != null) { for (int i = 0; i < deactivators.size(); ++i) { if (active.contains(deactivators.elementAt(i))) { target.activate(targetProperty, false); return; } } } } public String toString() { return target.getName() + "[" + targetProperty + "] " + activators; } } protected class TransformOption extends TerrainOption { private JComboBox from; private JComboBox to = new JComboBox(); TransformOption() { from = new JComboBox(); DefaultComboBoxModel model = (DefaultComboBoxModel) from.getModel(); model.addElement("-"); model.addElement("Woods"); model.addElement("Brush"); model.addElement("Grain"); model.addElement("Marsh"); model.addElement("Level -1"); model.addElement("Level 1"); model.addElement("Level 2"); model.addElement("Level 3"); model.addElement("Level 4"); from.setMaximumSize(new Dimension(from.getMaximumSize().width, from.getPreferredSize().height)); to = new JComboBox(); model = (DefaultComboBoxModel) to.getModel(); model.addElement("-"); model.addElement("Woods"); model.addElement("Brush"); model.addElement("Grain"); model.addElement("Marsh"); model.addElement("Level -1"); model.addElement("Level 0"); model.addElement("Level 1"); to.setMaximumSize(new Dimension(to.getMaximumSize().width, to.getPreferredSize().height)); panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); panel.add(new JLabel("All")); panel.add(from); panel.add(new JLabel("is")); panel.add(to); } public String getRule() { String s = ""; if (!from.getSelectedItem().equals("-") && !to.getSelectedItem().equals("-")) { String fromRule = ""; for (StringTokenizer st = new StringTokenizer((String) from.getSelectedItem()); st .hasMoreTokens();) { fromRule += st.nextToken(); } fromRule = fromRule.replace('-', '_'); String toRule = ""; for (StringTokenizer st = new StringTokenizer((String) to.getSelectedItem()); st .hasMoreTokens();) { toRule += st.nextToken(); } toRule = toRule.replace('-', '_'); s = s.concat(fromRule); s = s.concat("To"); s = s.concat(toRule); } return s; } public void reset() { from.setSelectedIndex(0); to.setSelectedIndex(0); } public String getText() { String s = ""; if (!from.getSelectedItem().equals("-") && !to.getSelectedItem().equals("-")) { s = "all " + ((String) from.getSelectedItem()).toLowerCase() + " is " + ((String) to.getSelectedItem()).toLowerCase(); } return s; } } } }