// // @(#)OrderControlBar.java 1.00 4/1/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.map; import dip.gui.order.*; import dip.misc.Utils; import dip.misc.Log; import dip.order.ValidationOptions; import dip.process.Adjustment; import dip.world.*; import org.apache.batik.dom.events.DOMKeyEvent; import org.w3c.dom.events.MouseEvent; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; /* * WARNING: some versions of batik (CVS version > 1.5b4b) * do not change cursors appropriately. */ /** * ControlBar that displays Order functionality <p> * * Mnemonics not used -- they interfere with menubar functionality. * */ public class OrderControlBar extends ViewControlBar { // action commands: movement phase private final static String MODE_HOLD = "MODE_HOLD"; private final static String MODE_MOVE = "MODE_MOVE"; private final static String MODE_SUPPORT = "MODE_SUPPORT"; private final static String MODE_CONVOY = "MODE_CONVOY"; // action commands: retreat phase private final static String MODE_RETREAT = "MODE_RETREAT"; private final static String MODE_DISBAND = "MODE_DISBAND"; // action commands: adjustment phase private final static String MODE_BUILD_ARMY = "MODE_BUILD_ARMY"; private final static String MODE_BUILD_FLEET = "MODE_BUILD_FLEET"; private final static String MODE_BUILD_WING = "MODE_BUILD_WING"; private final static String MODE_REMOVE = "MODE_REMOVE"; private final static String MODE_WAIVE_BUILD = "MODE_WAIVE_BUILD"; // cancel key (ESC) character code (char 27) private final static int KEY_CANCEL = java.awt.event.KeyEvent.VK_ESCAPE; // il8n private final static String[] GROUP_MOVEMENT_TEXT = Utils.getLocalStringArray( "OrdConBar.icons.movement" ); private final static String[] GROUP_MOVEMENT_CMD = {MODE_HOLD, MODE_MOVE, MODE_SUPPORT, MODE_CONVOY}; //public static String[] GROUP_MOVEMENT_MNEMONIC = Utils.getLocalStringArray("OrdConBar.mnemonic.movement"); private final static int[] GROUP_MOVEMENT_CHARCODES = Utils.getLocalIntArray( "OrdConBar.keys.movement" ); private final static String GROUP_MOVEMENT_DEFAULT_BUTTON = MODE_MOVE; private final static String[] GROUP_RETREAT_TEXT = Utils.getLocalStringArray( "OrdConBar.icons.retreat" ); private final static String[] GROUP_RETREAT_CMD = {MODE_RETREAT, MODE_DISBAND}; //public static String[] GROUP_RETREAT_MNEMONIC = Utils.getLocalStringArray("OrdConBar.mnemonic.retreat"); private final static int[] GROUP_RETREAT_CHARCODES = Utils.getLocalIntArray( "OrdConBar.keys.retreat" ); private final static String GROUP_RETREAT_DEFAULT_BUTTON = MODE_RETREAT; private final static String[] GROUP_ADJUSTMENT_TEXT = Utils.getLocalStringArray( "OrdConBar.icons.adjustment" ); private final static String[] GROUP_ADJUSTMENT_CMD = {MODE_BUILD_ARMY, MODE_BUILD_FLEET, MODE_BUILD_WING, MODE_REMOVE, MODE_WAIVE_BUILD}; //public static String[] GROUP_ADJUSTMENT_MNEMONIC = Utils.getLocalStringArray("OrdConBar.mnemonic.adjustment"); private final static int[] GROUP_ADJUSTMENT_CHARCODES = Utils.getLocalIntArray( "OrdConBar.keys.adjustment" ); private final static String GROUP_ADJUSTMENT_DEFAULT_BUTTON = MODE_BUILD_ARMY; // buttons private JToggleButton[] buttons = null; // instance variables private String currentAction = null; private String defaultAction = null; private int[] charMap = null; private GUIOrder.StateInfo stateInfo = null; private GUIOrderFactory guiOrderFactory = null; private GUIOrder currentOrder = null; private StringBuffer sb = null; // recycled stringbuffer private Location lastLoc = null; // last location we were over. // For dragging private Location dragLoc = null; private boolean inDrag = false; private Location dragSupportLoc = null; private GUIOrder tempOrder = null; // use explicit GUIMove? private final boolean useExplicitGUIMove; /** * Any time the TurnState changes, a new OrderControlBar is created. * * @param mp MapPanel object * @since */ public OrderControlBar( MapPanel mp ) { super( mp ); // init guiOrderFactory = mapPanel.getClientFrame().getGUIOrderFactory(); sb = new StringBuffer( 80 ); // Create GUIOrder StateInfo object stateInfo = new GUIOrder.StateInfo(); stateInfo.setTurnState( mapPanel.getTurnState() ); stateInfo.setRuleOptions( mapPanel.getWorld().getRuleOptions() ); stateInfo.setValidationOptions( mapPanel.getClientFrame().getValidationOptions() ); stateInfo.setClientFrame( mapPanel.getClientFrame() ); // set adjustment info, if appropriate phase // add to StateInfo object if( stateInfo.getTurnState().getPhase().getPhaseType() == Phase.PhaseType.ADJUSTMENT ) { Power[] powers = stateInfo.getTurnState().getWorld().getMap().getPowers(); Adjustment.AdjustmentInfoMap adjMap = Adjustment.getAdjustmentInfo( stateInfo.getTurnState(), stateInfo.getRuleOptions(), powers ); stateInfo.setAdjustmenInfoMap( adjMap ); } RuleOptions ro = mapPanel.getWorld().getRuleOptions(); useExplicitGUIMove = RuleOptions.VALUE_PATHS_EXPLICIT.equals(ro.getOptionValue(RuleOptions.OPTION_CONVOYED_MOVES)); makeLayout(); } // OrderControlBar() /** * Update the ValidationOptions * * @param valOpts The new validationOptions value * @since */ public void setValidationOptions( ValidationOptions valOpts ) { stateInfo.setValidationOptions( valOpts ); } // setValidationOptions() /** * Dispatch mouse over events * * @param loc current Location * @since me mouseEvent (<b>may be null</b>) */ public void mouseOver(MouseEvent me, Location loc) { lastLoc = loc; if( currentOrder != null ) { // handle null Location if( loc == null ) { handleNullLoc(); return; } if( currentOrder.testLocation( stateInfo, loc, sb ) ) { setUnitCursor( true ); // Painting the current order in drag mode // Are we dragging from a province that has a unit available to move? if( inDrag && !dragLoc.equals( loc ) && currentOrder.getSourceUnitType() != null ) { if( MODE_MOVE == currentAction ) { tempOrder = (GUIMove) guiOrderFactory.createMove( currentOrder.getPower(), dragLoc, currentOrder.getSourceUnitType(), loc, ( (GUIMove) currentOrder ).isViaConvoy() ); // The next two lines ensure tempOrder.isComplete() tempOrder.setLocation(stateInfo, dragLoc, sb); tempOrder.setLocation(stateInfo, loc, sb); mapPanel.getOrderDisplayPanel().addOrder( tempOrder, false ); } else if( MODE_RETREAT == currentAction ) { tempOrder = (GUIRetreat) guiOrderFactory.createRetreat( currentOrder.getPower(), dragLoc, currentOrder.getSourceUnitType(), loc ); mapPanel.getOrderDisplayPanel().addOrder( tempOrder, false ); } else if( MODE_SUPPORT == currentAction ) { if( dragSupportLoc == null ) { dragSupportLoc = loc; doOrder( me, loc ); } tempOrder = guiOrderFactory.createGUISupport(); tempOrder.setLocation( stateInfo, dragLoc, sb ); tempOrder.setLocation( stateInfo, dragSupportLoc, sb ); if( tempOrder.setLocation( stateInfo, loc, sb ) ) { mapPanel.getOrderDisplayPanel().addOrder( tempOrder, false ); } } } } else { // Current order can't be completed here // Change cursor if( currentOrder.getSourceUnitType() != null ) { setUnitCursor( false ); } else { mapPanel.setMapCursor( MapPanel.BAD_ACTION ); } // append cancel message, if the order is "in progress" // otherwise do not append the cancel message. if(currentOrder.getCurrentLocationNum() > 0) { sb.append(' '); sb.append( Utils.getLocalString( GUIOrder.CLICK_TO_CANCEL ) ); } } mapPanel.getStatusBarUtils().displayProvinceInfo( loc, sb.toString() ); } else { // inform that no order has been selected. mapPanel.getStatusBarUtils().displayProvinceInfo( loc, Utils.getLocalString( "OrdConBar.noselection" ) ); mapPanel.setMapCursor( MapPanel.BAD_ACTION ); } } // mouseOver() /** * Dispatch mouse out events * * @param loc current Location * @since */ public void mouseOut(MouseEvent me, Location loc) { lastLoc = loc; if( currentOrder == null ) { // inform that no order has been selected mapPanel.setMapCursor( MapPanel.BAD_ACTION ); mapPanel.getStatusBarUtils().setText( Utils.getLocalString( "OrdConBar.noselection" ) ); } else { handleNullLoc(); } } // handleNullLoc() /** * Dispatch mouse click events * */ public void mouseClicked(MouseEvent me, Location loc) { inDrag = false; dragLoc = null; dragSupportLoc = null; currentAction = defaultAction; } /** * Called when the user is attempting to start (by clicking or initiating a * drag) or complete (by clicking again or dropping) an order. * * @param loc The location where the mouse event took place. * @since */ public void doOrder(MouseEvent me, Location loc) { if( currentOrder != null ) { // cancel if we are in a null location if( loc == null ) { if((tempOrder!=null && tempOrder.isComplete())) // We can still finish up { loc = tempOrder.getSource(); } else { doCanceled(); return; } } if( currentOrder.setLocation( stateInfo, loc, sb ) ) { mapPanel.setMapCursor( MapPanel.DEFAULT_CURSOR ); mapPanel.getClientFrame().getOrderStatusPanel().setOrderText(currentOrder.toFormattedString(mapPanel.getClientFrame().getOFO())); mapPanel.getStatusBarUtils().displayProvinceInfo( loc, sb.toString() ); } else if(tempOrder != null && tempOrder.isComplete()) { currentOrder = tempOrder; } else { // we cannot set the location. // cancel the order. // mapPanel.setMapCursor( MapPanel.BAD_ACTION ); mapPanel.getStatusBarUtils().displayProvinceInfo( loc, Utils.getLocalString( GUIOrder.CANCELED, currentOrder.getFullName() ) ); setGUIOrder( loc ); // create a new order writer return; } if( currentOrder.isComplete() ) { mapPanel.getOrderDisplayPanel().addOrder( currentOrder, true ); mapPanel.getClientFrame().getOrderStatusPanel().clearOrderText(); mapPanel.setMapCursor( MapPanel.BAD_ACTION ); setGUIOrder( loc ); // create a new order writer } else { // retest the location if we aren't complete; the current location may not // be valid for subsequent clicks. mouseOver( me, loc ); } } } // mouseClicked() // Start of click or drag /** * Description of the Method * * @param loc current Location * @param me mouse event * @since */ public void mouseDown(MouseEvent me, Location loc) { if( loc != null ) { inDrag = true; dragLoc = loc; final short button = me.getButton(); if( button == DOMUIEventListener.BUTTON_RIGHT ) { currentAction = MODE_SUPPORT; setGUIOrder( loc ); // Start a support order } else if( button == DOMUIEventListener.BUTTON_MIDDLE ) { currentAction = MODE_HOLD; setGUIOrder( loc ); // Start a hold order currentAction = defaultAction; inDrag = false; // Ignore the rest of the drag } doOrder( me, loc ); if( currentOrder.getSourceUnitType() != null ) { setUnitCursor( true ); } } } // Note: A click seems to generate a mouseDown and mouseClicked but no mouseUp. /** * Description of the Method * * @param loc current Location * @since */ public void mouseUp(MouseEvent me, Location loc) { currentAction = defaultAction; if( inDrag ) { doOrder( me, loc ); } tempOrder = null; inDrag = false; dragLoc = null; dragSupportLoc = null; } // doCanceled() /** * Handle DOM keyPress events * * @param dke DOMKeyEvent * @param loc current mouse Location * @since */ public void keyPressed(DOMKeyEvent dke, Location loc) { super.keyPressed( dke, loc ); // ignore modifier keys if( dke.getAltKey() || dke.getCtrlKey() || dke.getMetaKey() ) { return; } final int charCode = dke.getCharCode(); if( charCode == KEY_CANCEL ) { doCanceled(); } for( int i = 0; i < charMap.length; i++ ) { if( charCode == charMap[i] ) { buttons[i].doClick(); if( loc != null ) { mouseOver( null, loc ); } return; } } } // inner class ToggleListener /** * Creates a new GUIOrder subclass based upon the currently selected order. * <p> * sets currentOrder null if nothing is selected. * * @param loc current mouse Location * @since */ protected void setGUIOrder( Location loc ) { mapPanel.getClientFrame().getOrderStatusPanel().clearOrderText(); /* * This is amateurish but effective. Should probably be replaced by * an object-oriented approach */ if( currentAction == MODE_HOLD ) { currentOrder = guiOrderFactory.createGUIHold(); } else if( currentAction == MODE_MOVE ) { if(useExplicitGUIMove) { currentOrder = guiOrderFactory.createGUIMoveExplicit(); } else { currentOrder = guiOrderFactory.createGUIMove(); } } else if( currentAction == MODE_SUPPORT ) { currentOrder = guiOrderFactory.createGUISupport(); } else if( currentAction == MODE_CONVOY ) { currentOrder = guiOrderFactory.createGUIConvoy(); } else if( currentAction == MODE_RETREAT ) { currentOrder = guiOrderFactory.createGUIRetreat(); } else if( currentAction == MODE_DISBAND ) { currentOrder = guiOrderFactory.createGUIDisband(); } else if( currentAction == MODE_BUILD_ARMY ) { currentOrder = guiOrderFactory.createGUIBuild(); currentOrder.setParam( GUIBuild.BUILD_UNIT, Unit.Type.ARMY ); } else if( currentAction == MODE_BUILD_FLEET ) { currentOrder = guiOrderFactory.createGUIBuild(); currentOrder.setParam( GUIBuild.BUILD_UNIT, Unit.Type.FLEET ); } else if( currentAction == MODE_BUILD_WING ) { currentOrder = guiOrderFactory.createGUIBuild(); currentOrder.setParam( GUIBuild.BUILD_UNIT, Unit.Type.WING ); } else if( currentAction == MODE_REMOVE ) { currentOrder = guiOrderFactory.createGUIRemove(); } else if( currentAction == MODE_WAIVE_BUILD ) { currentOrder = guiOrderFactory.createGUIWaive(); } else { currentOrder = null; } if( loc != null && currentOrder != null ) { mouseOver( null, loc ); } } // setGUIOrder() // If there's an order in progress, set the cursor to show which type of unit you're // ordering and/or show that the current status is invalid for that unit. private void setUnitCursor( boolean ok ) { if( ok ) { if( Unit.Type.ARMY == currentOrder.getSourceUnitType() ) { mapPanel.setMapCursor( MapPanel.CURSOR_DRAG_ARMY ); } else if( Unit.Type.FLEET == currentOrder.getSourceUnitType() ) { mapPanel.setMapCursor( MapPanel.CURSOR_DRAG_FLEET ); } else if( Unit.Type.WING == currentOrder.getSourceUnitType() ) { mapPanel.setMapCursor( MapPanel.CURSOR_DRAG_WING ); } else { mapPanel.setMapCursor( MapPanel.DEFAULT_CURSOR ); } } else { if( Unit.Type.ARMY == currentOrder.getSourceUnitType() ) { mapPanel.setMapCursor( MapPanel.CURSOR_DRAG_ARMY_NO ); } else if( Unit.Type.FLEET == currentOrder.getSourceUnitType() ) { mapPanel.setMapCursor( MapPanel.CURSOR_DRAG_FLEET_NO ); } else if( Unit.Type.WING == currentOrder.getSourceUnitType() ) { mapPanel.setMapCursor( MapPanel.CURSOR_DRAG_WING_NO ); } else { mapPanel.setMapCursor( MapPanel.BAD_ACTION ); } } } // mouseOut() /** * Handle a null location. Assumes currentOrder != null. Sets pointer and * text. * * @since */ private void handleNullLoc() { // the order cannot be executed here -- but if it is in progress, it may be cancelled. if( currentOrder != null && currentOrder.getSourceUnitType() != null ) { setUnitCursor( false ); } else { mapPanel.setMapCursor( MapPanel.BAD_ACTION ); } sb.setLength( 0 ); sb.append( Utils.getLocalString( "OrdConBar.noprovince.nostart", currentOrder.getFullName() ) ); if( currentOrder.getCurrentLocationNum() >= 1 ) { // the order is 'in progress' and in a bad location. click to cancel sb.append(' '); sb.append( Utils.getLocalString( GUIOrder.CLICK_TO_CANCEL ) ); } mapPanel.getStatusBarUtils().setText( sb.toString() ); } /** * Handle 'cancel' events * * @since */ private void doCanceled() { if( currentOrder != null ) { mapPanel.setMapCursor( MapPanel.BAD_ACTION ); mapPanel.getStatusBarUtils().setText( Utils.getLocalString( GUIOrder.CANCELED, currentOrder.getFullName() ) ); currentOrder = null; setGUIOrder( lastLoc ); // create a new GUIOrder } } /** * Layout the ControlBar * * @since */ private void makeLayout() { // phase determines groups String[] text = null; String[] cmd = null; String defaultButton = null; Phase.PhaseType phaseType = stateInfo.getTurnState().getPhase().getPhaseType(); Log.println("OCB:phase: ", stateInfo.getTurnState().getPhase()); if( phaseType == Phase.PhaseType.MOVEMENT ) { text = GROUP_MOVEMENT_TEXT; cmd = GROUP_MOVEMENT_CMD; charMap = GROUP_MOVEMENT_CHARCODES; defaultButton = GROUP_MOVEMENT_DEFAULT_BUTTON; } else if( phaseType == Phase.PhaseType.RETREAT ) { text = GROUP_RETREAT_TEXT; cmd = GROUP_RETREAT_CMD; charMap = GROUP_RETREAT_CHARCODES; defaultButton = GROUP_RETREAT_DEFAULT_BUTTON; } else if( phaseType == Phase.PhaseType.ADJUSTMENT ) { text = GROUP_ADJUSTMENT_TEXT; cmd = GROUP_ADJUSTMENT_CMD; charMap = GROUP_ADJUSTMENT_CHARCODES; defaultButton = GROUP_ADJUSTMENT_DEFAULT_BUTTON; // filter out 'wing' button, if wings are NOT enabled. RuleOptions ro = mapPanel.getWorld().getRuleOptions(); if( ro.getOptionValue( RuleOptions.OPTION_WINGS ) != RuleOptions.VALUE_WINGS_ENABLED ) { text = new String[GROUP_ADJUSTMENT_TEXT.length - 1]; cmd = new String[GROUP_ADJUSTMENT_CMD.length - 1]; charMap = new int[GROUP_ADJUSTMENT_CHARCODES.length - 1]; int idx = 0; for( int i = 0; i < GROUP_ADJUSTMENT_CMD.length; i++ ) { if( GROUP_ADJUSTMENT_CMD[i] != MODE_BUILD_WING ) { text[idx] = GROUP_ADJUSTMENT_TEXT[i]; cmd[idx] = GROUP_ADJUSTMENT_CMD[i]; charMap[idx] = GROUP_ADJUSTMENT_CHARCODES[i]; idx++; } } } } else { throw new IllegalArgumentException( "Unknown Phase: " + phaseType ); } // spacer addSeparator(); // listener ToggleListener tl = new ToggleListener(); // create toggle buttons. // set the default button as selected buttons = new JToggleButton[text.length]; ButtonGroup bg = new ButtonGroup(); for( int i = 0; i < text.length; i++ ) { buttons[i] = new JToggleButton( text[i], false ); buttons[i].setActionCommand( cmd[i] ); buttons[i].addActionListener( tl ); // check for default button; if we are it, make it appear 'clicked' if( cmd[i] == defaultButton ) { buttons[i].doClick(); } bg.add( buttons[i] ); this.add( buttons[i] ); } } // makeLayout() /** * Listens for toggle events; sets which button is selected */ private class ToggleListener implements ActionListener { public void actionPerformed( ActionEvent e ) { defaultAction = ( (JToggleButton) e.getSource() ).getActionCommand(); currentAction = defaultAction; setGUIOrder( null ); } // actionPerformed() } // setUnitCursor }