/*
* $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.
*/
/*
* Created by IntelliJ IDEA.
* User: rkinney
* Date: Sep 25, 2002
* Time: 10:43:11 PM
* To change template for new class use
* Code Style | Class Templates options (Tools | IDE Options).
*/
package VASSAL.build.module.map;
import java.awt.Component;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import javax.swing.Box;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import VASSAL.build.AbstractConfigurable;
import VASSAL.build.AutoConfigurable;
import VASSAL.build.Buildable;
import VASSAL.build.GameModule;
import VASSAL.build.module.Map;
import VASSAL.build.module.documentation.HelpFile;
import VASSAL.build.module.gamepieceimage.StringEnumConfigurer;
import VASSAL.build.module.properties.PropertySource;
import VASSAL.configure.Configurer;
import VASSAL.configure.ConfigurerFactory;
import VASSAL.configure.IconConfigurer;
import VASSAL.configure.IntConfigurer;
import VASSAL.configure.NamedHotKeyConfigurer;
import VASSAL.configure.PlayerIdFormattedStringConfigurer;
import VASSAL.configure.PropertyExpression;
import VASSAL.configure.StringArrayConfigurer;
import VASSAL.configure.StringEnum;
import VASSAL.counters.BooleanAndPieceFilter;
import VASSAL.counters.Decorator;
import VASSAL.counters.Embellishment;
import VASSAL.counters.GamePiece;
import VASSAL.counters.GlobalCommand;
import VASSAL.counters.PieceFilter;
import VASSAL.i18n.Resources;
import VASSAL.i18n.TranslatableConfigurerFactory;
import VASSAL.tools.FormattedString;
import VASSAL.tools.LaunchButton;
import VASSAL.tools.NamedKeyStroke;
import VASSAL.tools.RecursionLimiter;
import VASSAL.tools.ToolBarComponent;
/**
* Adds a button to a map window toolbar. Hitting the button applies a particular key command to all pieces on that map
* with a given name.
*/
public class MassKeyCommand extends AbstractConfigurable
implements RecursionLimiter.Loopable {
public static final String DEPRECATED_NAME = "text";
public static final String NAME = "name";
public static final String ICON = "icon";
public static final String TOOLTIP = "tooltip";
public static final String BUTTON_TEXT = "buttonText";
public static final String HOTKEY = "buttonHotkey";
public static final String KEY_COMMAND = "hotkey";
public static final String AFFECTED_PIECE_NAMES = "names";
public static final String PROPERTIES_FILTER = "filter";
public static final String REPORT_SINGLE = "reportSingle";
public static final String REPORT_FORMAT = "reportFormat";
public static final String CONDITION = "condition";
public static final String DECK_COUNT = "deckCount";
private static final String IF_ACTIVE = "If layer is active";
private static final String IF_INACTIVE = "If layer is inactive";
private static final String ALWAYS = "Always";
public static final String CHECK_PROPERTY = "property";
public static final String CHECK_VALUE = "propValue";
public static final String SINGLE_MAP = "singleMap";
protected LaunchButton launch;
protected NamedKeyStroke stroke = new NamedKeyStroke();
protected String[] names = new String[0];
protected String condition;
protected String checkProperty;
protected String checkValue;
protected PropertyExpression propertiesFilter = new PropertyExpression();
protected PropertySource propertySource;
protected PieceFilter filter;
protected Map map;
protected GlobalCommand globalCommand = new GlobalCommand(this);
protected FormattedString reportFormat = new FormattedString();
protected boolean singleMap = true;
public MassKeyCommand() {
ActionListener al = new ActionListener() {
public void actionPerformed(ActionEvent e) {
apply();
}
};
launch = new LaunchButton("CTRL", TOOLTIP, BUTTON_TEXT, HOTKEY, ICON, al);
}
public void addTo(Buildable parent) {
if (parent instanceof Map) {
map = (Map) parent;
}
if (parent instanceof ToolBarComponent) {
((ToolBarComponent)parent).getToolBar().add(launch);
}
if (parent instanceof PropertySource) {
propertySource = (PropertySource) parent;
}
setAttributeTranslatable(NAME, false);
globalCommand.setPropertySource(propertySource);
}
public void apply() {
buildFilter();
if (singleMap) {
GameModule.getGameModule().sendAndLog(globalCommand.apply(map, getFilter()));
}
else {
final List<Map> l = Map.getMapList();
GameModule.getGameModule().sendAndLog(
globalCommand.apply(l.toArray(new Map[l.size()]), getFilter()));
}
}
public void setPropertySource (PropertySource source) {
propertySource = source;
globalCommand.setPropertySource(source);
}
public Class<?>[] getAllowableConfigureComponents() {
return new Class<?>[0];
}
public String[] getAttributeDescriptions() {
if (condition == null) {
return new String[]{
Resources.getString(Resources.DESCRIPTION),
Resources.getString("Editor.MassKey.key"), //$NON-NLS-1$
Resources.getString("Editor.MassKey.match"), //$NON-NLS-1$
Resources.getString("Editor.MassKey.counters"), //$NON-NLS-1$
Resources.getString("Editor.MassKey.deck_content"), //$NON-NLS-1$
Resources.getString(Resources.BUTTON_TEXT),
Resources.getString(Resources.TOOLTIP_TEXT),
Resources.getString(Resources.BUTTON_ICON),
Resources.getString(Resources.HOTKEY_LABEL),
Resources.getString("Editor.MassKey.suppress"), //$NON-NLS-1$
Resources.getString("Editor.report_format"), //$NON-NLS-1$
};
}
else {
// Backward compatibility
return new String[]{
Resources.getString(Resources.DESCRIPTION),
Resources.getString("Editor.MassKey.key"), //$NON-NLS-1$
Resources.getString("Editor.MassKey.match"), //$NON-NLS-1$
Resources.getString("Editor.MassKey.counters"), //$NON-NLS-1$
Resources.getString("Editor.MassKey.deck_content"), //$NON-NLS-1$
Resources.getString(Resources.BUTTON_TEXT),
Resources.getString(Resources.TOOLTIP_TEXT),
Resources.getString(Resources.BUTTON_ICON),
Resources.getString(Resources.HOTKEY_LABEL),
Resources.getString("Editor.MassKey.suppress"), //$NON-NLS-1$
Resources.getString("Editor.report_format"), //$NON-NLS-1$
Resources.getString("Editor.MassKey.apply"), //$NON-NLS-1$
};
}
}
public String[] getAttributeNames() {
return new String[]{
NAME,
KEY_COMMAND,
PROPERTIES_FILTER,
SINGLE_MAP,
DECK_COUNT,
BUTTON_TEXT,
TOOLTIP,
ICON,
HOTKEY,
REPORT_SINGLE,
REPORT_FORMAT,
CONDITION,
CHECK_VALUE,
CHECK_PROPERTY,
AFFECTED_PIECE_NAMES
};
}
public static class Prompt extends StringEnum {
public String[] getValidValues(AutoConfigurable target) {
return new String[]{ALWAYS, IF_ACTIVE, IF_INACTIVE};
}
}
public Class<?>[] getAttributeTypes() {
if (condition == null) {
return new Class<?>[]{
String.class,
NamedKeyStroke.class,
PropertyExpression.class,
Boolean.class,
DeckPolicyConfig.class,
String.class,
String.class,
IconConfig.class,
NamedKeyStroke.class,
Boolean.class,
ReportFormatConfig.class
};
}
else {
// Backward compatibility
return new Class<?>[]{
String.class,
NamedKeyStroke.class,
String.class,
Boolean.class,
DeckPolicyConfig.class,
String.class,
String.class,
IconConfig.class,
NamedKeyStroke.class,
Boolean.class,
ReportFormatConfig.class,
Prompt.class
};
}
}
public static class IconConfig implements ConfigurerFactory {
public Configurer getConfigurer(AutoConfigurable c, String key, String name) {
return new IconConfigurer(key, name, "/images/keyCommand.gif");
}
}
public static class ReportFormatConfig implements TranslatableConfigurerFactory {
public Configurer getConfigurer(AutoConfigurable c, String key, String name) {
return new PlayerIdFormattedStringConfigurer(key, name, new String[0]);
}
}
public static class DeckPolicyConfig extends Configurer implements ConfigurerFactory {
protected static final String FIXED = "Fixed number of pieces";
protected static final String NONE = "No pieces";
protected static final String ALL = "All pieces";
protected IntConfigurer intConfig;
protected StringEnumConfigurer typeConfig;
protected JLabel prompt;
protected Box controls;
public DeckPolicyConfig() {
super(null, "");
typeConfig = new StringEnumConfigurer(null, "", new String[]{ALL, NONE, FIXED});
intConfig = new IntConfigurer(null, "");
controls = Box.createHorizontalBox();
prompt = new JLabel("Within a Deck, apply to: ");
controls.add(prompt);
controls.add(typeConfig.getControls());
controls.add(intConfig.getControls());
PropertyChangeListener l = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
intConfig.getControls().setVisible(FIXED.equals(typeConfig.getValueString()));
Window w = SwingUtilities.getWindowAncestor(intConfig.getControls());
if (w != null) {
w.pack();
}
}
};
PropertyChangeListener l2 = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
setValue(getIntValue());
}
};
typeConfig.addPropertyChangeListener(l);
typeConfig.addPropertyChangeListener(l2);
intConfig.addPropertyChangeListener(l2);
}
public Component getControls() {
return controls;
}
public String getValueString() {
return String.valueOf(getIntValue());
}
public int getIntValue() {
String type = typeConfig.getValueString();
if (ALL.equals(type)) {
return -1;
}
else if (NONE.equals(type)) {
return 0;
}
else {
return intConfig.getIntValue(1);
}
}
public void setValue(Object o) {
if (typeConfig != null) {
typeConfig.setFrozen(true);
intConfig.setFrozen(true);
if (o instanceof Integer) {
Integer i = (Integer) o;
switch ((i).intValue()) {
case 0:
typeConfig.setValue(NONE);
intConfig.setValue(1);
break;
case -1:
typeConfig.setValue(ALL);
intConfig.setValue(1);
break;
default:
typeConfig.setValue(FIXED);
intConfig.setValue(i);
}
intConfig.getControls().setVisible(FIXED.equals(typeConfig.getValueString()));
}
}
super.setValue(o);
if (typeConfig != null) {
typeConfig.setFrozen(false);
intConfig.setFrozen(false);
}
}
public void setValue(String s) {
if (s != null) {
setValue(Integer.valueOf(s));
}
}
public Configurer getConfigurer(AutoConfigurable c, String key, String name) {
setName(name);
this.key = key;
return this;
}
}
public String getAttributeValueString(String key) {
if (NAME.equals(key)) {
return getConfigureName();
}
else if (KEY_COMMAND.equals(key)) {
return NamedHotKeyConfigurer.encode(stroke);
}
else if (AFFECTED_PIECE_NAMES.equals(key)) {
return names == null || names.length == 0 ? null : StringArrayConfigurer.arrayToString(names);
}
else if (CHECK_PROPERTY.equals(key)) {
return propertiesFilter != null ? null : checkProperty;
}
else if (CHECK_VALUE.equals(key)) {
return propertiesFilter != null ? null : checkValue;
}
else if (PROPERTIES_FILTER.equals(key)) {
return propertiesFilter.getExpression();
}
else if (CONDITION.equals(key)) {
return ALWAYS.equals(condition) ? null : condition;
}
else if (REPORT_SINGLE.equals(key)) {
return String.valueOf(globalCommand.isReportSingle());
}
else if (DECK_COUNT.equals(key)) {
return String.valueOf(globalCommand.getSelectFromDeck());
}
else if (REPORT_FORMAT.equals(key)) {
return reportFormat.getFormat();
}
else if (SINGLE_MAP.equals(key)) {
return String.valueOf(singleMap);
}
else {
return launch.getAttributeValueString(key);
}
}
public static String getConfigureTypeName() {
return "Global Key Command";
}
protected LaunchButton getLaunchButton() {
return launch;
}
protected void setLaunchButton(LaunchButton launch) {
this.launch = launch;
}
public HelpFile getHelpFile() {
return HelpFile.getReferenceManualPage("Map.htm", "GlobalKeyCommand");
}
public void removeFrom(Buildable parent) {
if (parent instanceof ToolBarComponent) {
((ToolBarComponent)parent).getToolBar().remove(launch);
}
}
public PieceFilter getFilter() {
buildFilter();
return filter;
}
private void buildFilter() {
if (checkValue != null) {
propertiesFilter.setExpression(checkProperty + "=" + checkValue);
}
if (propertiesFilter != null) {
filter = propertiesFilter.getFilter(propertySource);
}
if (filter != null && condition != null) {
filter = new BooleanAndPieceFilter(filter, new PieceFilter() {
public boolean accept(GamePiece piece) {
boolean valid = false;
if (ALWAYS.equals(condition)) {
valid = true;
}
else if (IF_ACTIVE.equals(condition)) {
valid = Embellishment.getLayerWithMatchingActivateCommand(piece, stroke, true) != null;
}
else if (IF_INACTIVE.equals(condition)) {
valid = Embellishment.getLayerWithMatchingActivateCommand(piece, stroke, false) != null;
}
return valid;
}
});
}
}
public void setAttribute(String key, Object value) {
if (DEPRECATED_NAME.equals(key)) {
setAttribute(NAME, value);
setAttribute(BUTTON_TEXT, value);
}
else if (NAME.equals(key)) {
setConfigureName((String) value);
if (launch.getAttributeValueString(TOOLTIP) == null) {
launch.setAttribute(TOOLTIP, value);
}
}
else if (KEY_COMMAND.equals(key)) {
if (value instanceof String) {
value = NamedHotKeyConfigurer.decode((String) value);
}
stroke = (NamedKeyStroke) value;
globalCommand.setKeyStroke(stroke);
}
else if (AFFECTED_PIECE_NAMES.equals(key)) {
if (value instanceof String) {
value = StringArrayConfigurer.stringToArray((String) value);
}
names = (String[]) value;
if (names.length == 0) {
names = null;
}
else {
filter = new PieceFilter() {
public boolean accept(GamePiece piece) {
for (int j = 0; j < names.length; ++j) {
if (Decorator.getInnermost(piece).getName().equals(names[j])) {
return true;
}
}
return false;
}
};
}
}
else if (CHECK_PROPERTY.equals(key)) {
checkProperty = (String) value;
}
else if (CHECK_VALUE.equals(key)) {
checkValue = (String) value;
}
else if (PROPERTIES_FILTER.equals(key)) {
propertiesFilter.setExpression((String) value);
}
else if (CONDITION.equals(key)) {
condition = (String) value;
}
else if (REPORT_SINGLE.equals(key)) {
if (value instanceof String) {
value = Boolean.valueOf((String) value);
}
globalCommand.setReportSingle(((Boolean) value).booleanValue());
}
else if (DECK_COUNT.equals(key)) {
if (value instanceof String) {
value = Integer.valueOf((String) value);
}
globalCommand.setSelectFromDeck(((Integer) value).intValue());
}
else if (REPORT_FORMAT.equals(key)) {
reportFormat.setFormat((String) value);
globalCommand.setReportFormat((String) value);
}
else if (SINGLE_MAP.equals(key)) {
if (value instanceof String) {
value = Boolean.valueOf((String) value);
}
singleMap = (((Boolean) value).booleanValue());
}
else {
launch.setAttribute(key, value);
}
}
// Implement Loopable
public String getComponentName() {
return getConfigureName();
}
public String getComponentTypeName() {
return getConfigureTypeName();
}
}