// // @(#)EditControlBar.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.map; import dip.world.Province; import dip.world.Position; import dip.world.TurnState; import dip.world.Location; import dip.world.Phase; import dip.world.Unit; import dip.world.Power; import dip.world.Coast; import dip.world.RuleOptions; import dip.order.result.TimeResult; import dip.gui.OrderDisplayPanel; import dip.gui.order.GUIOrder; import dip.gui.undo.*; import dip.misc.Utils; import javax.swing.ButtonGroup; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JToggleButton; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import org.w3c.dom.events.MouseEvent; import org.apache.batik.dom.events.DOMKeyEvent; /** * Creates a ControlBar that allows units to be placed or removed, Supply Centers to * change ownership. * <p> * */ public class EditControlBar extends ViewControlBar { // i18n constants private static final String POWER_LABEL = "EdtConBar.label.power"; private static final String BUTTON_TEXT_ARMY = "EdtConBar.button.text.army"; private static final String BUTTON_TEXT_FLEET = "EdtConBar.button.text.fleet"; private static final String BUTTON_TEXT_WING = "EdtConBar.button.text.wing"; private static final String BUTTON_TEXT_SC = "EdtConBar.button.text.sc"; private static final String BUTTON_TEXT_REMOVE = "EdtConBar.button.text.remove"; private static final String TOOLTIP_ARMY = "EdtConBar.tooltip.army"; private static final String TOOLTIP_FLEET = "EdtConBar.tooltip.fleet"; private static final String TOOLTIP_WING = "EdtConBar.tooltip.wing"; private static final String TOOLTIP_SC = "EdtConBar.tooltip.sc"; private static final String TOOLTIP_REMOVE = "EdtConBar.tooltip.remove"; private static final String BUTTON_TEXT_DISLODGED= "EdtConBar.button.text.dislodged"; private static final String TOOLTIP_DISLODGED = "EdtConBar.tooltip.dislodged"; private static final String EDIT_TIME_STAMP_MSG = "TimeResult.game.edited"; private static final String ERR_NO_ARMY_IN_SEA = "EdtConBar.err.noarmyinsea"; private static final String ERR_NO_FLEET_IN_LANDLOCKED = "EdtConBar.err.nofleetland"; private static final String ERR_NO_UNIT_TO_REMOVE = "EdtConBar.err.nounit_remove"; private static final String ERR_NO_SC = "EdtConBar.err.no_sc"; private static final String CLICK_TO_SET_SC = "EdtConBar.click.set_sc"; private static final String CLICK_TO_REMOVE = "EdtConBar.click.remove"; private static final String CLICK_TO_ADD_FLEET = "EdtConBar.click.add_fleet"; private static final String CLICK_TO_ADD_ARMY = "EdtConBar.click.add_army"; private static final String CLICK_TO_ADD_WING = "EdtConBar.click.add_wing"; /* no longer used private static final String ERR_DISLODGED_UNIT_MUST_BE_REMOVED = "EdtConBar.err.remove_dislodged"; private static final String ERR_UNIT_MUST_BE_REMOVED = "EdtConBar.err.remove_unit"; private static final String ERR_FLEET_MUST_BE_ON_COAST = "EdtConBar.err.fleetcoastal"; private static final String ERR_NO_DISLODGED_UNIT_TO_REMOVE = "EdtConBar.err.nounit_remove_dislodged"; private static final String CLICK_TO_REMOVE_DISLODGED = "EdtConBar.click.remove_dislodged"; private static final String CLICK_TO_ADD_DISLODGED_FLEET = "EdtConBar.click.add_dislodged_fleet"; private static final String CLICK_TO_ADD_DISLODGED_ARMY = "EdtConBar.click.add_dislodged_army"; private static final String CLICK_TO_ADD_DISLODGED_WING = "EdtConBar.click.add_dislodged_wing"; */ // 'no power' list item private static final String POWER_NONE = Utils.getLocalString("EdtConBar.list.nopower"); // instance variables private static java.awt.Cursor defaultCursor; private final Position position; private final TurnState turnState; private final UndoRedoManager undoManager; private final OrderDisplayPanel orderDisplayPanel; private Power currentPower = null; private JToggleButton selectedButton = null; private boolean didEdit = false; // Mouse button handling private String currentAction = null; private String defaultAction = null; // GUI elements private JComboBox powerBox; private JToggleButton bSC; private JToggleButton bArmy; private JToggleButton bFleet; private JToggleButton bWing; // may be null private JToggleButton bRemove; private JCheckBox cbDislodged; // not available if not in retreat phase private ButtonGroup bg; /** Create an EditControlBar */ public EditControlBar(MapPanel mp) { super(mp); defaultCursor = java.awt.Cursor.getDefaultCursor(); position = mapPanel.getPosition(); turnState = mapPanel.getTurnState(); orderDisplayPanel = mapPanel.getClientFrame().getOrderDisplayPanel(); undoManager = mapPanel.getClientFrame().getUndoRedoManager(); makeLayout(); }// ViewControlBar() /** Do the layout */ private void makeLayout() { addSeparator(); addSeparator(new Dimension(10,0)); // create power label, and create combo box with all powers + "none" add(new JLabel(Utils.getLocalString(POWER_LABEL))); addSeparator(new Dimension(5,0)); powerBox = new JComboBox(mapPanel.getClientFrame().getWorld().getMap().getPowers()); powerBox.insertItemAt(POWER_NONE, 0); powerBox.setEditable(false); powerBox.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent e) { currentPower = getSelectedPower(); if(currentPower == null) { // deselect & disable Army / Fleet buttons bArmy.setEnabled(false); bFleet.setEnabled(false); mapPanel.getJSVGCanvas().setCursor(defaultCursor); } else { bArmy.setEnabled(true); bFleet.setEnabled(true); } }// itemStateChanged() }); powerBox.setMaximumSize(powerBox.getPreferredSize()); add(powerBox); // Toggle-Button Listener ToggleListener tl = new ToggleListener(); // button group bg = new ButtonGroup(); // create buttons addSeparator(new Dimension(10,0)); bArmy = new JToggleButton(Utils.getLocalString(BUTTON_TEXT_ARMY)); bArmy.setToolTipText(Utils.getLocalString(TOOLTIP_ARMY)); bArmy.addActionListener(tl); add(bArmy); bg.add(bArmy); bFleet = new JToggleButton(Utils.getLocalString(BUTTON_TEXT_FLEET)); bFleet.setToolTipText(Utils.getLocalString(TOOLTIP_FLEET)); bFleet.addActionListener(tl); add(bFleet); bg.add(bFleet); // if WING units enabled, add a WING unit button RuleOptions ro = mapPanel.getWorld().getRuleOptions(); if(ro.getOptionValue(RuleOptions.OPTION_WINGS) == RuleOptions.VALUE_WINGS_ENABLED) { bWing = new JToggleButton(Utils.getLocalString(BUTTON_TEXT_WING)); bWing.setToolTipText(Utils.getLocalString(TOOLTIP_WING)); bWing.addActionListener(tl); add(bWing); bg.add(bWing); } bSC = new JToggleButton(Utils.getLocalString(BUTTON_TEXT_SC)); bSC.setToolTipText(Utils.getLocalString(TOOLTIP_SC)); bSC.addActionListener(tl); add(bSC); bg.add(bSC); bRemove = new JToggleButton(Utils.getLocalString(BUTTON_TEXT_REMOVE)); bRemove.setToolTipText(Utils.getLocalString(TOOLTIP_REMOVE)); bRemove.addActionListener(tl); add(bRemove); bg.add(bRemove); cbDislodged = new JCheckBox(Utils.getLocalString(BUTTON_TEXT_DISLODGED), false); cbDislodged.setToolTipText(Utils.getLocalString(TOOLTIP_DISLODGED)); addSeparator(new Dimension(5,0)); // do not add dislodged if we are not in a retreat phase. if(turnState.getPhase().getPhaseType() == Phase.PhaseType.RETREAT) { addSeparator(); addSeparator(new Dimension(5,0)); add(cbDislodged); } // set current Power currentPower = getSelectedPower(); }// makeLayout() /** Get the selected power from the combo box, null if "none" selected. */ private Power getSelectedPower() { Object obj = powerBox.getSelectedItem(); if(obj == POWER_NONE) { return null; } else { return (Power) obj; } }// getSelectedPower(); /** * Determines if a Province is click-worthy, depending upon the selected mode. * <p> * Adds a brief status bar message as to why a click will or will not be accepted. * * */ public void mouseOver(MouseEvent me, Location loc) { // by default, can't accept mapPanel.getJSVGCanvas().setCursor(MapPanel.BAD_ACTION); // bad location if(loc == null) { mapPanel.getStatusBarUtils().setText(Utils.getLocalString(GUIOrder.NOT_IN_PROVINCE)); return; } if(currentAction != null) { if( checkValidity(loc) ) { mapPanel.getJSVGCanvas().setCursor(defaultCursor); mapPanel.getStatusBarUtils().displayProvinceInfo( loc, Utils.getLocalString(currentAction) ); } } else { mapPanel.getStatusBarUtils().displayProvinceInfo( loc ); } }// mouseOver() /** Handles mouseOut() */ public void mouseOut(MouseEvent me, Location loc) { mapPanel.getStatusBar().clearText(); mapPanel.getJSVGCanvas().setCursor(defaultCursor); }// mouseOver() /** Handle mouse clicks on the map */ public void mouseClicked(MouseEvent me, Location loc) { if(loc!=null) { final short button = me.getButton(); if(button == DOMUIEventListener.BUTTON_RIGHT) { // make RMB add the 'other unit' that is selected, // if an army or fleet is selected. if a wing or anything // else is selected, RMB doesn't apply if(currentAction == CLICK_TO_ADD_FLEET) { currentAction = CLICK_TO_ADD_ARMY; } else if(currentAction == CLICK_TO_ADD_ARMY) { currentAction = CLICK_TO_ADD_FLEET; } else { currentAction = defaultAction; } } else if(button == DOMUIEventListener.BUTTON_MIDDLE) { currentAction = CLICK_TO_REMOVE; } else { currentAction = defaultAction; } doAction(me, loc); } }// mouseClicked() public void doAction(MouseEvent me, Location loc) { // bad location if(loc == null) { mapPanel.getStatusBarUtils().setText(Utils.getLocalString(GUIOrder.NOT_IN_PROVINCE)); return; } if(checkValidity(loc)) { Province province = loc.getProvince(); // Removes first if(currentAction == CLICK_TO_ADD_FLEET || currentAction == CLICK_TO_ADD_ARMY || currentAction == CLICK_TO_ADD_WING || currentAction == CLICK_TO_REMOVE) { if(hasUnit(loc.getProvince())) { // get old unit Unit oldUnit = (isDislodged()) ? position.getDislodgedUnit(province) : position.getUnit(province); // remove an army or fleet removeUnit(province, isDislodged()); undoManager.addEdit(new UndoEditRemoveUnit(undoManager, position, province, oldUnit, isDislodged())); } } if(currentAction == CLICK_TO_ADD_ARMY) { // add an army Unit army = new Unit(currentPower, Unit.Type.ARMY); army.setCoast(Coast.NONE); addUnit(province, army, isDislodged()); undoManager.addEdit(new UndoEditAddUnit(undoManager, position, province, army, isDislodged())); } if(currentAction == CLICK_TO_ADD_FLEET) { // add a fleet Unit fleet = new Unit(currentPower, Unit.Type.FLEET); if(province.isMultiCoastal()) { Coast coast = loc.getCoast(); if(coast.isDirectional()) { fleet.setCoast(coast); } else { return; } } else { fleet.setCoast(Coast.SINGLE); } addUnit(province, fleet, isDislodged()); undoManager.addEdit(new UndoEditAddUnit(undoManager, position, province, fleet, isDislodged())); } if(currentAction == CLICK_TO_ADD_WING) { // add a Wing Unit wing = new Unit(currentPower, Unit.Type.WING); wing.setCoast(Coast.WING); addUnit(province, wing, isDislodged()); undoManager.addEdit(new UndoEditAddUnit(undoManager, position, province, wing, isDislodged())); } if(currentAction == CLICK_TO_SET_SC) { // change supply center ownership Power oldPower = position.getSupplyCenterOwner(province); changeSCOwner(province, currentPower); undoManager.addEdit(new UndoEditSCOwner(undoManager, position, province, oldPower, currentPower)); } } // add edit result if we haven't already if(!didEdit) { turnState.getResultList().add(new TimeResult(EDIT_TIME_STAMP_MSG)); didEdit = true; } // re-call mouseOver to avoid successive clicks mouseOver(me, loc); }// mouseClicked() /** Add a unit to a province; does not generate an undo/redo event. Revalidates orders. */ public void addUnit(Province province, Unit unit, boolean isDislodged) { if(isDislodged) { position.setDislodgedUnit(province, unit); } else { position.setUnit(province, unit); } update(province); orderDisplayPanel.revalidateAllOrders(); }// addUnit() /** Remove a unit from a province; does not generate an undo/redo event. Revalidates orders. */ public void removeUnit(Province province, boolean isDislodged) { if(isDislodged) { position.setDislodgedUnit(province, null); } else { position.setUnit(province, null); } update(province); orderDisplayPanel.revalidateAllOrders(); }// removeUnit() /** Change the supply center owner of a province; does not generate an undo/redo event. Revalidates orders. */ public void changeSCOwner(Province province, Power newPower) { position.setSupplyCenterOwner(province, newPower); update(province); orderDisplayPanel.revalidateAllOrders(); }// changeSCOwner() /** re-render SVG, set changed flag on game state */ private void update(Province province) { mapPanel.getClientFrame().fireStateModified(); mapPanel.updateProvince(province); }// update() /** convenience method to check dislodged checkbox */ private boolean isDislodged() { return cbDislodged.isSelected(); }// isDislodged() /** convenience method to check if a Province has a unit (or dislodged unit, if isDislodged()==true) */ private boolean hasUnit(Province p) { if(isDislodged()) { return position.hasDislodgedUnit(p); } else { return position.hasUnit(p); } }// hasUnit() /** Listens for toggle events; sets which button is selected */ private class ToggleListener implements ActionListener { public void actionPerformed(ActionEvent e) { selectedButton = (JToggleButton) e.getSource(); if(selectedButton == bArmy) { defaultAction = CLICK_TO_ADD_ARMY; } else if(selectedButton == bWing) { defaultAction = CLICK_TO_ADD_WING; } else if(selectedButton == bFleet) { defaultAction = CLICK_TO_ADD_FLEET; } else if(selectedButton == bRemove) { powerBox.setSelectedItem(POWER_NONE); defaultAction = CLICK_TO_REMOVE; } else if(selectedButton == bSC) { defaultAction = CLICK_TO_SET_SC; } currentAction = defaultAction; // disable dislodged checkbox, iff Supply Center button selected, // since supply center ownership has no relationship to dislodged if(selectedButton == bSC) { cbDislodged.setEnabled(false); } else { cbDislodged.setEnabled(true); } }// actionPerformed() }// inner class ToggleListener public boolean checkValidity(Location loc) { // determine validity Province province = loc.getProvince(); if(currentAction == CLICK_TO_ADD_ARMY && currentPower != null) { if(province.isSea()) { mapPanel.statusBarUtils.displayProvinceInfo(loc, Utils.getLocalString(ERR_NO_ARMY_IN_SEA)); return false; } else { return true; } } else if(currentAction == CLICK_TO_ADD_FLEET && currentPower != null) { if(province.isLandLocked()) { mapPanel.statusBarUtils.displayProvinceInfo(loc, Utils.getLocalString(ERR_NO_FLEET_IN_LANDLOCKED)); return false; } else { return true; } } else if(currentAction == CLICK_TO_ADD_WING && currentPower != null) { return true; } else if(currentAction == CLICK_TO_SET_SC) { if(province.hasSupplyCenter()) { return true; } else { mapPanel.statusBarUtils.displayProvinceInfo(loc, Utils.getLocalString(ERR_NO_SC)); return false; } } else if(currentAction == CLICK_TO_REMOVE) { if(position.hasUnit(province)) { return true; } else { mapPanel.statusBarUtils.displayProvinceInfo(loc, Utils.getLocalString(ERR_NO_UNIT_TO_REMOVE)); return false; } } return false; }// checkValidity() }// EditControlBar