/* * $Id$ * * Copyright (c) 2000-2013 by Brent Easton, 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 VASSAL.counters; import java.awt.Component; import java.awt.Window; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.ArrayList; import java.util.Arrays; import javax.swing.Box; import javax.swing.JLabel; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import VASSAL.build.GameModule; import VASSAL.build.module.Map; import VASSAL.build.module.documentation.HelpFile; import VASSAL.build.module.map.boardPicker.board.mapgrid.Zone; import VASSAL.build.module.properties.MutablePropertiesContainer; import VASSAL.build.module.properties.MutableProperty; import VASSAL.command.Command; import VASSAL.command.NullCommand; import VASSAL.configure.BooleanConfigurer; import VASSAL.configure.Configurer; import VASSAL.configure.FormattedExpressionConfigurer; import VASSAL.configure.IntConfigurer; import VASSAL.configure.ListConfigurer; import VASSAL.configure.PropertyNameExpressionConfigurer; import VASSAL.configure.StringConfigurer; import VASSAL.configure.StringEnumConfigurer; import VASSAL.tools.FormattedString; import VASSAL.tools.SequenceEncoder; /** * * @author Brent Easton * * A trait that allows counters to manipulate the value of Global properties. * Uses the Property manipulation functionality of DynamicPropert, but * applies them to Global Properties. */ public class SetGlobalProperty extends DynamicProperty { protected PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this); public static final String ID = "setprop;"; public static final String CURRENT_ZONE = "Current Zone/Current Map/Module"; public static final String NAMED_ZONE = "Named Zone"; public static final String NAMED_MAP = "Named Map"; protected String description; protected String propertyLevel; protected String searchName; protected Decorator dec; public SetGlobalProperty() { this(ID, null); } public SetGlobalProperty(String type, GamePiece p) { super(type, p); } public String getDescription() { String s = "Set Global Property"; if (description.length() > 0) { s += " - " + description; } return s; } public void mySetType(String s) { SequenceEncoder.Decoder sd = new SequenceEncoder.Decoder(s, ';'); sd.nextToken(); // Skip over command prefix key = sd.nextToken("name"); decodeConstraints(sd.nextToken("")); keyCommandListConfig.setValue(sd.nextToken("")); keyCommands = keyCommandListConfig.getListValue().toArray( new DynamicKeyCommand[keyCommandListConfig.getListValue().size()]); ArrayList<DynamicKeyCommand> l = new ArrayList<DynamicKeyCommand>(); for (DynamicKeyCommand dkc : keyCommands) { if (dkc.getName() != null && dkc.getName().length() > 0) { l.add(dkc); } } menuCommands = l.toArray(new DynamicKeyCommand[l.size()]); description = sd.nextToken(""); propertyLevel = sd.nextToken(CURRENT_ZONE); searchName = sd.nextToken(""); } public String myGetType() { SequenceEncoder se = new SequenceEncoder(';'); se.append(key); se.append(encodeConstraints()); se.append(keyCommandListConfig.getValueString()); se.append(description); se.append(propertyLevel); se.append(searchName); return ID + se.getValue(); } public String myGetState() { return ""; } public void mySetState(String state) { } /* * Duplicate code from Decorator for setProperty(), getProperty() Do not call super.xxxProperty() as we no longer * contain a DynamicProperty that can be manipulated, but you cannot call super.super.xxxProperty(). */ public Object getProperty(Object key) { if (Properties.KEY_COMMANDS.equals(key)) { return getKeyCommands(); } else if (Properties.INNER.equals(key)) { return piece; } else if (Properties.OUTER.equals(key)) { return dec; } else if (Properties.VISIBLE_STATE.equals(key)) { return myGetState() + piece.getProperty(key); } else { return piece.getProperty(key); } } public Object getLocalizedProperty(Object key) { if (Properties.KEY_COMMANDS.equals(key)) { return getProperty(key); } else if (Properties.INNER.equals(key)) { return getProperty(key); } else if (Properties.OUTER.equals(key)) { return getProperty(key); } else if (Properties.VISIBLE_STATE.equals(key)) { return getProperty(key); } else { return piece.getLocalizedProperty(key); } } public void setProperty(Object key, Object val) { if (Properties.INNER.equals(key)) { setInner((GamePiece) val); } else if (Properties.OUTER.equals(key)) { dec = (Decorator) val; } else { piece.setProperty(key, val); } } public HelpFile getHelpFile() { return HelpFile.getReferenceManualPage("SetGlobalProperty.htm"); } /* * Locate the correct Global Variable to adjust and update its value. The named global variables must already be * defined in the appropriate component before a counter can update them. $xxxx$ names are allowed in both the * property name and the target containing map/zone name. */ public Command myKeyEvent(KeyStroke stroke) { Command comm = new NullCommand(); for (int i = 0; i < keyCommands.length; i++) { if (keyCommands[i].matches(stroke)) { MutableProperty prop = null; String propertyName = (new FormattedString(key)).getText(Decorator.getOutermost(this)); ArrayList<MutablePropertiesContainer> propertyContainers = new ArrayList<MutablePropertiesContainer>(); propertyContainers.add(0, GameModule.getGameModule()); Map map = getMap(); if (NAMED_MAP.equals(propertyLevel)) { String mapName = (new FormattedString(searchName)).getText(Decorator.getOutermost(this)); map = Map.getMapById(mapName); } if (map != null) { propertyContainers.add(0, map); } Zone z = null; if (CURRENT_ZONE.equals(propertyLevel) && getMap() != null) { z = getMap().findZone(getPosition()); } else if (NAMED_ZONE.equals(propertyLevel) && getMap() != null) { String zoneName = (new FormattedString(searchName)).getText(Decorator.getOutermost(this)); z = getMap().findZone(zoneName); } if (z != null) { propertyContainers.add(0, z); } prop = MutableProperty.Util.findMutableProperty(propertyName, propertyContainers); /* * Debugging could be painful, so print a useful message in the * Chat Window if no property can be found to update */ if (prop == null) { String s = "Set Global Property (" + description + "): Unable to locate Global Property named " + propertyName; if (!propertyLevel.equals(CURRENT_ZONE)) { s += " in " + propertyLevel + " " + searchName; } GameModule.getGameModule().warn(s); } else { String oldValue = prop.getPropertyValue(); String newValue = keyCommands[i].propChanger.getNewValue(oldValue); format.setFormat(newValue); newValue = format.getText(Decorator.getOutermost(this)); comm = prop.setPropertyValue(newValue); } } } return comm; } public PieceEditor getEditor() { return new Ed(this); } protected static class Ed implements PieceEditor { protected StringConfigurer descConfig; protected PropertyNameExpressionConfigurer nameConfig; protected BooleanConfigurer numericConfig; protected IntConfigurer minConfig; protected IntConfigurer maxConfig; protected BooleanConfigurer wrapConfig; protected ListConfigurer keyCommandListConfig; protected StringEnumConfigurer levelConfig; protected FormattedExpressionConfigurer searchNameConfig; protected JLabel mapLabel = new JLabel("map"); protected JLabel zoneLabel = new JLabel("zone"); protected Box controls; protected Box nameBox; public Ed(final SetGlobalProperty m) { keyCommandListConfig = new ListConfigurer(null, "Key Commands") { protected Configurer buildChildConfigurer() { return new DynamicKeyCommandConfigurer(m); } }; keyCommandListConfig.setValue( new ArrayList<DynamicKeyCommand>(Arrays.asList(m.keyCommands))); PropertyChangeListener l = new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { boolean isNumeric = numericConfig.booleanValue().booleanValue(); minConfig.getControls().setVisible(isNumeric); maxConfig.getControls().setVisible(isNumeric); wrapConfig.getControls().setVisible(isNumeric); keyCommandListConfig.repack(); } }; controls = Box.createVerticalBox(); descConfig = new StringConfigurer(null, "Description: ", m.description); controls.add(descConfig.getControls()); nameConfig = new PropertyNameExpressionConfigurer(null, "Global Property Name: ", m.getKey(), (EditablePiece) m); controls.add(nameConfig.getControls()); levelConfig = new StringEnumConfigurer(null, "", new String[]{CURRENT_ZONE, NAMED_ZONE, NAMED_MAP}); levelConfig.setValue(m.propertyLevel); levelConfig.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { updateVisibility(); } }); Box box = Box.createHorizontalBox(); box.add(new JLabel("Locate Property starting in the: ")); box.add(levelConfig.getControls()); controls.add(box); nameBox = Box.createHorizontalBox(); nameBox.add(new JLabel("Name of ")); nameBox.add(mapLabel); nameBox.add(zoneLabel); nameBox.add(new JLabel(" containing property: ")); searchNameConfig = new FormattedExpressionConfigurer(null, "", m.searchName, (EditablePiece) m); nameBox.add(searchNameConfig.getControls()); controls.add(nameBox); numericConfig = new BooleanConfigurer(null, "Is numeric?", m.isNumeric()); controls.add(numericConfig.getControls()); minConfig = new IntConfigurer(null, "Minimum value: ", m.getMinimumValue()); controls.add(minConfig.getControls()); maxConfig = new IntConfigurer(null, "Maximum value: ", m.getMaximumValue()); controls.add(maxConfig.getControls()); wrapConfig = new BooleanConfigurer(null, "Wrap?", m.isWrap()); controls.add(wrapConfig.getControls()); controls.add(keyCommandListConfig.getControls()); numericConfig.addPropertyChangeListener(l); numericConfig.fireUpdate(); updateVisibility(); } protected void updateVisibility() { mapLabel.setVisible(levelConfig.getValueString().equals(NAMED_MAP)); zoneLabel.setVisible(levelConfig.getValueString().equals(NAMED_ZONE)); nameBox.setVisible(!levelConfig.getValueString().equals(CURRENT_ZONE)); Window w = SwingUtilities.getWindowAncestor(controls); if (w != null) { w.pack(); } } public Component getControls() { return controls; } protected String encodeConstraints() { return new SequenceEncoder(',').append(numericConfig.getValueString()).append(minConfig.getValueString()).append(maxConfig.getValueString()).append( wrapConfig.getValueString()).getValue(); } public String getType() { SequenceEncoder se = new SequenceEncoder(';'); se.append(nameConfig.getValueString()); se.append(encodeConstraints()); se.append(keyCommandListConfig.getValueString()); se.append(descConfig.getValueString()); se.append(levelConfig.getValueString()); se.append(searchNameConfig.getValueString()); return ID + se.getValue(); } public String getState() { return ""; } } }