// // @(#)ClientFrame.java 4/2002 // // Copyright 2002 Zachary DelProposto. All rights reserved. // Use is subject to license terms. // // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // Or from http://www.gnu.org/ // package dip.gui; import dip.gui.map.*; import dip.gui.map.RenderCommandFactory.RenderCommand; import dip.gui.dialog.*; import dip.gui.dialog.newgame.NewGameDialog; import dip.gui.dialog.prefs.*; import dip.gui.order.GUIOrderFactory; import dip.gui.report.*; import dip.order.Orderable; import dip.order.OrderFormatOptions; import dip.world.*; import dip.misc.*; import dip.gui.swing.XJFileChooser; import dip.gui.undo.UndoResolve; import dip.gui.undo.UndoRedoManager; import dip.world.variant.VariantManager; import dip.world.variant.data.Variant; import dip.process.StdAdjudicator; import dip.order.ValidationOptions; import dip.tool.ToolManager; import dip.tool.ToolProxyImpl; import dip.tool.Tool; import dip.misc.Help; //import dip.order.Order; import javax.swing.*; //import javax.swing.event.*; //import javax.swing.border.*; import java.awt.*; import java.awt.event.*; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; import java.util.*; import java.awt.dnd.*; import java.awt.datatransfer.*; import java.io.*; //import java.net.*; import jcmdline.*; // command line handlers //import org.apache.batik.swing.JSVGCanvas; import org.apache.batik.util.XMLResourceDescriptor; /** * The main class for starting the client... everything starts here. * <p> * Note that this is not required for non-gui functionality; see dip.misc.TestSuite * for an example of using the adjudicator classes without the GUI. * <p> * Currently, there is no "server". * <p> * * * */ public class ClientFrame extends JFrame { // public property constants for PropertyChange events /** Event indicating that a World object was created */ public static final String EVT_WORLD_CREATED = "EVT_WORLD_CREATED"; /** Event indicating the World was destroyed (!) */ public static final String EVT_WORLD_DESTROYED = "EVT_WORLD_DESTROYED"; /** Event indicating the <b>current</b> TurnState has changed. */ public static final String EVT_TURNSTATE_CHANGED = "EVT_TURNSTATE_CHANGED"; /** Event indicating that the saved game state doesn't jive with the current state */ public static final String EVT_MODIFIED_STATE = "EVT_MODIFIED_STATE"; /** Event indicating a TurnState was added */ public static final String EVT_TURNSTATE_ADDED = "EVT_TURNSTATE_ADDED"; /** Event indicating a TurnState was removed */ public static final String EVT_TURNSTATE_REMOVED = "EVT_TURNSTATE_REMOVED"; /** Event indicating a TurnState was resolved */ public static final String EVT_TURNSTATE_RESOLVED = "EVT_TURNSTATE_RESOLVED"; /** Event indicating a mode change */ public static final String EVT_MODE_CHANGED = "EVT_MODE_CHANGED"; /** Event indicating that order validation options have changed */ public static final String EVT_VALOPTS_CHANGED = "EVT_VALOPTS_CHANGED"; /** Event indicating that MapMetadata object is ready */ public static final String EVT_MMD_READY = "EVT_MMD_READY"; // order events /** Event indicating an order was created */ public static final String EVT_ORDER_CREATED = "EVT_ORDER_CREATED"; /** Event indicating an order was deleted */ public static final String EVT_ORDER_DELETED = "EVT_ORDER_DELETED"; /** Event indicating multiple orders were created */ public static final String EVT_MULTIPLE_ORDERS_CREATED = "EVT_MULTIPLE_ORDERS_CREATED"; /** Event indicating multiple orders were deleted (cleared) */ public static final String EVT_MULTIPLE_ORDERS_DELETED = "EVT_MULTIPLE_ORDERS_DELETED"; // power events /** Event indicating the Powers that may be displayed have changed. */ public static final String EVT_DISPLAYABLE_POWERS_CHANGED = "EVT_DISPLAYABLE_POWERS_CHANGED"; /** Event indicating the Powers for which orders may be entered have changed. */ public static final String EVT_ORDERABLE_POWERS_CHANGED = "EVT_ORDERABLE_POWERS_CHANGED"; // modes [for EVT_MODE_CHANGED] /** Order Mode: orders may be entered in this mode. */ public static final String MODE_ORDER = "MODE_ORDER"; /** Edit Mode: units/ownership/etc. may be changed in this mode. */ public static final String MODE_EDIT = "MODE_EDIT"; /** Review Mode: Orders from previous turns (or if game has ended) may be reviewed in this mode.*/ public static final String MODE_REVIEW = "MODE_REVIEW"; /** "None" Mode: no World object is active */ public static final String MODE_NONE = "MODE_NONE"; // private constants private static final String PROGRAM_NAME = Utils.getLocalString("PROGRAM_NAME"); private static final int VERSION_MAJOR = 1; private static final int VERSION_MINOR = 6; private static final String KEY_VERSION_REVISION = "VERSION_REVISION"; private static final String KEY_CURRENT_LANGUAGE = "CURRENT_LANGUAGE"; // plugin directories private static final String VARIANT_DIR = "variants"; private static final String TOOL_DIR = "plugins"; private File variantDirPath = null; // instance variables private final JSplitPane splitPane; private MapPanel mapPanel = null; private ClientMenu clientMenu = null; private final PersistenceManager persistMan; private OrderDisplayPanel orderDisplayPanel = null; private OrderStatusPanel orderStatusPanel = null; private boolean isMMDSuppressed = false; private final PhaseSelector phaseSel; private StatusBar statusBar = null; private World world = null; // loaded World (null if none) private TurnState turnState = null; // current TurnState of above World private String currentMode = MODE_NONE; private GUIOrderFactory guiOrderFactory = null; // default GUI order factory. private boolean isValidating = false; // extra data validation when parsing? private ValidationOptions valOpts = new ValidationOptions(); private UndoRedoManager undoManager = null; private OrderFormatOptions orderFormatOptions = null; private MapMetadata mapMetadata = null; // power control instance variables private Power[] orderablePowers = new Power[0]; // powers for which orders may be entered private Power[] displayablePowers = new Power[0]; // powers for which orders may be displayed // for testing private final Object fireLock = new Object(); /** It all starts here .... */ public static void main(String args[]) { new ClientFrame(args); }// main() /** Create a ClientFrame, the main screen for the GUI Client. */ public ClientFrame(String args[]) { super(); long ttime = System.currentTimeMillis(); // total time long dtime = ttime; // delta time // parse command-line args parseCmdLine(args); dtime = Log.printDelta(dtime, "CF: arg parse time: "); Log.println(" mem max: ", String.valueOf(Runtime.getRuntime().maxMemory())); Log.println(" mem total: ", String.valueOf(Runtime.getRuntime().totalMemory())); Log.println(" mem free: ", String.valueOf(Runtime.getRuntime().freeMemory())); /* // Batik setup. org.apache.crimson.parser.XMLReaderImpl is the built-in JDK 1.4 parser XMLResourceDescriptor.setXMLParserClassName("org.apache.crimson.parser.XMLReaderImpl"); // doesn't work correctly... */ Log.println("Batik XML parser: ", XMLResourceDescriptor.getXMLParserClassName()); // setup per-OS options if(Utils.isOSX()) { System.setProperty("apple.laf.useScreenMenuBar", "true"); System.setProperty("com.apple.mrj.application.apple.menu.about.name", PROGRAM_NAME); System.setProperty("apple.awt.showGrowBox", "true"); // may no longer need /* NOTE: brushed metal is disabled; a bug in the Cocoa implementation causes dialogs to behave strangely when this is enabled. // //System.setProperty("apple.awt.brushMetalLook", "true"); */ } // replace bad-looking (metal, motif) LAFs with better-looking // ones. String lafClassName = UIManager.getSystemLookAndFeelClassName(); assert (lafClassName != null); if(Utils.isWindows()) { // higher-fidelity windows LAF lafClassName = "com.jgoodies.plaf.windows.ExtWindowsLookAndFeel"; // enable this to use the Java (not windows system) default font. // UIManager.put("Application.useSystemFontSettings", Boolean.FALSE); } else if(!Utils.isOSX()) { // keep synth; switch if Motif / Metal if( lafClassName.indexOf("MotifLookAndFeel") >= 0 || lafClassName.indexOf("MetalLookAndFeel") >= 0 ) { // good generic LAF lafClassName = "com.jgoodies.plaf.plastic.PlasticLookAndFeel"; } } try { if(lafClassName.indexOf("jgoodies") >= 0) { // for WebStart compatibility UIManager.put("ClassLoader", com.jgoodies.plaf.LookUtils.class.getClassLoader()); } Log.println(lafClassName); UIManager.setLookAndFeel(lafClassName); } catch (Exception e) { // do nothing; swing will load default L&F Log.println(e); } dtime = Log.printDelta(dtime, "CF: LAF setup time: "); // set exception handler GUIExceptionHandler.registerHandler(); // get the variant and tool directories. // do not change the variantDirPath if it was set // from the command line File toolDirPath = null; if(System.getProperty("user.dir") == null) { variantDirPath = (variantDirPath == null) ? new File(".", VARIANT_DIR) : variantDirPath; toolDirPath = new File(".", TOOL_DIR); } else { variantDirPath = (variantDirPath == null) ? new File(System.getProperty("user.dir"), VARIANT_DIR) : variantDirPath; toolDirPath = new File(System.getProperty("user.dir"), TOOL_DIR ); } // parse variants try { VariantManager.init(new File[]{variantDirPath}, isValidating); } catch(javax.xml.parsers.ParserConfigurationException e) { ErrorDialog.displayFatal(this, e); } catch(dip.world.variant.NoVariantsException e) { ErrorDialog.displayFatal(this, e); } dtime = Log.printDelta(dtime, "CF: variant setup time: "); // init Tools ToolManager.init(new File[]{toolDirPath}); Tool[] tools = ToolManager.getTools(); ToolProxyImpl toolProxy = new ToolProxyImpl(this); for(int i=0; i<tools.length; i++) { tools[i].setToolProxy(toolProxy); } dtime = Log.printDelta(dtime, "CF: tool setup time: "); // set frame icon setIconImage(Utils.getImageIcon(Utils.FRAME_ICON).getImage()); // init help system Help.init(); dtime = Log.printDelta(dtime, "CF: help init time: "); // setup menu clientMenu = new ClientMenu(this); setJMenuBar(clientMenu.getJMenuBar()); dtime = Log.printDelta(dtime, "CF: menu setup time: "); // init special filedialog class // // NOTE: JDK bug (?) can cause rare error at startup, due to Swing not being able // to get the dialog icons it needs. We delay init of this class as long as possible, // to see if that helps. XJFileChooser.init(); // Cached dialogs [these dialogs appear slowly if not cached] NewGameDialog.createCachedDialog(this); AboutDialog.createCachedDialog(this); // persistence (must come after menus are defined) persistMan = new PersistenceManager(this); dtime = Log.printDelta(dtime, "CF: PersistenceManager setup time: "); // frame listener, handles JFrame close events setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { persistMan.exit(); } }); // default GUIOrderFactory // [in the future, variants may alter this] guiOrderFactory = new GUIOrderFactory(); // setup drag-and-drop support new DropTarget(this, new CFDropTargetListener()); dtime = Log.printDelta(dtime, "CF: point A: "); // create default split pane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, false); splitPane.setOneTouchExpandable(true); splitPane.setVisible(false); splitPane.setDividerSize(10); splitPane.setResizeWeight(1); dtime = Log.printDelta(dtime, "CF: point B: "); // create statusbar statusBar = new StatusBar(); statusBar.setText(ClientFrame.PROGRAM_NAME + " " + getVersion()); dtime = Log.printDelta(dtime, "CF: point c: "); // PhaseSelector phaseSel = new PhaseSelector(this); dtime = Log.printDelta(dtime, "CF: point d: "); // add mode listener for this object addPropertyChangeListener(new ModeListener()); // set initial mode fireChangeMode(MODE_NONE); dtime = Log.printDelta(dtime, "CF: point e: "); // register menu listeners MenuHandler mh = new MenuHandler(); mh.registerMenuItems(); // get default order formatting options orderFormatOptions = DisplayPreferencePanel.getOrderFormatOptions(); dtime = Log.printDelta(dtime, "CF: point f: "); // setup layout getContentPane().setLayout(new BorderLayout()); getContentPane().add(splitPane, BorderLayout.CENTER); getContentPane().add(statusBar, BorderLayout.SOUTH); pack(); GeneralPreferencePanel.getWindowSettings(this); setVisible(true); fireChangeMode(MODE_NONE); toFront(); splash.destroy(); dtime = Log.printDelta(dtime, "CF: frame setup time: "); Log.printTimed(ttime, "ClientFrame() startup time: "); }// ClientFrame() /** Get the Menu component */ public ClientMenu getClientMenu() { return clientMenu; }// getClientMenu() /** Get the PersistenceManager component */ public PersistenceManager getPM() { return persistMan; }// getPM() /** Get the PhaseSelector component */ public PhaseSelector getPhaseSelector() { return phaseSel; }// getPhaseSelector() /** Get the OrderDisplayPanel component */ public synchronized OrderDisplayPanel getOrderDisplayPanel() { return orderDisplayPanel; }// getOrderDisplayPanel() /** Get the OrderStatusPanel component */ public synchronized OrderStatusPanel getOrderStatusPanel() { return orderStatusPanel; }// getOrderStatusPanel() /** Get the MapPanel component */ public synchronized MapPanel getMapPanel() { return mapPanel; }// getMapPanel() /** Get the StatusBar component */ public StatusBar getStatusBar() { return statusBar; }// getStatusBar() /** Get the UndoRedoManager component */ public synchronized UndoRedoManager getUndoRedoManager() { return undoManager; }// getUndoRedoManager() /** Get the current order validation options settings. */ public synchronized ValidationOptions getValidationOptions() { return valOpts; } /** * Set if we are suppressing MapMetadata placement errors. * This should only be set by Map Editors. */ public void setMMDSuppressed(boolean value) { isMMDSuppressed = value; }// setMMDSuppressed() /** Returns if MapMetadata placement errors are suppressed. */ public boolean isMMDSuppressed() { return isMMDSuppressed; }// isMMDSuppressed() /** Get the user-specified Order Format Options (OFO) */ public synchronized OrderFormatOptions getOFO() { return orderFormatOptions; }// getOFO() /** Set the user-specified Order Format Options (OFO) */ public synchronized void setOFO(OrderFormatOptions value) { orderFormatOptions = value; }// setOFO() /** Get MapMetadata (note: may be null) */ public synchronized MapMetadata getMapMetadata() { return mapMetadata; }// getMapMetadata() /** * Returns the Powers for which orders may be entered. * If this value is changed, a EVT_ORDERABLE_POWERS_CHANGED * property event will be fired. * <p> * This is only applicable for phases in which orders may * be entered. */ public Power[] getOrderablePowers() { synchronized(this) { return orderablePowers; } }// getOrderablePowers() /** * Returns the Powers which for which orders may be displayed. * If this value is changed, a EVT_DISPLAYABLE_POWERS_CHANGED * property event will be fired. * <p> * This is only applicable for phases in which orders may * be entered. For example, in Review mode, all orders * should be displayed. */ public Power[] getDisplayablePowers() { synchronized(this) { return displayablePowers; } }// getDisplayablePowers() /** * Returns true if we should validate all data files. This * may or may not be applicable, depending upon the data file * format. */ public boolean getValidating() { return isValidating; }// getValidating() /** * Returns the default OrderFactory for generating * new Orders. By default, GUIOrders are used, so * the default class should be a GUIOrderFactory or * derivative. * */ public GUIOrderFactory getGUIOrderFactory() { return guiOrderFactory; }// getGUIOrderFactory() /** Set the OrderDisplayPanel */ synchronized void setOrderDisplayPanel(OrderDisplayPanel odp) { orderDisplayPanel = odp; }// setOrderDisplayPanel() /** Set the OrderStatusPanel */ synchronized void setOrderStatusPanel(OrderStatusPanel osp) { orderStatusPanel = osp; }// setOrderStatusPanel() /** Set the UndoRedoManager */ synchronized void setUndoRedoManager(UndoRedoManager urm) { undoManager = urm; }// setUndoRedoManager() /** Set the MapPanel */ synchronized void setMapPanel(MapPanel mp) { mapPanel = mp; }// setMapPanel() /** Get the JSplitPane component */ public JSplitPane getJSplitPane() { return splitPane; }// getJSplitPane() /** Returns the current World, or null if no World is loaded. */ public synchronized World getWorld() { return world; }// getWorld() /** * Sets the current World. Uses the GameSetup * object to perform additional, as-needed, * game setup. A Null argument is not permitted. */ public synchronized void createWorld(World w) { // safety check if(w == null) { throw new IllegalArgumentException(); } // disable menu-input // remove old GUI components // and cleanup GUI / etc components destroyWorld(); // set the world world = w; // setup the world. NOTE: GUIGameSetup objects absolutely // must fire a WorldCreated event and a TurnstateChanged // event. GUIGameSetup ggs = (GUIGameSetup) world.getGameSetup(); ggs.setup(this, world); // validate something here validate(); }// createWorld() /** Destroy the World (! use with caution). */ public synchronized void destroyWorld() { fireChangeMode(MODE_NONE); clientMenu.setEnabled(ClientMenu.EDIT_EDIT_MODE, false); // NOTE: splitPane.removeAll() has seriously bad (and weird) // side effects when used (especially if components are null). // we will use this more laborious but more deterministic // code. (if not used, the right-most panel will take up // all the space, and the splitpane divider is not drawn) // Component c = splitPane.getLeftComponent(); if(c != null) { splitPane.remove(c); } c = splitPane.getRightComponent(); if(c != null) { splitPane.remove(c); } splitPane.setVisible(false); // cleanup if(orderStatusPanel != null) { orderStatusPanel.close(); orderStatusPanel = null; } if(orderDisplayPanel != null) { orderDisplayPanel.close(); orderDisplayPanel = null; } if(mapPanel != null) { mapPanel.close(); mapPanel = null; } undoManager = null; world = null; turnState = null; // inform everybody fireWorldDestroyed(); validate(); }// destroyWorld() /** * Fired when the World object was created. * This is typically only sent by the GUIGameSetup object. */ protected void fireWorldCreated(World w) { checkNotNull(w); synchronized(fireLock) { firePropertyChange(EVT_WORLD_CREATED, null, w); } }// fireWorldCreated() /** Fired when the World object was destroyed */ private void fireWorldDestroyed() { synchronized(fireLock) { firePropertyChange(EVT_WORLD_DESTROYED, getWorld(), null); } }// fireWorldDestroyed() /** * Indicates to listeners what the current TurnState is. * It is upto the Listener (such as OrderDisplayPanel and MapPanel) to * actually update the GUI. * <p> * <b>Note: The OLD event value (TurnState) will always be null for this * event; otherwise 'update' events will not fire.</b> * <p> * null is not acceptable. */ public void fireTurnstateChanged(TurnState ts) { checkNotNull(ts); synchronized(fireLock) { firePropertyChange(EVT_TURNSTATE_CHANGED, null, ts); } }// fireTurnstateChanged() /** * Indicates that the given TurnState has been Resolved * (adjudicated). May not be null. * <p> * <b>Note: The OLD event value (TurnState) will always be null for this * event; otherwise 'update' events will not fire.</b> * */ public void fireTurnstateResolved(TurnState ts) { checkNotNull(ts); synchronized(fireLock) { firePropertyChange(EVT_TURNSTATE_RESOLVED, null, ts); } }// fireTurnstateResolved() /** * Fired when the MapMetadata object is ready, or if * it is not ready (null), such as when a map is reloaded. */ public void fireMMDReady(MapMetadata mmd) { firePropertyChange(EVT_MMD_READY, null, mmd); }// fireTurnstateChanged() /** * Returns the current TurnState. <p> * Returns null if no World (or current TurnState) exists. */ public synchronized TurnState getTurnState() { return turnState; }// getTurnState() /** Fired when an order was created */ public final void fireOrderCreated(Orderable newOrder) { synchronized(fireLock) { checkNotNull(newOrder); firePropertyChange(EVT_ORDER_CREATED, null, newOrder); } }// fireOrderCreated() /** Fired when an order was deleted */ public final void fireOrderDeleted(Orderable deletedOrder) { synchronized(fireLock) { checkNotNull(deletedOrder); firePropertyChange(EVT_ORDER_DELETED, deletedOrder, null); } }// fireOrderDeleted() /** Fired when multiple orders were created */ public final void fireMultipleOrdersCreated(Orderable[] createdOrders) { synchronized(fireLock) { checkNotNull(createdOrders); firePropertyChange(EVT_MULTIPLE_ORDERS_CREATED, null, createdOrders); } }// fireMultipleOrdersCreated() /** Fired when multiple orders were deleted */ public final void fireMultipleOrdersDeleted(Orderable[] deletedOrders) { synchronized(fireLock) { checkNotNull(deletedOrders); firePropertyChange(EVT_MULTIPLE_ORDERS_DELETED, deletedOrders, null); } }// fireMultipleOrdersDeleted() /** Fired when displayed orders have changed */ public final void fireDisplayablePowersChanged(Power[] oldPowers, final Power[] newPowers) { synchronized(fireLock) { checkNotNull(newPowers); checkNotNull(oldPowers); firePropertyChange(EVT_DISPLAYABLE_POWERS_CHANGED, oldPowers, newPowers); } }// fireDisplayablePowersChanged() /** Fired when Powers for which orders may be entered have changed */ public final void fireOrderablePowersChanged(Power[] oldPowers, final Power[] newPowers) { synchronized(fireLock) { checkNotNull(newPowers); checkNotNull(oldPowers); firePropertyChange(EVT_ORDERABLE_POWERS_CHANGED, oldPowers, newPowers); } }// fireOrderablePowersChanged() /** * Fired if we have modified the World in such a way it * is no longer reflected in its saved state. */ public final void fireStateModified() { synchronized(fireLock) { firePropertyChange(EVT_MODIFIED_STATE, false, true); } }// fireStateModified() /** Fired if we have added a turnstate */ public final void fireTurnStateAdded(TurnState newTS) { synchronized(fireLock) { checkNotNull(newTS); firePropertyChange(EVT_TURNSTATE_ADDED, null, newTS); } }// fireTurnStateAdded() /** Fired if we have removed a turnstate */ public final void fireTurnStateRemoved() { synchronized(fireLock) { firePropertyChange(EVT_TURNSTATE_REMOVED, null, null); } }// fireTurnStateRemoved() /** Fired if we change the order Validation Options. New/Old options sent. */ public final void fireValidationOptionsChanged(ValidationOptions oldOpts, ValidationOptions newOpts) { checkNotNull(oldOpts); checkNotNull(newOpts); firePropertyChange(EVT_VALOPTS_CHANGED, oldOpts, newOpts); }// fireValidationOptionsChanged() /** * Change the operating mode for this ClientFrame.<br> */ public final synchronized void fireChangeMode(String newMode) { if(newMode != MODE_ORDER && newMode != MODE_REVIEW && newMode != MODE_EDIT && newMode != MODE_NONE) { throw new IllegalArgumentException("bad mode constant"); } this.currentMode = newMode; firePropertyChange(EVT_MODE_CHANGED, null, newMode); }// fireChangeMode() /** Prints the currently registered listeners to stdout. For debugging only. */ public void dbgPrintListeners() { PropertyChangeListener[] pcls = getPropertyChangeListeners(); System.out.println("ClientFrame listeners: "+pcls.length); for(int i=0; i<pcls.length; i++) { System.out.println(" "+pcls[i].getClass().getName()); } }// dbgPrintListeners() /** * Returns the Client version; format: <br> * major.minor.revision (language) */ public static String getVersion() { String revision = Utils.getLocalStringNoEx(KEY_VERSION_REVISION); String language = Utils.getLocalStringNoEx(KEY_CURRENT_LANGUAGE); StringBuffer sb = new StringBuffer(80); sb.append(VERSION_MAJOR); sb.append('.'); sb.append(VERSION_MINOR); if(revision != null) { sb.append('.'); sb.append(revision); } if(language != null) { sb.append(" ("); sb.append(language); sb.append(")"); } return sb.toString(); }// getVersion() /** Returns the Client major version */ public static int getVersionMajor() { return VERSION_MAJOR; } /** Returns the Client minor version */ public static int getVersionMinor() { return VERSION_MINOR; } /** Returns the Client program name. */ public static String getProgramName() { return PROGRAM_NAME; } /** * DropTarget listener that allows ClientFrame to respond to * drag events. */ private class CFDropTargetListener extends FileDropTargetListener { public void processDroppedFiles(File[] files) { for(int i=0; i<files.length; i++) { if(files.length >= 0) { World world = ClientFrame.this.persistMan.acceptDrag(files[0], ClientFrame.this.getWorld()); if(world != null) { world.setGameSetup(new DefaultGUIGameSetup()); ClientFrame.this.createWorld(world); } ClientFrame.this.persistMan.updateTitle(); } } }// processDroppedFiles() }// inner class CFDropTargetListener /** * Gets the current mode. Use a fireChangeMode() to set the current mode. */ public synchronized String getMode() { // return a new reference, so that noone can change the mode // without using fireChangeMode() final String mode = currentMode; return mode; }// getMode() /** * Property Listener for listening to Settings Changes * */ private class ModeListener implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent evt) { String evtName = evt.getPropertyName(); if(evtName == EVT_MODE_CHANGED) { String newMode = (String) evt.getNewValue(); if(newMode == MODE_NONE) { statusBar.clearModeText(); } else if(newMode == MODE_REVIEW) { statusBar.setModeText(Utils.getLocalString("ClientFrame.mode.review")); if(ClientFrame.this.getTurnState().isEnded()) { statusBar.setModeText(Utils.getLocalString("ClientFrame.mode.ended")); } } else if(newMode == MODE_EDIT) { statusBar.setModeText(Utils.getLocalString("ClientFrame.mode.edit")); } else if(newMode == MODE_ORDER) { statusBar.setModeText(Utils.getLocalString("ClientFrame.mode.order")); } else { throw new IllegalStateException("invalid mode: "+newMode); } } else if(evtName == EVT_TURNSTATE_CHANGED) { synchronized(ClientFrame.this) { ClientFrame.this.turnState = (TurnState) evt.getNewValue(); if(turnState.isEnded() || turnState.isResolved()) { fireChangeMode(MODE_REVIEW); } else { fireChangeMode(MODE_ORDER); } } } else if(evtName == EVT_DISPLAYABLE_POWERS_CHANGED) { synchronized(ClientFrame.this) { ClientFrame.this.displayablePowers = (Power[]) evt.getNewValue(); } } else if(evtName == EVT_ORDERABLE_POWERS_CHANGED) { synchronized(ClientFrame.this) { ClientFrame.this.orderablePowers = (Power[]) evt.getNewValue(); } } else if(evtName == EVT_MMD_READY) { synchronized(ClientFrame.this) { ClientFrame.this.mapMetadata = (MapMetadata) evt.getNewValue(); } } } }// class ModeListener /** Setup command-line options and parse the command line. */ private void parseCmdLine(String args[]) { // locale option [takes 1 argument] StringParam argLocale = new StringParam("lang", "force language to the specified ISO-639 2-letter type (e.g., \"de\", \"en\", \"fr\")", 2, 2, true, false); // log options FileParam argLogFile = new FileParam("log", "writes logging information to file or stdout [if \"stdout\" specified]", FileParam.NO_ATTRIBUTES, StringParam.OPTIONAL, FileParam.SINGLE_VALUED); // variantpath specifier FileParam argVariantPath = new FileParam("variantpath", "load variant plugins from specified directory", FileParam.IS_DIR & FileParam.IS_READABLE & FileParam.EXISTS, FileParam.OPTIONAL, FileParam.SINGLE_VALUED); // validate option BooleanParam validateOpt = new BooleanParam("validate", "validate XML and SVG data files"); BooleanParam splashOpt = new BooleanParam("nosplash", "do not show splash screen"); // verbose help text String helpText = " "; // main command line handler CmdLineHandler cl = new VersionCmdLineHandler(getVersion(), new HelpCmdLineHandler(helpText, "jdip", "Adjudicator and Game Manager for multiplayer diplomacy-based strategy games", // options new Parameter[] {argLocale, argLogFile, argVariantPath, validateOpt, splashOpt}, // arguments [left on command line] new Parameter[] {} ) ); // parse command line cl.parse(args); // if Locale has been set, use it. if(argLocale.isSet()) { Locale locale = new Locale( argLocale.getValue().toLowerCase() ); System.out.println("Using Language: "+locale.getLanguage()+" ["+locale.getDisplayLanguage()+"]"); Utils.loadLocale(locale); } // set variant path if given if(argVariantPath.isSet()) { variantDirPath = argVariantPath.getFile(); } // do logging if(argLogFile.isSet()) { if("stdout".equalsIgnoreCase(argLogFile.getFile().getName())) { Log.setFile(null); } else { Log.setFile(argLogFile.getFile()); } Log.setLogging(true); } // set validation flag isValidating = validateOpt.isTrue(); }// parseCmdLine() /** Check if an argument is null; throw IllegalArgumentException if so */ private void checkNotNull(Object arg) { if(arg == null) { throw new IllegalArgumentException("null argument!"); } }// checkNotNull() /** Resolve orders. Requires an OrderDisplayPanel to be present. */ public void resolveOrders() { if(orderDisplayPanel != null) { final TurnState resolvedTurnState = getTurnState(); StdAdjudicator stdJudge = new StdAdjudicator(getGUIOrderFactory(), resolvedTurnState); stdJudge.setStatReporting(true); // report order statistics stdJudge.setPowerOrderChecking(true); // check for cheats & bugs stdJudge.process(); fireStateModified(); // this may be null, if the game has been won final TurnState newTurnState = stdJudge.getNextTurnState(); if(newTurnState != null) { world.setTurnState(newTurnState); fireTurnStateAdded(newTurnState); } // create Undo result undoManager.addEdit(new UndoResolve(undoManager, getTurnState(), newTurnState)); // simplify undoable actions undoManager.simplify(); // see if game has ended; if so, show a dialog & change mode if( getTurnState().isEnded() || (newTurnState != null && newTurnState.isEnded()) ) { Utils.popupInfo( ClientFrame.this, Utils.getLocalString("ClientFrame.ended.dialog.title"), Utils.getText(Utils.getLocalString("ClientFrame.ended.dialog.text")) ); fireChangeMode(MODE_REVIEW); } // show results (if desired) if(GeneralPreferencePanel.getShowResolutionResults()) { TurnState priorTS = getWorld().getPreviousTurnState(newTurnState); ResultWriter.displayDialog(ClientFrame.this, priorTS, getOFO()); } fireTurnstateResolved(resolvedTurnState); if(newTurnState != null) { fireTurnstateChanged(newTurnState); } } }// resolveOrders() /** * Private class to handle menu events, * without exposing public methods to other classes. * Furthermore, we can directly call no-arg methods in * final helper classes (PhaseSelector, PersistanceManager). */ private class MenuHandler { // inner state private String oldEditMode = null; /** Register the menu items */ public void registerMenuItems() { // file clientMenu.setActionMethod(ClientMenu.FILE_NEW_STD, this, "onFileNewStd"); clientMenu.setActionMethod(ClientMenu.FILE_NEW_F2F, this, "onFileNewF2F"); clientMenu.setActionMethod(ClientMenu.FILE_NEW_NET, this, "onFileNewNET"); clientMenu.setActionMethod(ClientMenu.FILE_OPEN, this, "onFileOpen"); clientMenu.setActionMethod(ClientMenu.FILE_SAVE, persistMan, "save"); clientMenu.setActionMethod(ClientMenu.FILE_SAVEAS, persistMan, "saveAs"); clientMenu.setActionMethod(ClientMenu.FILE_SAVETO, persistMan, "saveTo"); clientMenu.setActionMethod(ClientMenu.FILE_IMPORT_FILE, this, "onFileImport"); clientMenu.setActionMethod(ClientMenu.FILE_IMPORT_FLOC, this, "onFileImportFloc"); clientMenu.setActionMethod(ClientMenu.FILE_EXIT, persistMan, "exit"); // edit clientMenu.setActionMethod(ClientMenu.EDIT_UNDO, this, "onEditUndo"); clientMenu.setActionMethod(ClientMenu.EDIT_REDO, this, "onEditRedo"); clientMenu.setActionMethod(ClientMenu.EDIT_SELECT_ALL, this, "onEditSelectAll"); clientMenu.setActionMethod(ClientMenu.EDIT_SELECT_NONE, this, "onEditSelectNone"); clientMenu.setActionMethod(ClientMenu.EDIT_DELETE, this, "onEditDelete"); clientMenu.setActionMethod(ClientMenu.EDIT_CLEAR_ALL, this, "onEditClearAll"); clientMenu.setActionMethod(ClientMenu.EDIT_EDIT_MODE, this, "onEditEditMode"); clientMenu.setActionMethod(ClientMenu.EDIT_METADATA, this, "onEditMetadata"); clientMenu.setActionMethod(ClientMenu.EDIT_PREFERENCES, this, "onEditPreferences"); // orders clientMenu.setActionMethod(ClientMenu.ORDERS_VAL_OPTIONS, this, "onOrdersValOpts"); clientMenu.setActionMethod(ClientMenu.ORDERS_REVALIDATE, this, "onOrdersRevalidate"); clientMenu.setActionMethod(ClientMenu.ORDERS_MULTI_INPUT, this, "onOrdersMultiInput"); clientMenu.setActionMethod(ClientMenu.ORDERS_RESOLVE, this, "onOrdersResolve"); // history clientMenu.setActionMethod(ClientMenu.HISTORY_PREVIOUS, phaseSel, "previous"); clientMenu.setActionMethod(ClientMenu.HISTORY_NEXT, phaseSel, "next"); clientMenu.setActionMethod(ClientMenu.HISTORY_INITIAL, phaseSel, "first"); clientMenu.setActionMethod(ClientMenu.HISTORY_LAST, phaseSel, "last"); clientMenu.setActionMethod(ClientMenu.HISTORY_SELECT, this, "onHistorySelect"); // view clientMenu.setActionMethod(ClientMenu.VIEW_NAMES_NONE, this, "onViewNamesNone"); clientMenu.setActionMethod(ClientMenu.VIEW_NAMES_SHORT, this, "onViewNamesShort"); clientMenu.setActionMethod(ClientMenu.VIEW_NAMES_FULL, this, "onViewNamesFull"); clientMenu.setActionMethod(ClientMenu.VIEW_SUPPLY_CENTERS, this, "onViewSC"); clientMenu.setActionMethod(ClientMenu.VIEW_UNITS, this, "onViewUnits"); clientMenu.setActionMethod(ClientMenu.VIEW_DISLODGED_UNITS, this, "onViewDislodged"); clientMenu.setActionMethod(ClientMenu.VIEW_UNORDERED, this, "onViewUnordered"); clientMenu.setActionMethod(ClientMenu.VIEW_INFLUENCE, this, "onViewInfluence"); clientMenu.setActionMethod(ClientMenu.VIEW_SELECT_MAP, this, "onViewSelectMap"); clientMenu.setActionMethod(ClientMenu.VIEW_SHOW_MAP, this, "onViewShowMap"); // reports clientMenu.setActionMethod(ClientMenu.REPORTS_RESULTS, this, "onReportsResults"); clientMenu.setActionMethod(ClientMenu.REPORTS_PREVIOUS_RESULTS, this, "onReportsPreviousResults"); clientMenu.setActionMethod(ClientMenu.REPORTS_STATUS, this, "onReportsStatus"); clientMenu.setActionMethod(ClientMenu.REPORTS_SC_HISTORY, this, "onReportsSCHistory"); clientMenu.setActionMethod(ClientMenu.REPORTS_ORDER_STATS, this, "onReportsOrderStats"); clientMenu.setActionMethod(ClientMenu.REPORTS_MAP_INFO, this, "onReportsMapInfo"); // help clientMenu.setActionMethod(ClientMenu.HELP_ABOUT, this, "onHelpAbout"); Help.enableHelpOnButton(clientMenu.getMenuItem(ClientMenu.HELP_CONTENTS), Help.HelpID.Contents); }// registerMenuItems() // file // public void onFileNewStd() { World world = persistMan.newGame(); if(world != null) { createWorld(world); persistMan.updateTitle(); } }// onFileNewStd() public void onFileNewF2F() { World world = persistMan.newF2FGame(); if(world != null) { createWorld(world); persistMan.updateTitle(); } }// onFileNewStd() public void onFileNewNET() { World world = persistMan.newNetworkGame(); if(world != null) { createWorld(world); persistMan.updateTitle(); } }// onFileNewStd() public void onFileOpen() { World world = persistMan.open(); if(world != null) { createWorld(world); } } public void onFileImport() { World world = persistMan.importJudge(getWorld()); if(world != null) { world.setGameSetup(new DefaultGUIGameSetup()); createWorld(world); } persistMan.updateTitle(); } public void onFileImportFloc() { World world = persistMan.importFloc(); if(world != null) { world.setGameSetup(new DefaultGUIGameSetup()); createWorld(world); persistMan.updateTitle(); } } // edit // public void onEditUndo() { if(orderDisplayPanel != null) { undoManager.undo(); } } public void onEditRedo() { if(orderDisplayPanel != null) { undoManager.redo(); } } public void onEditSelectAll() { if(orderDisplayPanel != null) { orderDisplayPanel.selectAll(); } } public void onEditSelectNone() { if(orderDisplayPanel != null) { orderDisplayPanel.selectNone(); } } public void onEditDelete() { if(orderDisplayPanel != null) { orderDisplayPanel.removeSelected(); } } public void onEditClearAll() { if(orderDisplayPanel != null) { orderDisplayPanel.removeAllOrders(true); } } public void onEditEditMode() { if(clientMenu.getSelected(ClientMenu.EDIT_EDIT_MODE)) { oldEditMode = getMode(); fireChangeMode(MODE_EDIT); } else { // check and see if any powers were eliminated after edit getTurnState().getPosition().setEliminationStatus(world.getMap().getPowers()); fireChangeMode(oldEditMode); } } public void onEditMetadata() { if(orderDisplayPanel != null) { MetadataDialog.displayDialog(ClientFrame.this); } } public void onEditPreferences() { PreferenceDialog.displayDialog(ClientFrame.this); } // orders // public void onOrdersValOpts() { if(getOrderDisplayPanel() != null) { ValidationOptions newOpts = ValidationOptionsDialog.displayDialog(ClientFrame.this, valOpts); fireValidationOptionsChanged(valOpts, newOpts); valOpts = newOpts; } } public void onOrdersRevalidate() { if(getOrderDisplayPanel() != null) { getOrderDisplayPanel().revalidateAllOrders(); } } public void onOrdersMultiInput() { if(getWorld() != null) { MultiOrderEntry.displayDialog(ClientFrame.this, getWorld()); } } public void onOrdersResolve() { resolveOrders(); }// onOrdersResolve() // history // public void onHistorySelect() { if(orderDisplayPanel != null) { Phase phase = SelectPhaseDialog.displayDialog(ClientFrame.this); if(phase != null) { fireTurnstateChanged(world.getTurnState(phase)); } } } // view // public void onViewNamesNone() { if(mapPanel != null) { RenderCommand rc = mapPanel.getRenderCommandFactory().createRCSetLabel(mapPanel.getMapRenderer(), MapRenderer2.VALUE_LABELS_NONE); execRenderCommand(rc); } } public void onViewNamesShort() { if(mapPanel != null) { RenderCommand rc = mapPanel.getRenderCommandFactory().createRCSetLabel(mapPanel.getMapRenderer(), MapRenderer2.VALUE_LABELS_BRIEF); execRenderCommand(rc); } } public void onViewNamesFull() { if(mapPanel != null) { RenderCommand rc = mapPanel.getRenderCommandFactory().createRCSetLabel(mapPanel.getMapRenderer(), MapRenderer2.VALUE_LABELS_FULL); execRenderCommand(rc); } } public void onViewSC() { if(mapPanel != null) { boolean value = clientMenu.getSelected(ClientMenu.VIEW_SUPPLY_CENTERS); RenderCommand rc = mapPanel.getRenderCommandFactory().createRCSetDisplaySC(mapPanel.getMapRenderer(), value); execRenderCommand(rc); } } public void onViewUnits() { if(mapPanel != null) { boolean value = clientMenu.getSelected(ClientMenu.VIEW_UNITS); RenderCommand rc = mapPanel.getRenderCommandFactory().createRCSetDisplayUnits(mapPanel.getMapRenderer(), value); execRenderCommand(rc); } } public void onViewDislodged() { if(mapPanel != null) { boolean value = clientMenu.getSelected(ClientMenu.VIEW_DISLODGED_UNITS); RenderCommand rc = mapPanel.getRenderCommandFactory().createRCSetDisplayDislodgedUnits(mapPanel.getMapRenderer(), value); execRenderCommand(rc); } } // VIEW_ORDERS is handled internally by ClientMenu public void onViewUnordered() { if(mapPanel != null) { boolean value = clientMenu.getSelected(ClientMenu.VIEW_UNORDERED); RenderCommand rc = mapPanel.getRenderCommandFactory().createRCSetDisplayUnordered(mapPanel.getMapRenderer(), value); execRenderCommand(rc); } } public void onViewInfluence() { if(mapPanel != null) { boolean value = clientMenu.getSelected(ClientMenu.VIEW_INFLUENCE); RenderCommand rc = mapPanel.getRenderCommandFactory().createRCSetInfluenceMode(mapPanel.getMapRenderer(), value); execRenderCommand(rc); } } public void onViewSelectMap() { if(world != null) { MapPicker.displayDialog(ClientFrame.this, world); } } public void onViewShowMap() { if(mapPanel != null) { boolean value = clientMenu.getSelected(ClientMenu.VIEW_SHOW_MAP); RenderCommand rc = mapPanel.getRenderCommandFactory().createRCShowMap(mapPanel.getMapRenderer(), value); execRenderCommand(rc); } } // reports // public void onReportsResults() { ResultWriter.displayDialog(ClientFrame.this, getTurnState(), getOFO()); } public void onReportsPreviousResults() { // getPreviousTurnState() should not return null, if this item is enabled. if(mapPanel != null) { final TurnState ts = getTurnState(); ResultWriter.displayDialog(ClientFrame.this, world.getPreviousTurnState(ts), getOFO()); } } public void onReportsSCHistory() { SCHistoryWriter.displayDialog(ClientFrame.this, getWorld()); } public void onReportsStatus() { StateWriter.displayDialog(ClientFrame.this, getTurnState()); } public void onReportsOrderStats() { OrderStatsWriter.displayDialog(ClientFrame.this, getWorld(), getOFO()); } public void onReportsMapInfo() { if(getWorld() != null) { VariantInfoWriter.displayDialog(ClientFrame.this, getWorld()); } } // help // public void onHelpAbout() { AboutDialog.displayDialog(ClientFrame.this); } /** Helper method for View methods */ private void execRenderCommand(RenderCommand rc) { MapRenderer2 mr2 = mapPanel.getMapRenderer(); mr2.execRenderCommand(rc); }// execRenderCommand }// inner class MenuHandler() }// class ClientFrame