mirror of https://github.com/FreeCol/freecol.git
2324 lines
78 KiB
Java
2324 lines
78 KiB
Java
/**
|
|
* Copyright (C) 2002-2022 The FreeCol Team
|
|
*
|
|
* This file is part of FreeCol.
|
|
*
|
|
* FreeCol 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.
|
|
*
|
|
* FreeCol 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 FreeCol. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
package net.sf.freecol.client.gui.panel;
|
|
|
|
import static net.sf.freecol.common.util.CollectionUtils.dump;
|
|
import static net.sf.freecol.common.util.CollectionUtils.sort;
|
|
import static net.sf.freecol.common.util.CollectionUtils.transform;
|
|
|
|
import java.awt.Color;
|
|
import java.awt.Component;
|
|
import java.awt.Container;
|
|
import java.awt.Dimension;
|
|
import java.awt.FlowLayout;
|
|
import java.awt.Font;
|
|
import java.awt.Graphics;
|
|
import java.awt.Graphics2D;
|
|
import java.awt.GridLayout;
|
|
import java.awt.event.ActionEvent;
|
|
import java.awt.event.ActionListener;
|
|
import java.awt.event.KeyEvent;
|
|
import java.awt.event.MouseAdapter;
|
|
import java.awt.event.MouseEvent;
|
|
import java.awt.event.MouseListener;
|
|
import java.beans.PropertyChangeEvent;
|
|
import java.beans.PropertyChangeListener;
|
|
import java.util.List;
|
|
import java.util.logging.Level;
|
|
import java.util.logging.Logger;
|
|
|
|
import javax.swing.AbstractAction;
|
|
import javax.swing.BorderFactory;
|
|
import javax.swing.ComponentInputMap;
|
|
import javax.swing.ImageIcon;
|
|
import javax.swing.InputMap;
|
|
import javax.swing.JButton;
|
|
import javax.swing.JComboBox;
|
|
import javax.swing.JComponent;
|
|
import javax.swing.JLabel;
|
|
import javax.swing.JMenuItem;
|
|
import javax.swing.JPanel;
|
|
import javax.swing.JPopupMenu;
|
|
import javax.swing.JScrollPane;
|
|
import javax.swing.JToolTip;
|
|
import javax.swing.KeyStroke;
|
|
import javax.swing.ScrollPaneConstants;
|
|
import javax.swing.SwingUtilities;
|
|
|
|
import net.miginfocom.swing.MigLayout;
|
|
import net.sf.freecol.client.ClientOptions;
|
|
import net.sf.freecol.client.FreeColClient;
|
|
import net.sf.freecol.client.gui.FontLibrary;
|
|
import net.sf.freecol.client.gui.GUI;
|
|
import net.sf.freecol.client.gui.ImageLibrary;
|
|
import net.sf.freecol.client.gui.label.GoodsLabel;
|
|
import net.sf.freecol.client.gui.label.ProductionLabel;
|
|
import net.sf.freecol.client.gui.label.UnitLabel;
|
|
import net.sf.freecol.client.gui.tooltip.RebelToolTip;
|
|
import net.sf.freecol.common.debug.DebugUtils;
|
|
import net.sf.freecol.common.debug.FreeColDebugger;
|
|
import net.sf.freecol.common.i18n.Messages;
|
|
import net.sf.freecol.common.model.AbstractGoods;
|
|
import net.sf.freecol.common.model.BuildableType;
|
|
import net.sf.freecol.common.model.Building;
|
|
import net.sf.freecol.common.model.Colony;
|
|
import net.sf.freecol.common.model.Colony.ColonyChangeEvent;
|
|
import net.sf.freecol.common.model.ColonyTile;
|
|
import net.sf.freecol.common.model.Direction;
|
|
import net.sf.freecol.common.model.FreeColGameObject;
|
|
import net.sf.freecol.common.model.Game;
|
|
import net.sf.freecol.common.model.Goods;
|
|
import net.sf.freecol.common.model.GoodsType;
|
|
import net.sf.freecol.common.model.ModelMessage;
|
|
import net.sf.freecol.common.model.Nation;
|
|
import net.sf.freecol.common.model.Player;
|
|
import net.sf.freecol.common.model.Player.NoClaimReason;
|
|
import net.sf.freecol.common.model.ProductionInfo;
|
|
import net.sf.freecol.common.model.Specification;
|
|
import net.sf.freecol.common.model.StringTemplate;
|
|
import net.sf.freecol.common.model.Tile;
|
|
import net.sf.freecol.common.model.Unit;
|
|
import net.sf.freecol.common.model.UnitLocation.NoAddReason;
|
|
import net.sf.freecol.common.model.UnitType;
|
|
import net.sf.freecol.common.model.WorkLocation;
|
|
|
|
|
|
/**
|
|
* This is a panel for the Colony display. It shows the units that
|
|
* are working in the colony, the buildings and much more.
|
|
* <p>
|
|
* Beware that in debug mode, this might be a server-side version of
|
|
* the colony which is why we need to call getColony().getSpecification()
|
|
* to get the spec that corresponds to the good types in this colony.
|
|
* <p>
|
|
* Panel Layout:
|
|
* <p style="display: block; font-family: monospace; white-space: pre; margin: 1em 0;">
|
|
* |--------------------------------------------------------|
|
|
* | nameBox | netProductionPanel |
|
|
* |--------------------------------------------------------|
|
|
* | tilesPanel | buildingsPanel |
|
|
* |-------------------| |
|
|
* | populationPanel | |
|
|
* |-------------------| |
|
|
* | constructionPanel | |
|
|
* |--------------------------------------------------------|
|
|
* | inPortPanel | cargoPanel | outsideColonyPanel |
|
|
* |--------------------------------------------------------|
|
|
* | warehousePanel |
|
|
* |--------------------------------------------------------|
|
|
*/
|
|
public final class ColonyPanel extends PortPanel
|
|
implements PropertyChangeListener {
|
|
|
|
private static final Logger logger = Logger.getLogger(ColonyPanel.class.getName());
|
|
|
|
/** The height of the area in which autoscrolling should happen. */
|
|
public static final int SCROLL_AREA_HEIGHT = 40;
|
|
|
|
/** The speed of the scrolling. */
|
|
public static final int SCROLL_SPEED = 40;
|
|
|
|
// Buttons
|
|
private JButton unloadButton
|
|
= Utility.localizedButton("unload");
|
|
|
|
private JButton fillButton
|
|
= Utility.localizedButton("load");
|
|
|
|
private JButton warehouseButton
|
|
= Utility.localizedButton("colonyPanel.warehouse");
|
|
|
|
private JButton buildQueueButton
|
|
= Utility.localizedButton("colonyPanel.buildQueue");
|
|
|
|
private JButton colonyUnitsButton
|
|
= Utility.localizedButton("colonyPanel.colonyUnits");
|
|
|
|
// Only present in debug mode
|
|
private JButton setGoodsButton = null;
|
|
private JButton traceWorkButton = null;
|
|
|
|
/** The {@code Colony} this panel is displaying. */
|
|
private Colony colony;
|
|
|
|
// inherit PortPanel.pressListener
|
|
// inherit PortPanel.defaultTransferHandler
|
|
// inherit PortPanel.selectedUnitLabel
|
|
|
|
private transient MouseListener releaseListener = null;
|
|
|
|
// Subparts
|
|
private final JComboBox<Colony> nameBox = new JComboBox<>();
|
|
|
|
private JPanel netProductionPanel = null;
|
|
|
|
private JScrollPane buildingsScroll = null;
|
|
private BuildingsPanel buildingsPanel = null;
|
|
|
|
private JScrollPane cargoScroll = null;
|
|
// inherit protected PortPanel.cargoPanel
|
|
|
|
private ConstructionPanel constructionPanel = null;
|
|
|
|
private JScrollPane inPortScroll = null;
|
|
// inherit protected PortPanel.inPortPanel
|
|
|
|
private JScrollPane outsideColonyScroll = null;
|
|
private OutsideColonyPanel outsideColonyPanel = null;
|
|
|
|
private PopulationPanel populationPanel = null;
|
|
|
|
private JScrollPane tilesScroll = null;
|
|
private TilesPanel tilesPanel = null;
|
|
|
|
private JScrollPane warehouseScroll = null;
|
|
private WarehousePanel warehousePanel = null;
|
|
|
|
// The action commands
|
|
|
|
private final ActionListener unloadCmd = ae -> {
|
|
final Unit unit = getSelectedUnit();
|
|
if (unit == null || !unit.isCarrier()) return;
|
|
for (Goods goods : unit.getGoodsList()) {
|
|
igc().unloadCargo(goods, false);
|
|
}
|
|
for (Unit u : unit.getUnitList()) {
|
|
igc().leaveShip(u);
|
|
}
|
|
cargoPanel.update();
|
|
updateOutsideColonyPanel();
|
|
unloadButton.setEnabled(false);
|
|
fillButton.setEnabled(false);
|
|
};
|
|
|
|
private final ActionListener warehouseCmd = ae -> {
|
|
getGUI().showWarehouseDialog(getColony(), updated -> {
|
|
if (updated) {
|
|
updateWarehousePanel();
|
|
}
|
|
});
|
|
};
|
|
|
|
private final ActionListener buildQueueCmd = ae -> {
|
|
getGUI().showBuildQueuePanel(getColony());
|
|
updateConstructionPanel();
|
|
};
|
|
|
|
private final ActionListener fillCmd = ae -> {
|
|
final Unit unit = getSelectedUnit();
|
|
if (unit == null || !unit.isCarrier()) return;
|
|
final Colony colony = getColony();
|
|
for (Goods goods : unit.getCompactGoodsList()) {
|
|
final GoodsType type = goods.getType();
|
|
int space = unit.getLoadableAmount(type);
|
|
int count = colony.getGoodsCount(type);
|
|
if (space > 0 && count > 0) {
|
|
int n = Math.min(space, count);
|
|
igc().loadCargo(new Goods(goods.getGame(), colony, type, n),
|
|
unit);
|
|
}
|
|
}
|
|
};
|
|
|
|
private final ActionListener colonyUnitsCmd =
|
|
ae -> generateColonyUnitsMenu();
|
|
|
|
private final ActionListener setGoodsCmd = ae -> {
|
|
DebugUtils.setColonyGoods(getFreeColClient(), colony);
|
|
updateWarehousePanel();
|
|
updateProduction();
|
|
};
|
|
|
|
private final ActionListener occupationCmd =
|
|
ae -> colony.setOccupationTrace(!colony.getOccupationTrace());
|
|
|
|
|
|
/**
|
|
* The constructor for the panel.
|
|
*
|
|
* @param freeColClient The {@code FreeColClient} for the game.
|
|
* @param colony The {@code Colony} to display in this panel.
|
|
*/
|
|
public ColonyPanel(FreeColClient freeColClient, Colony colony) {
|
|
super(freeColClient, new MigLayout());
|
|
|
|
getMigLayout().setLayoutConstraints("fill, wrap 2, insets 2");
|
|
getMigLayout().setColumnConstraints("[" + getTilesScrollGuiScaledDimension().width + "px!][fill]");
|
|
getMigLayout().setRowConstraints("[growprio 100,shrinkprio 10][]0[]0[]"
|
|
+ "[growprio 150,shrinkprio 50]"
|
|
+ "[][]");
|
|
|
|
final Player player = getMyPlayer();
|
|
// Do not just use colony.getOwner() == getMyPlayer() because
|
|
// in debug mode we are in the *server* colony, and the equality
|
|
// will fail.
|
|
editable = colony.getOwner().getId().equals(player.getId());
|
|
|
|
// Only enable the set goods button in debug mode when not spying
|
|
if (editable
|
|
&& FreeColDebugger.isInDebugMode(FreeColDebugger.DebugMode.MENUS)) {
|
|
setGoodsButton = Utility.localizedButton("colonyPanel.setGoods");
|
|
traceWorkButton = Utility.localizedButton("colonyPanel.traceWork");
|
|
}
|
|
|
|
// Use ESCAPE for closing the ColonyPanel:
|
|
InputMap closeIM = new ComponentInputMap(okButton);
|
|
closeIM.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false),
|
|
"pressed");
|
|
closeIM.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true),
|
|
"released");
|
|
SwingUtilities.replaceUIInputMap(okButton,
|
|
JComponent.WHEN_IN_FOCUSED_WINDOW, closeIM);
|
|
okButton.setText(Messages.message("close"));
|
|
|
|
InputMap unloadIM = new ComponentInputMap(unloadButton);
|
|
unloadIM.put(KeyStroke.getKeyStroke(KeyEvent.VK_U, 0, false),
|
|
"pressed");
|
|
unloadIM.put(KeyStroke.getKeyStroke(KeyEvent.VK_U, 0, true),
|
|
"released");
|
|
SwingUtilities.replaceUIInputMap(unloadButton,
|
|
JComponent.WHEN_IN_FOCUSED_WINDOW, unloadIM);
|
|
|
|
InputMap fillIM = new ComponentInputMap(fillButton);
|
|
fillIM.put(KeyStroke.getKeyStroke(KeyEvent.VK_L, 0, false),
|
|
"pressed");
|
|
fillIM.put(KeyStroke.getKeyStroke(KeyEvent.VK_L, 0, true),
|
|
"released");
|
|
SwingUtilities.replaceUIInputMap(fillButton,
|
|
JComponent.WHEN_IN_FOCUSED_WINDOW, fillIM);
|
|
|
|
InputMap warehouseIM = new ComponentInputMap(warehouseButton);
|
|
warehouseIM.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false),
|
|
"pressed");
|
|
warehouseIM.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true),
|
|
"released");
|
|
SwingUtilities.replaceUIInputMap(warehouseButton,
|
|
JComponent.WHEN_IN_FOCUSED_WINDOW, warehouseIM);
|
|
|
|
InputMap buildQueueIM = new ComponentInputMap(buildQueueButton);
|
|
buildQueueIM.put(KeyStroke.getKeyStroke(KeyEvent.VK_B, 0, false),
|
|
"pressed");
|
|
buildQueueIM.put(KeyStroke.getKeyStroke(KeyEvent.VK_B, 0, true),
|
|
"released");
|
|
SwingUtilities.replaceUIInputMap(buildQueueButton,
|
|
JComponent.WHEN_IN_FOCUSED_WINDOW, buildQueueIM);
|
|
|
|
InputMap colonyUnitsIM = new ComponentInputMap(colonyUnitsButton);
|
|
colonyUnitsIM.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, 0, false),
|
|
"pressed");
|
|
colonyUnitsIM.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, 0, true),
|
|
"released");
|
|
SwingUtilities.replaceUIInputMap(colonyUnitsButton,
|
|
JComponent.WHEN_IN_FOCUSED_WINDOW, colonyUnitsIM);
|
|
|
|
defaultTransferHandler
|
|
= new DefaultTransferHandler(freeColClient, this);
|
|
pressListener = new DragListener(freeColClient, this);
|
|
releaseListener = new DropListener();
|
|
selectedUnitLabel = null;
|
|
|
|
// Make the colony label
|
|
StringBuilder sb = new StringBuilder(32);
|
|
String compat = colony.getName();
|
|
if (editable) {
|
|
for (Colony c : player.getColonyList()) {
|
|
this.nameBox.addItem(c);
|
|
sb.append(c.getName());
|
|
}
|
|
} else {
|
|
this.nameBox.addItem(colony);
|
|
sb.append(colony.getName());
|
|
}
|
|
Font nameBoxFont = FontLibrary.getScaledFont("header-plain-big", compat);
|
|
|
|
this.nameBox.setFont(nameBoxFont);
|
|
this.nameBox.setBorder(BorderFactory.createEmptyBorder());
|
|
this.nameBox.setSelectedItem(colony);
|
|
this.nameBox.getInputMap().put(KeyStroke.getKeyStroke("LEFT"),
|
|
"selectPrevious2");
|
|
this.nameBox.getInputMap().put(KeyStroke.getKeyStroke("RIGHT"),
|
|
"selectNext2");
|
|
|
|
netProductionPanel = new JPanel(new MigLayout("align center, center, nogrid"));
|
|
netProductionPanel.setOpaque(false);
|
|
|
|
buildingsPanel = new BuildingsPanel();
|
|
buildingsScroll = new JScrollPane(buildingsPanel,
|
|
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
|
|
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
|
buildingsScroll.getVerticalScrollBar().setUnitIncrement(16);
|
|
buildingsScroll.getViewport().setOpaque(false);
|
|
buildingsPanel.setOpaque(false);
|
|
buildingsScroll.setBorder(Utility.ETCHED_BORDER);
|
|
|
|
cargoPanel = new ColonyCargoPanel(freeColClient);
|
|
cargoScroll = new JScrollPane(cargoPanel,
|
|
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
|
|
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
|
cargoScroll.setBorder(Utility.ETCHED_BORDER);
|
|
|
|
constructionPanel = new ConstructionPanel(freeColClient, colony, true);
|
|
|
|
inPortPanel = new ColonyInPortPanel();
|
|
inPortScroll = new JScrollPane(inPortPanel);
|
|
inPortScroll.getVerticalScrollBar().setUnitIncrement(16);
|
|
inPortScroll.setBorder(Utility.ETCHED_BORDER);
|
|
|
|
outsideColonyPanel = new OutsideColonyPanel();
|
|
outsideColonyScroll = new JScrollPane(outsideColonyPanel,
|
|
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
|
|
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
|
outsideColonyScroll.getVerticalScrollBar().setUnitIncrement(16);
|
|
outsideColonyScroll.setBorder(Utility.ETCHED_BORDER);
|
|
|
|
populationPanel = new PopulationPanel();
|
|
|
|
tilesPanel = new TilesPanel();
|
|
tilesScroll = new JScrollPane(tilesPanel,
|
|
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
|
|
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
|
tilesScroll.setBorder(Utility.BEVEL_BORDER);
|
|
|
|
warehousePanel = new WarehousePanel();
|
|
warehouseScroll = new JScrollPane(warehousePanel,
|
|
ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER,
|
|
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
|
|
warehouseScroll.setBorder(Utility.ETCHED_BORDER);
|
|
|
|
InputMap nameIM = new ComponentInputMap(this.nameBox);
|
|
nameIM.put(KeyStroke.getKeyStroke("LEFT"), "selectPrevious2");
|
|
nameIM.put(KeyStroke.getKeyStroke("RIGHT"), "selectNext2");
|
|
SwingUtilities.replaceUIInputMap(this.nameBox,
|
|
JComponent.WHEN_IN_FOCUSED_WINDOW, nameIM);
|
|
|
|
if (getGUI().getMapViewDimension().height < 770) {
|
|
/*
|
|
* Reduces the border size so that the Colony panel
|
|
* can get slightly bigger.
|
|
*/
|
|
setBorder(FreeColImageBorder.panelWithoutShadowBorder);
|
|
}
|
|
|
|
initialize(colony);
|
|
getGUI().restoreSavedSize(this, new Dimension(1050, 675));
|
|
|
|
setEscapeAction(new AbstractAction() {
|
|
@Override
|
|
public void actionPerformed(ActionEvent ae) {
|
|
okButton.doClick();
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
/**
|
|
* Set the current colony.
|
|
*
|
|
* @param colony The new colony value.
|
|
*/
|
|
private synchronized void setColony(Colony colony) {
|
|
this.colony = colony;
|
|
}
|
|
|
|
private Dimension getTilesScrollGuiScaledDimension() {
|
|
final int tilesScrollWidth = 3 * getImageLibrary().getTileSize().width;
|
|
final int tilesScrollHeight = 3 * getImageLibrary().getTileSize().height;
|
|
return new Dimension(tilesScrollWidth, tilesScrollHeight);
|
|
}
|
|
|
|
/**
|
|
* Initialize the entire panel.
|
|
*
|
|
* We can arrive here normally when a colony panel is created,
|
|
* or when an existing colony panel is changed via the colony name
|
|
* menu in the nameBox.
|
|
*
|
|
* @param colony The {@code Colony} to be displayed.
|
|
*/
|
|
private void initialize(Colony colony) {
|
|
cleanup();
|
|
|
|
setColony(colony);
|
|
|
|
addPropertyChangeListeners();
|
|
addMouseListeners();
|
|
setTransferHandlers(isEditable());
|
|
|
|
// Enable/disable widgets
|
|
unloadButton.addActionListener(unloadCmd);
|
|
fillButton.addActionListener(fillCmd);
|
|
warehouseButton.addActionListener(warehouseCmd);
|
|
buildQueueButton.addActionListener(buildQueueCmd);
|
|
colonyUnitsButton.addActionListener(colonyUnitsCmd);
|
|
if (setGoodsButton != null) {
|
|
setGoodsButton.addActionListener(setGoodsCmd);
|
|
}
|
|
if (traceWorkButton != null) {
|
|
traceWorkButton.addActionListener(occupationCmd);
|
|
}
|
|
|
|
unloadButton.setEnabled(isEditable());
|
|
fillButton.setEnabled(isEditable());
|
|
warehouseButton.setEnabled(isEditable());
|
|
buildQueueButton.setEnabled(isEditable());
|
|
colonyUnitsButton.setEnabled(isEditable());
|
|
if (setGoodsButton != null) {
|
|
setGoodsButton.setEnabled(isEditable());
|
|
}
|
|
if (traceWorkButton != null) {
|
|
traceWorkButton.setEnabled(isEditable());
|
|
}
|
|
|
|
final GUI gui = getGUI();
|
|
this.nameBox.setEnabled(isEditable());
|
|
this.nameBox.addActionListener((ActionEvent ae) -> {
|
|
final Colony newColony = (Colony)nameBox.getSelectedItem();
|
|
closeColonyPanel();
|
|
gui.showColonyPanel(newColony, null);
|
|
});
|
|
updateNetProductionPanel();
|
|
|
|
buildingsPanel.initialize();
|
|
cargoPanel.initialize();
|
|
constructionPanel.initialize();
|
|
inPortPanel.initialize();
|
|
outsideColonyPanel.initialize();
|
|
populationPanel.initialize();
|
|
tilesPanel.initialize();
|
|
warehousePanel.initialize();
|
|
|
|
final Dimension tilesScrollDimension = getTilesScrollGuiScaledDimension();
|
|
|
|
add(this.nameBox, "grow");
|
|
add(netProductionPanel, "grow");
|
|
add(tilesScroll, "width " + tilesScrollDimension.width + "px!, height " + tilesScrollDimension.height +"px!, top");
|
|
add(buildingsScroll, "span 1 3, grow");
|
|
add(populationPanel, "grow");
|
|
add(constructionPanel, "grow, top");
|
|
add(inPortScroll, "span, split 3, grow, sg, height 60:160:");
|
|
add(cargoScroll, "grow, sg, height 60:160:");
|
|
add(outsideColonyScroll, "grow, sg, height 60:160:");
|
|
add(warehouseScroll, "span, height " + (ImageLibrary.ICON_SIZE.height * 2) + "!, growx");
|
|
int buttonFields = 6;
|
|
if (setGoodsButton != null) buttonFields++;
|
|
if (traceWorkButton != null) buttonFields++;
|
|
add(unloadButton, "span, split " + Integer.toString(buttonFields)
|
|
+ ", align center");
|
|
add(fillButton);
|
|
add(warehouseButton);
|
|
add(buildQueueButton);
|
|
add(colonyUnitsButton);
|
|
if (setGoodsButton != null) add(setGoodsButton);
|
|
if (traceWorkButton != null) add(traceWorkButton);
|
|
add(okButton, "gapbefore push"); // tag ok
|
|
|
|
update();
|
|
}
|
|
|
|
/**
|
|
* Clean up this colony panel.
|
|
*/
|
|
private void cleanup() {
|
|
unloadButton.removeActionListener(unloadCmd);
|
|
fillButton.removeActionListener(fillCmd);
|
|
warehouseButton.removeActionListener(warehouseCmd);
|
|
buildQueueButton.removeActionListener(buildQueueCmd);
|
|
colonyUnitsButton.removeActionListener(colonyUnitsCmd);
|
|
if (setGoodsButton != null) {
|
|
setGoodsButton.removeActionListener(setGoodsCmd);
|
|
}
|
|
if (traceWorkButton != null) {
|
|
traceWorkButton.removeActionListener(occupationCmd);
|
|
}
|
|
|
|
removePropertyChangeListeners();
|
|
if (getSelectedUnit() != null) {
|
|
getSelectedUnit().removePropertyChangeListener(this);
|
|
}
|
|
removeMouseListeners();
|
|
for (MouseListener listener : getMouseListeners()) {
|
|
removeMouseListener(listener);
|
|
}
|
|
setTransferHandlers(false);
|
|
|
|
buildingsPanel.cleanup();
|
|
cargoPanel.cleanup();
|
|
constructionPanel.cleanup();
|
|
inPortPanel.cleanup();
|
|
outsideColonyPanel.cleanup();
|
|
populationPanel.cleanup();
|
|
tilesPanel.cleanup();
|
|
warehousePanel.cleanup();
|
|
|
|
removeAll();
|
|
}
|
|
|
|
private void addMouseListeners() {
|
|
if (isEditable()) {
|
|
cargoPanel.addMouseListener(releaseListener);
|
|
inPortPanel.addMouseListener(releaseListener);
|
|
outsideColonyPanel.addMouseListener(releaseListener);
|
|
warehousePanel.addMouseListener(releaseListener);
|
|
}
|
|
}
|
|
|
|
private void removeMouseListeners() {
|
|
cargoPanel.removeMouseListener(releaseListener);
|
|
inPortPanel.removeMouseListener(releaseListener);
|
|
outsideColonyPanel.removeMouseListener(releaseListener);
|
|
warehousePanel.removeMouseListener(releaseListener);
|
|
}
|
|
|
|
private void setTransferHandlers(boolean enable) {
|
|
DefaultTransferHandler dth = (enable) ? defaultTransferHandler : null;
|
|
cargoPanel.setTransferHandler(dth);
|
|
inPortPanel.setTransferHandler(dth);
|
|
outsideColonyPanel.setTransferHandler(dth);
|
|
warehousePanel.setTransferHandler(dth);
|
|
}
|
|
|
|
/**
|
|
* Add property change listeners needed by this ColonyPanel.
|
|
*/
|
|
private void addPropertyChangeListeners() {
|
|
final Colony colony = getColony();
|
|
if (colony != null) {
|
|
colony.addPropertyChangeListener(this);
|
|
colony.getGoodsContainer().addPropertyChangeListener(this);
|
|
colony.getTile().addPropertyChangeListener(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove the property change listeners of ColonyPanel.
|
|
*/
|
|
private void removePropertyChangeListeners() {
|
|
final Colony colony = getColony();
|
|
if (colony != null) {
|
|
colony.removePropertyChangeListener(this);
|
|
colony.getGoodsContainer().removePropertyChangeListener(this);
|
|
colony.getTile().removePropertyChangeListener(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update all the production-related panels.
|
|
*
|
|
* This has to be very broad as a change at one work location can
|
|
* have a secondary effect on another, and especially if the
|
|
* population hits a bonus boundary. A simple example is that
|
|
* adding extra lumber production may improve the production of the
|
|
* lumber mill. These changes can then flow on to production and
|
|
* construction displays.
|
|
*/
|
|
private void updateProduction() {
|
|
final Colony colony = getColony();
|
|
if (colony == null) return;
|
|
|
|
// Check for non-producing locations that can now produce.
|
|
for (WorkLocation wl : colony.getCurrentWorkLocationsList()) {
|
|
boolean change = false, check = wl.getProductionType() == null;
|
|
for (Unit unit : transform(wl.getUnits(), u ->
|
|
(check || !wl.produces(u.getWorkType())))) {
|
|
GoodsType workType = wl.getWorkFor(unit);
|
|
if (workType != null && workType != unit.getWorkType()) {
|
|
change |= igc().changeWorkType(unit, workType);
|
|
}
|
|
}
|
|
if (change) wl.updateProductionType();
|
|
}
|
|
|
|
updateTilesPanel();
|
|
updateBuildingsPanel();
|
|
updateNetProductionPanel();
|
|
updateConstructionPanel();
|
|
}
|
|
|
|
/**
|
|
* Update the entire colony panel.
|
|
*/
|
|
public void update() {
|
|
buildingsPanel.update();
|
|
constructionPanel.update();
|
|
inPortPanel.update();
|
|
updateNetProductionPanel();
|
|
outsideColonyPanel.update();
|
|
populationPanel.update();
|
|
tilesPanel.update();
|
|
warehousePanel.update();
|
|
}
|
|
|
|
/**
|
|
* Enables the unload and fill buttons if the currently selected unit is a
|
|
* carrier with some cargo.
|
|
*/
|
|
private void updateCarrierButtons() {
|
|
final Colony colony = getColony();
|
|
unloadButton.setEnabled(false);
|
|
fillButton.setEnabled(false);
|
|
if (selectedUnitLabel != null && isEditable()) {
|
|
Unit unit = selectedUnitLabel.getUnit();
|
|
if (unit != null && unit.isCarrier() && unit.hasCargo()) {
|
|
unloadButton.setEnabled(true);
|
|
for (Goods goods : unit.getCompactGoodsList()) {
|
|
if (colony.getGoodsCount(goods.getType()) > 0) {
|
|
fillButton.setEnabled(true);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generates a menu containing the units currently accessible
|
|
* from the Colony Panel allowing keyboard access to said units.
|
|
*/
|
|
private void generateColonyUnitsMenu() {
|
|
final FreeColClient freeColClient = getFreeColClient();
|
|
ImageLibrary lib = getImageLibrary();
|
|
final Colony colony = getColony();
|
|
JPopupMenu colonyUnitsMenu
|
|
= new JPopupMenu(Messages.message("colonyPanel.colonyUnits"));
|
|
ImageIcon unitIcon = null;
|
|
final QuickActionMenu unitMenu
|
|
= new QuickActionMenu(freeColClient, this);
|
|
Tile colonyTile = colony.getTile();
|
|
int unitNumber = 0;
|
|
JMenuItem subMenu = null;
|
|
|
|
for (final Unit unit : colony.getUnitList()) {
|
|
WorkLocation wl = unit.getWorkLocation();
|
|
GoodsType goodsType = unit.getWorkType();
|
|
Unit student = unit.getStudent();
|
|
|
|
unitIcon = new ImageIcon(lib.getSmallerUnitImage(unit));
|
|
StringBuilder sb = new StringBuilder(64);
|
|
String prodLabel = Messages.message("colonyPanel.producing");
|
|
if (student != null) {
|
|
sb.append(unit.getDescription())
|
|
.append(' ').append(prodLabel).append(' ')
|
|
.append(Messages.getName(unit.getType()
|
|
.getSkillTaught()))
|
|
.append(' ')
|
|
.append(unit.getTurnsOfTraining())
|
|
.append('/')
|
|
.append(unit.getNeededTurnsOfTraining());
|
|
} else if (wl != null && goodsType != null) {
|
|
int producing = wl.getProductionOf(unit, goodsType);
|
|
sb.append(unit.getDescription())
|
|
.append(' ').append(prodLabel).append(' ')
|
|
.append(producing)
|
|
.append(' ')
|
|
.append(Messages.message(StringTemplate.template(goodsType)
|
|
.addAmount("%amount%", producing)));
|
|
} else {
|
|
sb.append(unit.getDescription())
|
|
.append(' ').append(prodLabel).append(' ')
|
|
.append(Messages.message("nothing"));
|
|
}
|
|
String menuTitle = sb.toString();
|
|
subMenu = new JMenuItem(menuTitle, unitIcon);
|
|
subMenu.addActionListener((ActionEvent ae) -> {
|
|
unitMenu.addMenuItems(new UnitLabel(freeColClient, unit));
|
|
getGUI().showPopupMenu(unitMenu, 0, 0);
|
|
});
|
|
unitNumber++;
|
|
colonyUnitsMenu.add(subMenu);
|
|
}
|
|
colonyUnitsMenu.addSeparator();
|
|
for (final Unit unit : colonyTile.getUnitList()) {
|
|
if (unit.isCarrier()) {
|
|
unitIcon = new ImageIcon(lib.getSmallerUnitImage(unit));
|
|
String menuTitle = unit.getDescription()
|
|
+ " " + Messages.message("colonyPanel.inPort");
|
|
subMenu = new JMenuItem(menuTitle, unitIcon);
|
|
subMenu.addActionListener((ActionEvent ae) -> {
|
|
unitMenu.addMenuItems(new UnitLabel(freeColClient, unit));
|
|
getGUI().showPopupMenu(unitMenu, 0, 0);
|
|
});
|
|
unitNumber++;
|
|
colonyUnitsMenu.add(subMenu);
|
|
for (final Unit innerUnit : unit.getUnitList()) {
|
|
unitIcon = new ImageIcon(lib.getSmallerUnitImage(innerUnit));
|
|
menuTitle = innerUnit.getDescription()
|
|
+ " " + Messages.message("cargoOnCarrier")
|
|
+ " " + unit.getDescription();
|
|
subMenu = new JMenuItem(menuTitle, unitIcon);
|
|
subMenu.addActionListener((ActionEvent ae) -> {
|
|
unitMenu.addMenuItems(new UnitLabel(freeColClient, innerUnit));
|
|
getGUI().showPopupMenu(unitMenu, 0, 0);
|
|
});
|
|
unitNumber++;
|
|
colonyUnitsMenu.add(subMenu);
|
|
}
|
|
} else if (!unit.isOnCarrier()) {
|
|
unitIcon = new ImageIcon(lib.getSmallerUnitImage(unit));
|
|
String menuTitle = unit.getDescription()
|
|
+ " " + Messages.message("colonyPanel.outsideColony");
|
|
subMenu = new JMenuItem(menuTitle, unitIcon);
|
|
subMenu.addActionListener((ActionEvent ae) -> {
|
|
unitMenu.addMenuItems(new UnitLabel(freeColClient, unit));
|
|
getGUI().showPopupMenu(unitMenu, 0, 0);
|
|
});
|
|
unitNumber++;
|
|
colonyUnitsMenu.add(subMenu);
|
|
}
|
|
}
|
|
colonyUnitsMenu.addSeparator();
|
|
int elements = colonyUnitsMenu.getSubElements().length;
|
|
if (elements > 0) {
|
|
int lastIndex = colonyUnitsMenu.getComponentCount() - 1;
|
|
if (colonyUnitsMenu.getComponent(lastIndex) instanceof JPopupMenu.Separator) {
|
|
colonyUnitsMenu.remove(lastIndex);
|
|
}
|
|
}
|
|
getGUI().showPopupMenu(colonyUnitsMenu, 0, 0);
|
|
}
|
|
|
|
/**
|
|
* Try to assign a unit to work in a colony work location.
|
|
*
|
|
* @param unit The {@code Unit} that is to work.
|
|
* @param wl The {@code WorkLocation} to work in.
|
|
* @return True if the unit is now in the work location.
|
|
*/
|
|
private boolean tryWork(Unit unit, WorkLocation wl) {
|
|
// Choose the best work type.
|
|
GoodsType workType = wl.getWorkFor(unit);
|
|
|
|
// Set the unit to work. Note this might upgrade the unit,
|
|
// and possibly even change its work type as the server has
|
|
// the right to maintain consistency.
|
|
igc().work(unit, wl);
|
|
|
|
// Did it work?
|
|
if (unit.getLocation() != wl) return false;
|
|
|
|
// Now recheck, and see if we want to change to the expected
|
|
// work type.
|
|
if (workType != null && workType != unit.getWorkType()) {
|
|
igc().changeWorkType(unit, workType);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
// Public base accessors and mutators
|
|
|
|
/**
|
|
* Gets the {@code Colony} in use.
|
|
*
|
|
* Try to use this at the top of all the routines that need the colony,
|
|
* to get a stable value. There have been nasty races when the colony
|
|
* changes out from underneath us, and more may be lurking.
|
|
*
|
|
* @return The {@code Colony}.
|
|
*/
|
|
public final synchronized Colony getColony() {
|
|
return colony;
|
|
}
|
|
|
|
/**
|
|
* Gets the {@code TilesPanel}-object in use.
|
|
*
|
|
* @return The {@code TilesPanel}.
|
|
*/
|
|
public final TilesPanel getTilesPanel() {
|
|
return tilesPanel;
|
|
}
|
|
|
|
/**
|
|
* Gets the {@code WarehousePanel}-object in use.
|
|
*
|
|
* @return The {@code WarehousePanel}.
|
|
*/
|
|
public final WarehousePanel getWarehousePanel() {
|
|
return warehousePanel;
|
|
}
|
|
|
|
|
|
// Override PortPanel
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void setSelectedUnitLabel(UnitLabel unitLabel) {
|
|
if (selectedUnitLabel != unitLabel) {
|
|
inPortPanel.removePropertyChangeListeners();
|
|
if (selectedUnitLabel != null) {
|
|
selectedUnitLabel.setSelected(false);
|
|
selectedUnitLabel.getUnit().removePropertyChangeListener(this);
|
|
}
|
|
super.setSelectedUnitLabel(unitLabel);
|
|
if (unitLabel == null) {
|
|
cargoPanel.setCarrier(null);
|
|
} else {
|
|
cargoPanel.setCarrier(unitLabel.getUnit());
|
|
unitLabel.setSelected(true);
|
|
unitLabel.getUnit().addPropertyChangeListener(this);
|
|
}
|
|
inPortPanel.addPropertyChangeListeners();
|
|
}
|
|
updateCarrierButtons();
|
|
inPortPanel.revalidate();
|
|
inPortPanel.repaint();
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public boolean setSelectedUnit(Unit unit) {
|
|
return ((unit.isCarrier()) ? inPortPanel.setSelectedUnit(unit)
|
|
: super.setSelectedUnit(unit));
|
|
}
|
|
|
|
|
|
// Update routines
|
|
|
|
private void updateBuildingsPanel() {
|
|
// FIXME: find out why the cache needs to be explicitly invalidated
|
|
colony.invalidateCache();
|
|
buildingsPanel.update();
|
|
}
|
|
|
|
private void updateConstructionPanel() {
|
|
constructionPanel.update();
|
|
}
|
|
|
|
private void updateInPortPanel() {
|
|
inPortPanel.update();
|
|
}
|
|
|
|
private void updateNetProductionPanel() {
|
|
final FreeColClient freeColClient = getFreeColClient();
|
|
final Colony colony = getColony();
|
|
final Specification spec = colony.getSpecification();
|
|
// FIXME: find out why the cache needs to be explicitly invalidated
|
|
colony.invalidateCache();
|
|
|
|
netProductionPanel.removeAll();
|
|
for (GoodsType goodsType : spec.getGoodsTypeList()) {
|
|
int amount = colony.getAdjustedNetProductionOf(goodsType);
|
|
if (amount != 0) {
|
|
AbstractGoods ag = new AbstractGoods(goodsType, amount);
|
|
netProductionPanel.add(new ProductionLabel(freeColClient, ag));
|
|
}
|
|
}
|
|
netProductionPanel.revalidate();
|
|
netProductionPanel.repaint();
|
|
}
|
|
|
|
private void updateOutsideColonyPanel() {
|
|
outsideColonyPanel.update();
|
|
}
|
|
|
|
private void updatePopulationPanel() {
|
|
populationPanel.update();
|
|
}
|
|
|
|
private void updateTilesPanel() {
|
|
tilesPanel.update();
|
|
}
|
|
|
|
private void updateWarehousePanel() {
|
|
warehousePanel.update();
|
|
}
|
|
|
|
|
|
/**
|
|
* Close this {@code ColonyPanel}.
|
|
*/
|
|
public void closeColonyPanel() {
|
|
final Colony colony = getColony();
|
|
final Player player = getMyPlayer();
|
|
boolean abandon = false;
|
|
BuildableType buildable;
|
|
if (player.owns(colony)) {
|
|
if (colony.getUnitCount() == 0) {
|
|
final String key = (player.isRebel()
|
|
&& player.getNumberOfPorts() == 1
|
|
&& colony.isConnectedPort())
|
|
? "abandonColony.lastPort.text"
|
|
: "abandonColony.text";
|
|
if (!getGUI().confirm(null, StringTemplate.key(key), colony,
|
|
"abandonColony.yes", "abandonColony.no")) return;
|
|
abandon = true;
|
|
} else if ((buildable = colony.getCurrentlyBuilding()) != null) {
|
|
int required = buildable.getRequiredPopulation();
|
|
if (required > colony.getUnitCount()
|
|
&& !getGUI().confirm(null, StringTemplate
|
|
.template("colonyPanel.reducePopulation")
|
|
.addName("%colony%", colony.getName())
|
|
.addAmount("%number%", required)
|
|
.addNamed("%buildable%", buildable),
|
|
colony, "ok", "cancel")) return;
|
|
}
|
|
}
|
|
|
|
cleanup();
|
|
getGUI().removeComponent(this);
|
|
// Abandon colony and active unit handling is in IGC
|
|
igc().closeColony(colony, abandon);
|
|
}
|
|
|
|
|
|
// Interface PortPanel
|
|
|
|
/**
|
|
* Gets the list of units on the colony tile.
|
|
* Note, does not include the units *inside* the colony.
|
|
*
|
|
* @return A sorted list of units on the colony tile.
|
|
*/
|
|
@Override
|
|
public List<Unit> getUnitList() {
|
|
return sort(colony.getTile().getUnits());
|
|
}
|
|
|
|
|
|
// Interface ActionListener
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void actionPerformed(ActionEvent ae) {
|
|
if (OK.equals(ae.getActionCommand())) {
|
|
closeColonyPanel();
|
|
} else {
|
|
super.actionPerformed(ae);
|
|
}
|
|
}
|
|
|
|
|
|
// Interface PropertyChangeListener
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void propertyChange(PropertyChangeEvent event) {
|
|
final Colony colony = getColony();
|
|
if (!isShowing() || colony == null) return;
|
|
String property = event.getPropertyName();
|
|
logger.finest(colony.getName() + " change " + property
|
|
+ ": " + event.getOldValue()
|
|
+ " -> " + event.getNewValue());
|
|
|
|
if (property == null) {
|
|
logger.warning("Null property change");
|
|
} else if (Unit.CARGO_CHANGE.equals(property)) {
|
|
cargoPanel.update();
|
|
updateInPortPanel();
|
|
} else if (ColonyChangeEvent.POPULATION_CHANGE.toString().equals(property)) {
|
|
updatePopulationPanel();
|
|
updateNetProductionPanel(); // food production changes
|
|
} else if (ColonyChangeEvent.BONUS_CHANGE.toString().equals(property)) {
|
|
if (colony.getUnitCount() > 0) { // Ignore messages when abandoning
|
|
ModelMessage msg = colony.checkForGovMgtChangeMessage();
|
|
if (msg != null) {
|
|
getGUI().showInformationPanel(colony, msg);
|
|
}
|
|
}
|
|
updatePopulationPanel();
|
|
} else if (ColonyChangeEvent.BUILD_QUEUE_CHANGE.toString().equals(property)) {
|
|
updateProduction();
|
|
} else if (ColonyChangeEvent.UNIT_TYPE_CHANGE.toString().equals(property)) {
|
|
FreeColGameObject object = (FreeColGameObject)event.getSource();
|
|
UnitType oldType = (UnitType) event.getOldValue();
|
|
UnitType newType = (UnitType) event.getNewValue();
|
|
getGUI().showInformationPanel(object, StringTemplate
|
|
.template("colonyPanel.unitChange")
|
|
.addName("%colony%", colony.getName())
|
|
.addNamed("%oldType%", oldType)
|
|
.addNamed("%newType%", newType));
|
|
updateTilesPanel();
|
|
updateBuildingsPanel();
|
|
updateProduction();
|
|
} else if (property.startsWith("model.goods.")) {
|
|
// Changes to warehouse goods count may affect building production
|
|
// which requires a view update.
|
|
updateWarehousePanel();
|
|
updateProduction();
|
|
} else if (Tile.UNIT_CHANGE.equals(property)) {
|
|
updateOutsideColonyPanel();
|
|
updateInPortPanel();
|
|
updatePopulationPanel();
|
|
updateWarehousePanel();// Role change changes equipment goods
|
|
} else if (Unit.MOVE_CHANGE.equals(property)) {
|
|
updateOutsideColonyPanel();
|
|
} else {
|
|
// ColonyTiles and Buildings now have their own
|
|
// propertyChangeListeners so {ColonyTile,Building}.UNIT_CHANGE
|
|
// events should not arrive here.
|
|
logger.warning("Unknown property change event: "
|
|
+ event.getPropertyName());
|
|
}
|
|
}
|
|
|
|
|
|
// Override Component
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void removeNotify() {
|
|
if (colony == null) return; // Been here already
|
|
colony.setOccupationTrace(false);
|
|
colony = null; // Now Canvas.getColonyPanel will no longer find this
|
|
|
|
super.removeNotify();
|
|
|
|
// Alas, ColonyPanel is often leaky.
|
|
unloadButton = null;
|
|
fillButton = null;
|
|
warehouseButton = null;
|
|
buildQueueButton = null;
|
|
colonyUnitsButton = null;
|
|
setGoodsButton = null;
|
|
traceWorkButton = null;
|
|
netProductionPanel = null;
|
|
buildingsPanel = null;
|
|
buildingsScroll = null;
|
|
cargoScroll = null;
|
|
constructionPanel = null;
|
|
inPortScroll = null;
|
|
outsideColonyPanel = null;
|
|
outsideColonyScroll = null;
|
|
populationPanel = null;
|
|
tilesPanel = null;
|
|
tilesScroll = null;
|
|
warehousePanel = null;
|
|
warehouseScroll = null;
|
|
|
|
releaseListener = null;
|
|
|
|
// Inherited from PortPanel
|
|
//cargoPanel = null;
|
|
//inPortPanel = null;
|
|
//defaultTransferHandler = null;
|
|
//pressListener = null;
|
|
//selectedUnitLabel = null;
|
|
}
|
|
|
|
|
|
// Subpanel classes
|
|
|
|
/**
|
|
* This panel shows the content of a carrier in the colony
|
|
*/
|
|
public final class ColonyCargoPanel extends CargoPanel {
|
|
|
|
/**
|
|
* Create this colony cargo panel.
|
|
*
|
|
* @param freeColClient The {@code FreeColClient} for the game.
|
|
*/
|
|
public ColonyCargoPanel(FreeColClient freeColClient) {
|
|
super(freeColClient, true);
|
|
}
|
|
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void update() {
|
|
super.update();
|
|
// May have un/loaded cargo, "Unload" could have changed validity
|
|
updateCarrierButtons();
|
|
updatePopulationPanel();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The panel to display the population breakdown for this colony.
|
|
*/
|
|
public final class PopulationPanel extends MigPanel {
|
|
|
|
// Predefine all the required labels.
|
|
private final JLabel rebelShield = new JLabel();
|
|
private final JLabel rebelLabel = new JLabel();
|
|
private final JLabel bonusLabel = new JLabel();
|
|
private final JLabel royalistLabel = new JLabel();
|
|
private final JLabel royalistShield = new JLabel();
|
|
private final JLabel rebelMemberLabel = new JLabel();
|
|
private final JLabel popLabel = new JLabel();
|
|
private final JLabel royalistMemberLabel = new JLabel();
|
|
|
|
|
|
/**
|
|
* Create a new population panel.
|
|
*/
|
|
public PopulationPanel() {
|
|
super("PopulationPanelUI",
|
|
new MigLayout("wrap 5, fill, insets 0",
|
|
"[][]:push[center]:push[right][]"));
|
|
|
|
setOpaque(false);
|
|
setToolTipText(" ");
|
|
}
|
|
|
|
|
|
/**
|
|
* Initialize this population panel.
|
|
*/
|
|
public void initialize() {
|
|
cleanup();
|
|
update();
|
|
}
|
|
|
|
/**
|
|
* Clean up this population panel.
|
|
*/
|
|
public void cleanup() {
|
|
// Nothing yet
|
|
}
|
|
|
|
/**
|
|
* Update this population panel.
|
|
*/
|
|
public void update() {
|
|
final Colony colony = getColony();
|
|
if (colony == null) return;
|
|
final ImageLibrary lib = getImageLibrary();
|
|
final Font font = FontLibrary.getScaledFont("normal-plain-smaller", null);
|
|
final int uc = colony.getUnitCount();
|
|
final int solPercent = colony.getSonsOfLiberty();
|
|
final int rebelCount = Colony.calculateRebelCount(uc, solPercent);
|
|
final Nation nation = colony.getOwner().getNation();
|
|
final int grow = colony.getPreferredSizeChange();
|
|
final int bonus = colony.getProductionBonus();
|
|
StringTemplate t;
|
|
|
|
removeAll();
|
|
|
|
rebelShield.setIcon(new ImageIcon(lib.getSmallerNationImage(nation)));
|
|
add(rebelShield, "bottom");
|
|
|
|
t = StringTemplate.template("colonyPanel.rebelLabel")
|
|
.addAmount("%number%", rebelCount);
|
|
rebelLabel.setText(Messages.message(t));
|
|
rebelLabel.setFont(font);
|
|
add(rebelLabel, "split 2, flowy");
|
|
|
|
rebelMemberLabel.setText(solPercent + "%");
|
|
rebelMemberLabel.setFont(font);
|
|
add(rebelMemberLabel);
|
|
|
|
t = StringTemplate.template("colonyPanel.populationLabel")
|
|
.addAmount("%number%", uc);
|
|
popLabel.setText(Messages.message(t));
|
|
popLabel.setFont(font);
|
|
add(popLabel, "split 2, flowy");
|
|
|
|
String growth = (grow == 0) ? ""
|
|
: '(' + ((grow >= Colony.CHANGE_UPPER_BOUND)
|
|
? Messages.message("many")
|
|
: String.valueOf(grow)) + ')';
|
|
t = StringTemplate.template("colonyPanel.bonusLabel")
|
|
.addAmount("%number%", bonus)
|
|
.addName("%extra%", growth);
|
|
bonusLabel.setText(Messages.message(t));
|
|
bonusLabel.setFont(font);
|
|
add(bonusLabel);
|
|
|
|
t = StringTemplate.template("colonyPanel.royalistLabel")
|
|
.addAmount("%number%", uc - rebelCount);
|
|
royalistLabel.setText(Messages.message(t));
|
|
royalistLabel.setFont(font);
|
|
add(royalistLabel, "split 2, flowy");
|
|
|
|
royalistMemberLabel.setText((100 - solPercent) + "%");
|
|
royalistMemberLabel.setFont(font);
|
|
add(royalistMemberLabel);
|
|
|
|
Nation other = (colony.getOwner().isREF())
|
|
? nation.getRebelNation()
|
|
: nation.getREFNation();
|
|
try {
|
|
royalistShield.setIcon(new ImageIcon(lib.getSmallerNationImage(other)));
|
|
add(royalistShield, "bottom");
|
|
} catch (Exception e) {
|
|
logger.log(Level.WARNING, "Shield: " + nation + "/" + other, e);
|
|
}
|
|
|
|
revalidate();
|
|
repaint();
|
|
}
|
|
|
|
/**
|
|
* Generates the {@code JToolTip} with the colony's
|
|
* detailed population information.
|
|
*
|
|
* @return The JToolTip created.
|
|
*/
|
|
@Override
|
|
public JToolTip createToolTip() {
|
|
JToolTip toolTip = new RebelToolTip(getFreeColClient(), getColony());
|
|
return toolTip;
|
|
}
|
|
|
|
|
|
// Override Component
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void removeNotify() {
|
|
super.removeNotify();
|
|
|
|
removeAll();
|
|
setLayout(null);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A panel that holds UnitLabels that represent Units that are standing in
|
|
* front of a colony.
|
|
*/
|
|
public final class OutsideColonyPanel extends UnitPanel
|
|
implements DropTarget {
|
|
|
|
/**
|
|
* Create this OutsideColonyPanel.
|
|
*/
|
|
public OutsideColonyPanel() {
|
|
super("OutsideColonyPanelUI",
|
|
new MigLayout("wrap 3, fill, insets 0"), ColonyPanel.this,
|
|
null, ColonyPanel.this.isEditable());
|
|
|
|
setBorder(Utility.localizedBorder("colonyPanel.outsideColony"));
|
|
}
|
|
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void initialize() {
|
|
super.initialize();
|
|
|
|
final Colony colony = getColony();
|
|
if (colony != null) {
|
|
setName(colony.getName() + " - " + Messages.message("port"));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void cleanup() {
|
|
super.cleanup();
|
|
|
|
removeAll();
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
protected void addPropertyChangeListeners() {
|
|
final Colony colony = getColony();
|
|
if (colony != null) {
|
|
colony.getTile().addPropertyChangeListener(Tile.UNIT_CHANGE,
|
|
this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
protected void removePropertyChangeListeners() {
|
|
final Colony colony = getColony();
|
|
if (colony != null) {
|
|
colony.getTile().removePropertyChangeListener(Tile.UNIT_CHANGE,
|
|
this);
|
|
}
|
|
}
|
|
|
|
// Inherit UnitPanel.update
|
|
|
|
|
|
// Interface DropTarget
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public boolean accepts(Goods goods) {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public boolean accepts(GoodsType goodsType) {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public boolean accepts(Unit unit) {
|
|
return !unit.isCarrier();
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void selectLabel() {
|
|
// do nothing
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public Component add(Component comp, boolean editState) {
|
|
Container oldParent = comp.getParent();
|
|
if (editState) {
|
|
if (comp instanceof UnitLabel) {
|
|
UnitLabel unitLabel = (UnitLabel)comp;
|
|
Unit unit = unitLabel.getUnit();
|
|
|
|
if (!unit.isOnCarrier()) {
|
|
igc().putOutsideColony(unit);
|
|
}
|
|
|
|
if (unit.getColony() == null) {
|
|
closeColonyPanel();
|
|
return null;
|
|
} else if (!unit.isOnTile() && !unit.isOnCarrier()) {
|
|
return null;
|
|
}
|
|
|
|
oldParent.remove(comp);
|
|
initialize();
|
|
return comp;
|
|
} else {
|
|
logger.warning("Invalid component: " + comp);
|
|
return null;
|
|
}
|
|
} else {
|
|
((UnitLabel)comp).setSmall(false);
|
|
return add(comp);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public int suggested(GoodsType type) { return -1; } // N/A
|
|
}
|
|
|
|
/**
|
|
* A panel that holds UnitLabels that represent naval Units that are
|
|
* waiting in the port of the colony.
|
|
*/
|
|
public final class ColonyInPortPanel extends InPortPanel {
|
|
|
|
/**
|
|
* Creates this ColonyInPortPanel.
|
|
*/
|
|
public ColonyInPortPanel() {
|
|
super(new MigLayout("wrap 3, fill, insets 0"), ColonyPanel.this,
|
|
null, ColonyPanel.this.isEditable());
|
|
|
|
setBorder(Utility.localizedBorder("colonyPanel.inPort"));
|
|
}
|
|
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void initialize() {
|
|
super.initialize();
|
|
|
|
final Colony colony = getColony();
|
|
if (colony != null) {
|
|
setName(colony.getName() + " - " + Messages.message("port"));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
protected void addPropertyChangeListeners() {
|
|
Unit selected = getSelectedUnit();
|
|
if (selected != null) {
|
|
selected.addPropertyChangeListener(Unit.CARGO_CHANGE, this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
protected void removePropertyChangeListeners() {
|
|
Unit selected = getSelectedUnit();
|
|
if (selected != null) {
|
|
selected.removePropertyChangeListener(Unit.CARGO_CHANGE, this);
|
|
}
|
|
}
|
|
|
|
|
|
// extend UnitPanel
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public boolean accepts(Unit unit) {
|
|
return unit.isCarrier();
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void selectLabel() {
|
|
removePropertyChangeListeners();
|
|
super.selectLabel();
|
|
addPropertyChangeListeners();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A panel that holds goods that represent cargo that is inside
|
|
* the Colony.
|
|
*/
|
|
public final class WarehousePanel extends MigPanel
|
|
implements DropTarget, PropertyChangeListener {
|
|
|
|
/**
|
|
* Creates a WarehousePanel.
|
|
*/
|
|
public WarehousePanel() {
|
|
super("WarehousePanelUI",
|
|
new MigLayout("fill, gap push, insets 0"));
|
|
}
|
|
|
|
|
|
/**
|
|
* Initialize this WarehousePanel.
|
|
*/
|
|
public void initialize() {
|
|
cleanup();
|
|
addPropertyChangeListeners();
|
|
update();
|
|
}
|
|
|
|
/**
|
|
* Clean up this WarehousePanel.
|
|
*/
|
|
public void cleanup() {
|
|
removePropertyChangeListeners();
|
|
removeAll();
|
|
}
|
|
|
|
/**
|
|
* Add the property change listeners to this panel.
|
|
*/
|
|
private void addPropertyChangeListeners() {
|
|
final Colony colony = getColony();
|
|
if (colony != null) {
|
|
colony.getGoodsContainer().addPropertyChangeListener(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove the property change listeners from this panel.
|
|
*/
|
|
private void removePropertyChangeListeners() {
|
|
final Colony colony = getColony();
|
|
if (colony != null) {
|
|
colony.getGoodsContainer().removePropertyChangeListener(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update this WarehousePanel.
|
|
*/
|
|
public void update() {
|
|
final Colony colony = getColony();
|
|
if (colony == null) return;
|
|
removeAll();
|
|
|
|
ClientOptions options = getClientOptions();
|
|
final int threshold = (FreeColDebugger.isInDebugMode(FreeColDebugger.DebugMode.MENUS))
|
|
? 1
|
|
: options.getInteger(ClientOptions.MIN_NUMBER_FOR_DISPLAYING_GOODS);
|
|
final Game game = colony.getGame();
|
|
final Specification spec = colony.getSpecification();
|
|
for (GoodsType goodsType : spec.getStorableGoodsTypeList()) {
|
|
int count = colony.getGoodsCount(goodsType);
|
|
if (count >= threshold) {
|
|
Goods goods = new Goods(game, colony, goodsType, count);
|
|
GoodsLabel goodsLabel
|
|
= new GoodsLabel(getFreeColClient(), goods);
|
|
if (ColonyPanel.this.isEditable()) {
|
|
goodsLabel.setTransferHandler(defaultTransferHandler);
|
|
goodsLabel.addMouseListener(pressListener);
|
|
}
|
|
add(goodsLabel, "alignx center");
|
|
}
|
|
}
|
|
ColonyPanel.this.updateProduction();
|
|
revalidate();
|
|
repaint();
|
|
}
|
|
|
|
|
|
// Interface DropTarget
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public boolean accepts(Goods goods) {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public boolean accepts(GoodsType goodsType) {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public boolean accepts(Unit unit) {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public Component add(Component comp, boolean editState) {
|
|
if (editState) {
|
|
if (!(comp instanceof GoodsLabel)) {
|
|
logger.warning("Invalid component: " + comp);
|
|
return null;
|
|
}
|
|
comp.getParent().remove(comp);
|
|
return comp;
|
|
}
|
|
|
|
return add(comp);
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public int suggested(GoodsType type) {
|
|
Colony colony = getColony();
|
|
return colony.getWarehouseCapacity() - colony.getGoodsCount(type);
|
|
}
|
|
|
|
|
|
// Interface PropertyChangeListener
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void propertyChange(PropertyChangeEvent event) {
|
|
final Colony colony = getColony();
|
|
if (colony != null && event != null) {
|
|
logger.finest(colony.getName() + "-warehouse change "
|
|
+ event.getPropertyName()
|
|
+ ": " + event.getOldValue()
|
|
+ " -> " + event.getNewValue());
|
|
}
|
|
update();
|
|
}
|
|
|
|
|
|
// Override Component
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void removeNotify() {
|
|
super.removeNotify();
|
|
|
|
removeAll();
|
|
setLayout(null);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* This panel is a list of the colony's buildings.
|
|
*/
|
|
public final class BuildingsPanel extends MigPanel {
|
|
|
|
/** Always pop up the build queue when clicking on a building. */
|
|
private final MouseAdapter buildQueueListener
|
|
= new MouseAdapter() {
|
|
@Override
|
|
public void mousePressed(MouseEvent e) {
|
|
getGUI().showBuildQueuePanel(getColony());
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
* Creates this BuildingsPanel.
|
|
*/
|
|
public BuildingsPanel() {
|
|
super("BuildingsPanelUI", new MigLayout("fill, wrap 4, insets 0, gap 0:10:10:push 0:10:10:push"));
|
|
}
|
|
|
|
|
|
/**
|
|
* Initializes the game data in this buildings panel.
|
|
*/
|
|
public void initialize() {
|
|
final Colony colony = getColony();
|
|
if (colony == null) return;
|
|
cleanup();
|
|
|
|
for (Building building : sort(colony.getBuildings())) {
|
|
ASingleBuildingPanel aSBP = new ASingleBuildingPanel(building);
|
|
aSBP.initialize();
|
|
add(aSBP);
|
|
}
|
|
|
|
update();
|
|
}
|
|
|
|
/**
|
|
* Clean up this buildings panel.
|
|
*/
|
|
public void cleanup() {
|
|
for (Component component : getComponents()) {
|
|
if (component instanceof ASingleBuildingPanel) {
|
|
((ASingleBuildingPanel)component).cleanup();
|
|
}
|
|
}
|
|
removeAll();
|
|
}
|
|
|
|
/**
|
|
* Update this buildings panel.
|
|
*/
|
|
public void update() {
|
|
for (Component component : getComponents()) {
|
|
if (component instanceof ASingleBuildingPanel) {
|
|
((ASingleBuildingPanel)component).update();
|
|
}
|
|
}
|
|
|
|
repaint();
|
|
}
|
|
|
|
|
|
// Override Component
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void removeNotify() {
|
|
super.removeNotify();
|
|
|
|
removeAll();
|
|
setLayout(null);
|
|
}
|
|
|
|
|
|
/**
|
|
* This panel is a single line (one building) in the
|
|
* {@code BuildingsPanel}.
|
|
*/
|
|
public final class ASingleBuildingPanel extends BuildingPanel
|
|
implements DropTarget {
|
|
|
|
/**
|
|
* Creates this ASingleBuildingPanel.
|
|
*
|
|
* @param building The {@code Building} to display
|
|
* information from.
|
|
*/
|
|
public ASingleBuildingPanel(Building building) {
|
|
super(getFreeColClient(), building);
|
|
|
|
setOpaque(false);
|
|
}
|
|
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void initialize() {
|
|
if (ColonyPanel.this.isEditable()) {
|
|
super.initialize();
|
|
|
|
addMouseListener(releaseListener);
|
|
addMouseListener(buildQueueListener);
|
|
setTransferHandler(defaultTransferHandler);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
protected void cleanup() {
|
|
super.cleanup();
|
|
|
|
removeMouseListener(releaseListener);
|
|
removeMouseListener(buildQueueListener);
|
|
setTransferHandler(null);
|
|
removeAll();
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void update() {
|
|
super.update();
|
|
|
|
if (ColonyPanel.this.isEditable()) {
|
|
for (UnitLabel unitLabel : getUnitLabels()) {
|
|
unitLabel.setTransferHandler(defaultTransferHandler);
|
|
unitLabel.addMouseListener(pressListener);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Try to assign a unit to work this building.
|
|
*
|
|
* @param unit The {@code Unit} to try.
|
|
* @return True if the work begins.
|
|
*/
|
|
private boolean tryWork(Unit unit) {
|
|
Building building = getBuilding();
|
|
NoAddReason reason = building.getNoAddReason(unit);
|
|
if (reason != NoAddReason.NONE) {
|
|
getGUI().showInformationPanel(building, reason.getDescriptionKey());
|
|
return false;
|
|
}
|
|
|
|
return ColonyPanel.this.tryWork(unit, building);
|
|
}
|
|
|
|
|
|
// Interface DropTarget
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public boolean accepts(Goods goods) {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public boolean accepts(GoodsType goodsType) {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public boolean accepts(Unit unit) {
|
|
return unit.isPerson();
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public Component add(Component comp, boolean editState) {
|
|
if (editState) {
|
|
if (comp instanceof UnitLabel) {
|
|
if (!tryWork(((UnitLabel)comp).getUnit())) return null;
|
|
} else {
|
|
logger.warning("An invalid component was dropped"
|
|
+ " on this ASingleBuildingPanel.");
|
|
return null;
|
|
}
|
|
update();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public int suggested(GoodsType type) { return -1; } // N/A
|
|
|
|
|
|
// Interface PropertyChangeListener
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void propertyChange(PropertyChangeEvent event) {
|
|
super.propertyChange(event);
|
|
|
|
ColonyPanel.this.updateProduction();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A panel that displays the tiles in the immediate area around the colony.
|
|
* TilesPanel class must use the ImageLibrary inside SwingGUI.tileMapViewer
|
|
* for everything.
|
|
*/
|
|
public final class TilesPanel extends JPanel {
|
|
|
|
/** The tiles around the colony. */
|
|
private final Tile[][] tiles = new Tile[3][3];
|
|
|
|
/** A currently displayed production message. */
|
|
private FreeColPanel cachedPanel = null;
|
|
/** The work location that would be better to produce with. */
|
|
private WorkLocation bestLocation = null;
|
|
|
|
|
|
/**
|
|
* Creates a TilesPanel.
|
|
*/
|
|
public TilesPanel() {
|
|
setBackground(Color.BLACK);
|
|
setBorder(null);
|
|
setLayout(null);
|
|
}
|
|
|
|
|
|
/**
|
|
* Initialize the game data in this tiles panel.
|
|
*/
|
|
public void initialize() {
|
|
final Colony colony = getColony();
|
|
if (colony == null) return;
|
|
cleanup();
|
|
|
|
Tile tile = colony.getTile();
|
|
tiles[0][0] = tile.getNeighbourOrNull(Direction.N);
|
|
tiles[0][1] = tile.getNeighbourOrNull(Direction.NE);
|
|
tiles[0][2] = tile.getNeighbourOrNull(Direction.E);
|
|
tiles[1][0] = tile.getNeighbourOrNull(Direction.NW);
|
|
tiles[1][1] = tile;
|
|
tiles[1][2] = tile.getNeighbourOrNull(Direction.SE);
|
|
tiles[2][0] = tile.getNeighbourOrNull(Direction.W);
|
|
tiles[2][1] = tile.getNeighbourOrNull(Direction.SW);
|
|
tiles[2][2] = tile.getNeighbourOrNull(Direction.S);
|
|
|
|
int layer = 2;
|
|
for (int x = 0; x < tiles.length; x++) {
|
|
for (int y = 0; y < tiles[x].length; y++) {
|
|
if (tiles[x][y] == null) {
|
|
logger.warning("Null tile for " + getColony()
|
|
+ " at " + x + "," + y);
|
|
continue;
|
|
}
|
|
ColonyTile colonyTile = colony.getColonyTile(tiles[x][y]);
|
|
if (colonyTile == null) {
|
|
logger.warning("Null colony tile for " + getColony()
|
|
+ " on " + tiles[x][y] + " at " + x + "," + y);
|
|
dump("TILES", colony.getColonyTiles());
|
|
continue;
|
|
}
|
|
ASingleTilePanel aSTP
|
|
= new ASingleTilePanel(colonyTile, x, y);
|
|
aSTP.initialize();
|
|
add(aSTP, Integer.valueOf(layer++));
|
|
}
|
|
}
|
|
|
|
update();
|
|
}
|
|
|
|
/**
|
|
* Clean up this tiles panel.
|
|
*/
|
|
public void cleanup() {
|
|
for (Component component : getComponents()) {
|
|
if (component instanceof ASingleTilePanel) {
|
|
((ASingleTilePanel)component).cleanup();
|
|
}
|
|
}
|
|
removeAll();
|
|
}
|
|
|
|
/**
|
|
* Update this tiles panel.
|
|
*/
|
|
public void update() {
|
|
for (Component component : getComponents()) {
|
|
if (component instanceof ASingleTilePanel) {
|
|
((ASingleTilePanel)component).update();
|
|
}
|
|
}
|
|
repaint();
|
|
}
|
|
|
|
/**
|
|
* Display the poor production message.
|
|
*
|
|
* @param best The better work location.
|
|
* @param template The {@code StringTemplate} with the message.
|
|
*/
|
|
public void showPoorProduction(WorkLocation best,
|
|
StringTemplate template) {
|
|
// Already warned about this? Do nothing if so.
|
|
if (this.bestLocation == best) return;
|
|
|
|
if (this.cachedPanel != null) {
|
|
getGUI().removeComponent(this.cachedPanel);
|
|
}
|
|
this.cachedPanel = getGUI().showInformationPanel(best, template);
|
|
this.bestLocation = best;
|
|
}
|
|
|
|
|
|
// Override JComponent
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public void paintComponent(Graphics g) {
|
|
g.setColor(Color.BLACK);
|
|
g.fillRect(0, 0, getWidth(), getHeight());
|
|
|
|
final Colony colony = getColony();
|
|
if (colony != null) {
|
|
getGUI().displayColonyTiles((Graphics2D)g, tiles, colony);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Panel for visualizing a {@code ColonyTile}. The
|
|
* component itself is not visible, however the content of the
|
|
* component is (i.e. the people working and the production)
|
|
*/
|
|
public final class ASingleTilePanel extends JPanel
|
|
implements DropTarget, PropertyChangeListener {
|
|
|
|
/** The colony tile to monitor. */
|
|
private final ColonyTile colonyTile;
|
|
|
|
|
|
/**
|
|
* Create a new single tile panel.
|
|
*
|
|
* @param colonyTile The {@code ColonyTile} to monitor.
|
|
* @param x The x offset.
|
|
* @param y The y offset.
|
|
*/
|
|
public ASingleTilePanel(ColonyTile colonyTile, int x, int y) {
|
|
this.colonyTile = colonyTile;
|
|
|
|
setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
|
|
setOpaque(false);
|
|
// Size and position:
|
|
Dimension size = getImageLibrary().getTileSize();
|
|
setSize(size);
|
|
setLocation(((2 - x) + y) * size.width / 2,
|
|
(x + y) * size.height / 2);
|
|
}
|
|
|
|
|
|
/**
|
|
* Initialize this single tile panel.
|
|
*/
|
|
public void initialize() {
|
|
if (ColonyPanel.this.isEditable()) {
|
|
cleanup();
|
|
|
|
addMouseListener(pressListener);
|
|
addMouseListener(releaseListener);
|
|
setTransferHandler(defaultTransferHandler);
|
|
addPropertyChangeListeners();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clean up this single tile panel.
|
|
*/
|
|
public void cleanup() {
|
|
removeMouseListener(pressListener);
|
|
removeMouseListener(releaseListener);
|
|
setTransferHandler(null);
|
|
removePropertyChangeListeners();
|
|
removeAll();
|
|
}
|
|
|
|
/**
|
|
* Adds PropertyChangeListeners
|
|
*/
|
|
private void addPropertyChangeListeners() {
|
|
if (this.colonyTile != null) {
|
|
this.colonyTile.addPropertyChangeListener(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Removes PropertyChangeListeners
|
|
*/
|
|
private void removePropertyChangeListeners() {
|
|
if (this.colonyTile != null) {
|
|
this.colonyTile.removePropertyChangeListener(this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update this single tile panel.
|
|
*/
|
|
public void update() {
|
|
removeAll();
|
|
if (this.colonyTile == null) {
|
|
logger.warning("Update of " + getColony()
|
|
+ " null colony tile.");
|
|
return;
|
|
}
|
|
|
|
final FreeColClient fcc = getFreeColClient();
|
|
UnitLabel label = null;
|
|
for (Unit unit : this.colonyTile.getUnitList()) {
|
|
label = new UnitLabel(fcc, unit, false, false);
|
|
if (ColonyPanel.this.isEditable()) {
|
|
label.setTransferHandler(defaultTransferHandler);
|
|
label.addMouseListener(pressListener);
|
|
}
|
|
super.add(label);
|
|
}
|
|
updateDescriptionLabel(label);
|
|
if (this.colonyTile.isColonyCenterTile()) {
|
|
setLayout(new GridLayout(2, 1));
|
|
ProductionInfo info
|
|
= colony.getProductionInfo(this.colonyTile);
|
|
if (info != null) {
|
|
for (AbstractGoods ag : info.getProduction()) {
|
|
ProductionLabel productionLabel
|
|
= new ProductionLabel(fcc, ag);
|
|
productionLabel.addMouseListener(pressListener);
|
|
add(productionLabel);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the colony tile this panel is handling.
|
|
*
|
|
* @return The colony tile.
|
|
*/
|
|
public ColonyTile getColonyTile() {
|
|
return this.colonyTile;
|
|
}
|
|
|
|
/**
|
|
* Updates the description label, which is a tooltip with
|
|
* the terrain type, road and plow indicator, if any.
|
|
*
|
|
* If a unit is on it update the tooltip of it instead.
|
|
*
|
|
* @param unitLabel The {@code UnitLabel} to update.
|
|
*/
|
|
private void updateDescriptionLabel(UnitLabel unitLabel) {
|
|
String tileMsg = Messages.message(this.colonyTile.getLabel());
|
|
if (unitLabel == null) {
|
|
setToolTipText(tileMsg);
|
|
} else {
|
|
final Unit unit = unitLabel.getUnit();
|
|
unitLabel.setDescriptionLabel(tileMsg + " ["
|
|
+ unit.getDescription(Unit.UnitLabelType.NATIONAL)
|
|
+ "]");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Try to work this tile with a specified unit.
|
|
*
|
|
* @param unit The {@code Unit} to work the tile.
|
|
* @return True if the unit succeeds.
|
|
*/
|
|
private boolean tryWork(Unit unit) {
|
|
final Colony colony = getColony();
|
|
Tile tile = this.colonyTile.getWorkTile();
|
|
Player player = unit.getOwner();
|
|
|
|
if (tile.getOwningSettlement() != colony) {
|
|
// Need to acquire the tile before working it.
|
|
NoClaimReason claim
|
|
= player.canClaimForSettlementReason(tile);
|
|
switch (claim) {
|
|
case NONE: case NATIVES:
|
|
if (igc().claimTile(tile, colony)
|
|
&& tile.getOwningSettlement() == colony) {
|
|
logger.info("Colony " + colony.getName()
|
|
+ " claims tile " + tile
|
|
+ " with unit " + unit.getId());
|
|
} else {
|
|
logger.warning("Colony " + colony.getName()
|
|
+ " did not claim " + tile
|
|
+ " with unit " + unit.getId());
|
|
return false;
|
|
}
|
|
break;
|
|
default: // Otherwise, can not use land
|
|
getGUI().showInformationPanel(tile, claim.getDescriptionKey());
|
|
return false;
|
|
}
|
|
// Check reason again, claim should be satisfied.
|
|
if (tile.getOwningSettlement() != colony) {
|
|
throw new RuntimeException("Claim failed: " + claim);
|
|
}
|
|
}
|
|
|
|
// Claim sorted, but complain about other failure.
|
|
NoAddReason reason = this.colonyTile.getNoAddReason(unit);
|
|
if (reason != NoAddReason.NONE) {
|
|
getGUI().showInformationPanel(this.colonyTile,
|
|
StringTemplate.template(reason.getDescriptionKey()));
|
|
return false;
|
|
}
|
|
|
|
if (!ColonyPanel.this.tryWork(unit, this.colonyTile)) return false;
|
|
|
|
GoodsType workType = unit.getWorkType();
|
|
if (workType != null
|
|
&& getClientOptions().getBoolean(ClientOptions.SHOW_NOT_BEST_TILE)) {
|
|
WorkLocation best = colony.getWorkLocationFor(unit,
|
|
workType);
|
|
if (best != null && this.colonyTile != best
|
|
&& (this.colonyTile.getPotentialProduction(workType, unit.getType())
|
|
< best.getPotentialProduction(workType, unit.getType()))) {
|
|
StringTemplate template = StringTemplate
|
|
.template("colonyPanel.notBestTile")
|
|
.addStringTemplate("%unit%",
|
|
unit.getLabel(Unit.UnitLabelType.NATIONAL))
|
|
.addNamed("%goods%", workType)
|
|
.addStringTemplate("%tile%", best.getLabel());
|
|
showPoorProduction(best, template);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
// Interface DropTarget
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public boolean accepts(Unit unit) {
|
|
return unit.isPerson();
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
@Override
|
|
public Component add(Component comp, boolean editState) {
|
|
if (editState) {
|
|
if (comp instanceof UnitLabel) {
|
|
UnitLabel label = (UnitLabel)comp;
|
|
if (!tryWork(label.getUnit())) return null;
|
|
label.setSmall(false);
|
|
} else {
|
|
logger.warning("An invalid component was dropped"
|
|
+ " on this ASingleTilePanel.");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
update();
|
|
return comp;
|
|
}
|
|
|
|
|
|
// Interface PropertyChangeListener
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*
|
|
* Updates the production value via {@link #updateProduction()}
|
|
*/
|
|
@Override
|
|
public void propertyChange(PropertyChangeEvent event) {
|
|
String property = event.getPropertyName();
|
|
logger.finest(this.colonyTile.getId() + " change " + property
|
|
+ ": " + event.getOldValue()
|
|
+ " -> " + event.getNewValue());
|
|
ColonyPanel.this.updateProduction();
|
|
}
|
|
|
|
|
|
// Override JComponent
|
|
|
|
/**
|
|
* Checks if this {@code JComponent} contains the given
|
|
* coordinate.
|
|
*
|
|
* @param px The x coordinate to check.
|
|
* @param py The y coordinate to check.
|
|
* @return true if the coordinate is inside.
|
|
*/
|
|
@Override
|
|
public boolean contains(int px, int py) {
|
|
int w = getWidth();
|
|
int h = getHeight();
|
|
int dx = Math.abs(w/2 - px);
|
|
int dy = Math.abs(h/2 - py);
|
|
return (dx + w * dy / h) <= w/2;
|
|
}
|
|
}
|
|
}
|
|
}
|