/* * $Id$ * * Copyright (c) 2000-2012 by Rodney Kinney, Brent Easton * * 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.build.module; import java.awt.Container; import java.awt.dnd.DragSource; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Properties; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.apache.commons.lang.SystemUtils; import VASSAL.build.AbstractConfigurable; import VASSAL.build.AutoConfigurable; import VASSAL.build.Buildable; import VASSAL.build.Builder; import VASSAL.build.GameModule; import VASSAL.build.IllegalBuildException; import VASSAL.build.module.documentation.HelpFile; import VASSAL.build.module.map.PieceMover; import VASSAL.configure.BooleanConfigurer; import VASSAL.configure.Configurer; import VASSAL.configure.ConfigurerFactory; import VASSAL.configure.FormattedStringConfigurer; import VASSAL.configure.IntConfigurer; import VASSAL.configure.SingleChildInstance; import VASSAL.configure.StringEnum; import VASSAL.i18n.Resources; import VASSAL.preferences.BasicPreference; import VASSAL.preferences.BooleanPreference; import VASSAL.preferences.DoublePreference; import VASSAL.preferences.EnumPreference; import VASSAL.preferences.IntegerPreference; import VASSAL.preferences.Prefs; import VASSAL.preferences.StringPreference; import VASSAL.preferences.TextPreference; import VASSAL.tools.ErrorDialog; import VASSAL.tools.FormattedString; public class GlobalOptions extends AbstractConfigurable { public static final String NON_OWNER_UNMASKABLE = "nonOwnerUnmaskable"; //$NON-NLS-1$ public static final String PROMPT_STRING = "promptString"; //$NON-NLS-1$ public static final String CENTER_ON_MOVE = "centerOnMove"; //$NON-NLS-1$ public static final String MARK_MOVED = "markMoved"; //$NON-NLS-1$ public static final String AUTO_REPORT = "autoReport"; //$NON-NLS-1$ public static final String ALWAYS = "Always"; //$NON-NLS-1$ public static final String NEVER = "Never"; //$NON-NLS-1$ public static final String PROMPT = "Use Preferences Setting"; //$NON-NLS-1$ public static final String SINGLE_WINDOW = "singleWindow"; //$NON-NLS-1$ public static final String MAXIMUM_HEAP = "maximumHeap"; //$NON-NLS-1$ public static final String INITIAL_HEAP = "initialHeap"; //$NON-NLS-1$ public static final String BUG_10295 = "bug10295"; public static final String PLAYER_NAME = "PlayerName"; //$NON-NLS-1$ public static final String PLAYER_NAME_ALT = "playerName"; //$NON-NLS-1$ public static final String PLAYER_SIDE = "PlayerSide"; //$NON-NLS-1$ public static final String PLAYER_SIDE_ALT = "playerSide"; //$NON-NLS-1$ public static final String PLAYER_ID = "PlayerId"; //$NON-NLS-1$ public static final String PLAYER_ID_ALT = "playerId"; //$NON-NLS-1$ public static final String PLAYER_ID_FORMAT = "playerIdFormat"; //$NON-NLS-1$ private String promptString = "Opponents can unmask my pieces"; //$NON-NLS-1$ private String nonOwnerUnmaskable = NEVER; private String centerOnMoves = ALWAYS; private String autoReport = ALWAYS; private String markMoved = NEVER; private Map<String,Object> properties = new HashMap<String,Object>(); private static Map<String,Configurer> optionConfigurers = new LinkedHashMap<String,Configurer>(); private static Properties optionInitialValues = new Properties(); private FormattedString playerIdFormat = new FormattedString("$" + PLAYER_NAME + "$"); //$NON-NLS-1$ //$NON-NLS-2$ private static GlobalOptions instance = new GlobalOptions(); private boolean useSingleWindow; public void addTo(Buildable parent) { instance = this; final GameModule gm = GameModule.getGameModule(); final Prefs prefs = gm.getPrefs(); // should this moudule use a combined main window? final BooleanConfigurer combConf = new BooleanConfigurer( SINGLE_WINDOW, Resources.getString("GlobalOptions.use_combined"), //$NON-NLS-1$ Boolean.TRUE ); prefs.addOption(combConf); useSingleWindow = !Boolean.FALSE.equals(combConf.getValue()); // the initial heap size for this module final IntConfigurer initHeapConf = new IntConfigurer( INITIAL_HEAP, Resources.getString("GlobalOptions.initial_heap"), //$NON-NLS-1$ Integer.valueOf(256) ); prefs.addOption(initHeapConf); // the maximum heap size for this module final IntConfigurer maxHeapConf = new IntConfigurer( MAXIMUM_HEAP, Resources.getString("GlobalOptions.maximum_heap"), //$NON-NLS-1$ Integer.valueOf(512) ); prefs.addOption(maxHeapConf); // Bug 10295: Sometimes, for unknown reasons, the native drag handler // fails to draw images properly on Windows. This lets the user select // the drag handler to use. if (SystemUtils.IS_OS_WINDOWS) { final BooleanConfigurer bug10295Conf = new BooleanConfigurer( BUG_10295, Resources.getString("GlobalOptions.bug10295"), Boolean.FALSE ); if (Boolean.TRUE.equals(bug10295Conf.getValue()) && !(PieceMover.AbstractDragHandler.getTheDragHandler() instanceof PieceMover.DragHandlerNoImage)) { PieceMover.AbstractDragHandler.setTheDragHandler( new PieceMover.DragHandlerNoImage() ); } bug10295Conf.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent e) { PieceMover.AbstractDragHandler.setTheDragHandler( (Boolean.TRUE.equals(e.getNewValue()) || !DragSource.isDragImageSupported()) ? new PieceMover.DragHandlerNoImage() : new PieceMover.DragHandler() ); } }); prefs.addOption(bug10295Conf); } validator = new SingleChildInstance(gm, getClass()); } public static GlobalOptions getInstance() { return instance; } public boolean isUseSingleWindow() { return useSingleWindow; } @Deprecated public boolean isAveragedScaling() { return true; } public static String getConfigureTypeName() { return Resources.getString("Editor.GlobalOption.component_type"); //$NON-NLS-1$ } public static class Prompt extends StringEnum { public String[] getValidValues(AutoConfigurable target) { return new String[]{ALWAYS, NEVER, PROMPT}; } } public static class PlayerIdFormatConfig implements ConfigurerFactory { public Configurer getConfigurer(AutoConfigurable c, String key, String name) { return new FormattedStringConfigurer(key, name, new String[]{PLAYER_NAME, PLAYER_SIDE}); } } public Class<?>[] getAllowableConfigureComponents() { return new Class<?>[]{ StringPreference.class, TextPreference.class, EnumPreference.class, IntegerPreference.class, DoublePreference.class, BooleanPreference.class }; } public String[] getAttributeDescriptions() { return new String[]{ Resources.getString("Editor.GlobalOption.nonowner_unmask"), //$NON-NLS-1$ null, Resources.getString("Editor.GlobalOption.center_moves"), //$NON-NLS-1$ Resources.getString("Editor.GlobalOption.autoreport_moves"), //$NON-NLS-1$ Resources.getString("Editor.GlobalOption.playerid_format") //$NON-NLS-1$ }; } public String[] getAttributeNames() { final ArrayList<String> attributes = new ArrayList<String>( Arrays.asList( NON_OWNER_UNMASKABLE, PROMPT_STRING, CENTER_ON_MOVE, AUTO_REPORT, PLAYER_ID_FORMAT ) ); for (String key : properties.keySet()) { attributes.add(key); } return attributes.toArray(new String[attributes.size()]); } public Class<?>[] getAttributeTypes() { return new Class<?>[]{ Prompt.class, null, Prompt.class, Prompt.class, PlayerIdFormatConfig.class }; } /** * Components may use GlobalOptions to store generic global attributes. * This method registers the given key as an attribute of the GlobalOptions * with the given type. */ public void addOption(Configurer option) { optionConfigurers.put(option.getKey(), option); Object initValue = optionInitialValues.get(option.getKey()); if (initValue instanceof String) { option.setValue((String)initValue); } if (config != null) { ((Container)config.getControls()).add(option.getControls()); } } public void build(Element e) { if (e == null) return; final NamedNodeMap nnm = e.getAttributes(); for (int i = 0; i < nnm.getLength(); ++i) { final Attr att = (Attr) nnm.item(i); setAttribute(att.getName(), att.getValue()); } for (Node n = e.getFirstChild(); n != null; n = n.getNextSibling()) { if (n.getNodeType() == Node.ELEMENT_NODE) { final Element element = (Element) n; if ("option".equals(element.getTagName())) { //$NON-NLS-1$ final String optionName = element.getAttribute("name"); //$NON-NLS-1$ final String value = Builder.getText(element); optionInitialValues.put(optionName, value); // Update the Configurer value if it is already registered final Configurer config = optionConfigurers.get(optionName); if (config != null) { config.setValue(value); } } else { try { final Buildable b = Builder.create(element); b.addTo(this); add(b); } catch (IllegalBuildException ex) { ErrorDialog.bug(ex); } } } } } public Element getBuildElement(Document doc) { final Element e = super.getBuildElement(doc); for (Configurer c : optionConfigurers.values()) { final Element option = doc.createElement("option"); //$NON-NLS-1$ option.setAttribute("name", c.getKey()); //$NON-NLS-1$ option.appendChild(doc.createTextNode(c.getValueString())); e.appendChild(option); } return e; } public Configurer getConfigurer() { if (config == null) { final Configurer defaultConfig = super.getConfigurer(); for (Configurer c : optionConfigurers.values()) { ((Container) defaultConfig.getControls()).add(c.getControls()); } } return config; } public String getAttributeValueString(String key) { if (NON_OWNER_UNMASKABLE.equals(key)) { return nonOwnerUnmaskable; } else if (PROMPT_STRING.equals(key)) { return promptString; } else if (CENTER_ON_MOVE.equals(key)) { return centerOnMoves; } else if (AUTO_REPORT.equals(key)) { return autoReport; } else if (MARK_MOVED.equals(key)) { return markMoved; } else if (PLAYER_ID_FORMAT.equals(key)) { return playerIdFormat.getFormat(); } else if (!optionConfigurers.containsKey(key)) { Object val = properties.get(key); return val != null ? val.toString() : null; } else { return null; } } public HelpFile getHelpFile() { return HelpFile.getReferenceManualPage("GlobalOptions.htm"); //$NON-NLS-1$ } public void removeFrom(Buildable parent) { } public void setAttribute(String key, Object value) { if (NON_OWNER_UNMASKABLE.equals(key)) { nonOwnerUnmaskable = (String) value; if (ALWAYS.equals(nonOwnerUnmaskable)) { ObscurableOptions.getInstance().allowAll(); } else if (NEVER.equals(nonOwnerUnmaskable)) { ObscurableOptions.getInstance().allowNone(); } else if (PROMPT.equals(nonOwnerUnmaskable)) { ObscurableOptions.getInstance().allowSome(promptString); GameModule.getGameModule().getGameState().addGameComponent(ObscurableOptions.getInstance()); GameModule.getGameModule().addCommandEncoder(ObscurableOptions.getInstance()); } } else if (PROMPT_STRING.equals(key)) { promptString = (String) value; ObscurableOptions.getInstance().setPrompt(promptString); } else if (CENTER_ON_MOVE.equals(key)) { centerOnMoves = (String) value; if (PROMPT.equals(centerOnMoves)) { BooleanConfigurer config = new BooleanConfigurer(CENTER_ON_MOVE, Resources.getString("GlobalOptions.center_on_move")); //$NON-NLS-1$ GameModule.getGameModule().getPrefs().addOption(config); } } else if (AUTO_REPORT.equals(key)) { autoReport = (String) value; if (PROMPT.equals(autoReport)) { BooleanConfigurer config = new BooleanConfigurer(AUTO_REPORT, Resources.getString("GlobalOptions.auto_report")); //$NON-NLS-1$ GameModule.getGameModule().getPrefs().addOption(config); } } else if (MARK_MOVED.equals(key)) { markMoved = (String) value; if (PROMPT.equals(markMoved)) { BooleanConfigurer config = new BooleanConfigurer(MARK_MOVED, Resources.getString("GlobalOptions.mark_moved")); //$NON-NLS-1$ GameModule.getGameModule().getPrefs().addOption(config); } } else if (PLAYER_ID_FORMAT.equals(key)) { playerIdFormat.setFormat((String) value); } else if (optionConfigurers.containsKey(key)) { optionConfigurers.get(key).setValue(value); } else { properties.put(key, value); } } public boolean autoReportEnabled() { return isEnabled(autoReport, AUTO_REPORT); } public boolean centerOnOpponentsMove() { return isEnabled(centerOnMoves, CENTER_ON_MOVE); } public boolean isMarkMoveEnabled() { return isEnabled(markMoved, MARK_MOVED); } public String getPlayerId() { playerIdFormat.setProperty(PLAYER_NAME, (String) GameModule.getGameModule().getPrefs().getValue(GameModule.REAL_NAME)); playerIdFormat.setProperty(PLAYER_SIDE, PlayerRoster.getMyLocalizedSide()); return playerIdFormat.getText(); } private boolean isEnabled(String attValue, String prefsPrompt) { if (ALWAYS.equals(attValue)) { return true; } else if (NEVER.equals(attValue)) { return false; } else { return Boolean.TRUE.equals(GameModule.getGameModule().getPrefs().getValue(prefsPrompt)); } } /** * Implement PropertyNameSource - Expose our preference names */ public List<String> getPropertyNames() { final ArrayList<String> l = new ArrayList<String>(); for (Buildable b : getBuildables()) { if (b instanceof BasicPreference) { l.add(((BasicPreference) b).getVariableName()); } } return l; } }