/**
* Copyright (C) 2002-2012 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 java.beans.PropertyChangeEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Logger;
import org.freecolandroid.repackaged.java.awt.Component;
import org.freecolandroid.repackaged.java.awt.Dimension;
import org.freecolandroid.repackaged.java.awt.datatransfer.DataFlavor;
import org.freecolandroid.repackaged.java.awt.datatransfer.Transferable;
import org.freecolandroid.repackaged.java.awt.datatransfer.UnsupportedFlavorException;
import org.freecolandroid.repackaged.java.awt.event.ActionEvent;
import org.freecolandroid.repackaged.java.awt.event.ActionListener;
import org.freecolandroid.repackaged.java.awt.event.ItemEvent;
import org.freecolandroid.repackaged.java.awt.event.ItemListener;
import org.freecolandroid.repackaged.java.awt.event.MouseAdapter;
import org.freecolandroid.repackaged.java.awt.event.MouseEvent;
import org.freecolandroid.repackaged.javax.swing.AbstractAction;
import org.freecolandroid.repackaged.javax.swing.Action;
import org.freecolandroid.repackaged.javax.swing.DefaultListModel;
import org.freecolandroid.repackaged.javax.swing.ImageIcon;
import org.freecolandroid.repackaged.javax.swing.JButton;
import org.freecolandroid.repackaged.javax.swing.JCheckBox;
import org.freecolandroid.repackaged.javax.swing.JComponent;
import org.freecolandroid.repackaged.javax.swing.JLabel;
import org.freecolandroid.repackaged.javax.swing.JList;
import org.freecolandroid.repackaged.javax.swing.JPanel;
import org.freecolandroid.repackaged.javax.swing.JScrollPane;
import org.freecolandroid.repackaged.javax.swing.KeyStroke;
import org.freecolandroid.repackaged.javax.swing.ListCellRenderer;
import org.freecolandroid.repackaged.javax.swing.ListModel;
import org.freecolandroid.repackaged.javax.swing.ListSelectionModel;
import org.freecolandroid.repackaged.javax.swing.SwingConstants;
import org.freecolandroid.repackaged.javax.swing.TransferHandler;
import org.freecolandroid.repackaged.javax.swing.plaf.PanelUI;
import net.miginfocom.swing.MigLayout;
import net.sf.freecol.client.FreeColClient;
import net.sf.freecol.client.gui.GUI;
import net.sf.freecol.client.gui.i18n.Messages;
import net.sf.freecol.client.gui.plaf.FreeColComboBoxRenderer;
import net.sf.freecol.client.gui.plaf.FreeColSelectedPanelUI;
import net.sf.freecol.common.model.Ability;
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.BuildingType;
import net.sf.freecol.common.model.Colony;
import net.sf.freecol.common.model.FeatureContainer;
import net.sf.freecol.common.model.FreeColGameObjectType;
import net.sf.freecol.common.model.Limit;
import net.sf.freecol.common.model.StringTemplate;
import net.sf.freecol.common.model.UnitType;
import net.sf.freecol.common.resources.ResourceManager;
import net.sf.freecol.common.util.Utils;
public class BuildQueuePanel extends FreeColPanel implements ActionListener, ItemListener {
private static Logger logger = Logger.getLogger(BuildQueuePanel.class.getName());
private static final String BUY = "buy";
private static final int UNABLE_TO_BUILD = -1;
private final BuildQueueTransferHandler buildQueueHandler = new BuildQueueTransferHandler();
private ListCellRenderer cellRenderer;
private static JCheckBox compact = new JCheckBox();
private static JCheckBox showAll = new JCheckBox();
private JList buildQueueList;
private JList unitList;
private JList buildingList;
private ConstructionPanel constructionPanel;
private JButton buyBuilding;
private Colony colony;
private int unitCount;
private FeatureContainer featureContainer;
private Map<BuildableType, String> lockReasons = new HashMap<BuildableType, String>();
private Set<BuildableType> unbuildableTypes = new HashSet<BuildableType>();
/**
* A list of unit types that can be build. Most unit types are
* human and can never be built.
*/
private List<UnitType> buildableUnits = new ArrayList<UnitType>();
public BuildQueuePanel(FreeColClient freeColClient, GUI gui, Colony colony) {
super(freeColClient, gui, new MigLayout("wrap 3", "[260:][390:, fill][260:]", "[][][300:400:][]"));
this.colony = colony;
this.unitCount = colony.getUnitCount();
featureContainer = new FeatureContainer();
for (UnitType unitType : getSpecification().getUnitTypeList()) {
if (!(unitType.getGoodsRequired().isEmpty()
|| unitType.hasAbility(Ability.BORN_IN_COLONY))) {
// can be built
buildableUnits.add(unitType);
}
}
DefaultListModel current = new DefaultListModel();
for (BuildableType type : colony.getBuildQueue()) {
current.addElement(type);
featureContainer.add(type.getFeatureContainer());
}
cellRenderer = getCellRenderer();
// remove previous listeners
for (ItemListener listener : compact.getItemListeners()) {
compact.removeItemListener(listener);
}
compact.setText(Messages.message("colonyPanel.compactView"));
compact.addItemListener(this);
// remove previous listeners
for (ItemListener listener : showAll.getItemListeners()) {
showAll.removeItemListener(listener);
}
showAll.setText(Messages.message("colonyPanel.showAll"));
showAll.addItemListener(this);
buildQueueList = new JList(current);
buildQueueList.setTransferHandler(buildQueueHandler);
buildQueueList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
buildQueueList.setDragEnabled(true);
buildQueueList.setCellRenderer(cellRenderer);
buildQueueList.addMouseListener(new BuildQueueMouseAdapter(false));
Action deleteAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
DefaultListModel model = (DefaultListModel) buildQueueList.getModel();
for (Object type : buildQueueList.getSelectedValues()) {
model.removeElement(type);
}
updateAllLists();
}
};
buildQueueList.getInputMap().put(KeyStroke.getKeyStroke("DELETE"), "delete");
buildQueueList.getActionMap().put("delete", deleteAction);
Action addAction = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
DefaultListModel model = (DefaultListModel) buildQueueList.getModel();
for (Object type : ((JList) e.getSource()).getSelectedValues()) {
model.addElement(type);
}
updateAllLists();
}
};
BuildQueueMouseAdapter adapter = new BuildQueueMouseAdapter(true);
DefaultListModel units = new DefaultListModel();
unitList = new JList(units);
unitList.setTransferHandler(buildQueueHandler);
unitList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
unitList.setDragEnabled(true);
unitList.setCellRenderer(cellRenderer);
unitList.addMouseListener(adapter);
unitList.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "add");
unitList.getActionMap().put("add", addAction);
DefaultListModel buildings = new DefaultListModel();
buildingList = new JList(buildings);
buildingList.setTransferHandler(buildQueueHandler);
buildingList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
buildingList.setDragEnabled(true);
buildingList.setCellRenderer(cellRenderer);
buildingList.addMouseListener(adapter);
buildingList.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "add");
buildingList.getActionMap().put("add", addAction);
JLabel headLine = new JLabel(Messages.message("colonyPanel.buildQueue"));
headLine.setFont(bigHeaderFont);
buyBuilding = new JButton(Messages.message("colonyPanel.buyBuilding"));
buyBuilding.setActionCommand(BUY);
buyBuilding.addActionListener(this);
constructionPanel = new ConstructionPanel(freeColClient, gui, colony, false);
constructionPanel.setOpaque(false);
StringTemplate buildingNothing = StringTemplate.template("colonyPanel.currentlyBuilding")
.add("%buildable%", "nothing");
constructionPanel.setDefaultLabel(buildingNothing);
updateAllLists();
add(headLine, "span 3, align center, wrap 40");
add(new JLabel(Messages.message("colonyPanel.units")), "align center");
add(new JLabel(Messages.message("colonyPanel.buildQueue")), "align center");
add(new JLabel(Messages.message("colonyPanel.buildings")), "align center");
add(new JScrollPane(unitList), "grow");
add(constructionPanel, "split 2, flowy");
add(new JScrollPane(buildQueueList), "grow");
add(new JScrollPane(buildingList), "grow, wrap 20");
add(buyBuilding, "span, split 4");
add(compact);
add(showAll);
add(okButton, "tag ok");
}
private void updateUnitList() {
DefaultListModel units = (DefaultListModel) unitList.getModel();
units.clear();
loop: for (UnitType unitType : buildableUnits) {
// compare colony.getNoBuildReason()
List<String> lockReason = new ArrayList<String>();
if (unbuildableTypes.contains(unitType)) {
continue;
}
if (unitType.getPopulationRequired() > unitCount) {
lockReason.add(Messages.message(StringTemplate.template("colonyPanel.populationTooSmall")
.addAmount("%number%", unitType.getPopulationRequired())));
}
if (unitType.getLimits() != null) {
for (Limit limit : unitType.getLimits()) {
if (!limit.evaluate(colony)) {
lockReason.add(Messages.message(limit.getDescriptionKey()));
}
}
}
if (!(colony.getFeatureContainer()
.hasAbility(Ability.BUILD, unitType, getGame().getTurn())
|| featureContainer.hasAbility(Ability.BUILD, unitType))) {
boolean builderFound = false;
for (Ability ability : getSpecification().getAbilities(Ability.BUILD)) {
if (ability.appliesTo(unitType)
&& ability.getValue()
&& ability.getSource() != null
&& !unbuildableTypes.contains(ability.getSource())) {
builderFound = true;
lockReason.add(Messages.message(ability.getSource().getNameKey()));
break;
}
}
if (!builderFound) {
unbuildableTypes.add(unitType);
continue;
}
}
Map<String, Boolean> requiredAbilities = unitType.getAbilitiesRequired();
for (Entry<String, Boolean> entry : requiredAbilities.entrySet()) {
if (colony.hasAbility(entry.getKey()) != entry.getValue()
&& featureContainer.hasAbility(entry.getKey()) != entry.getValue()) {
List<FreeColGameObjectType> sources = getSpecification()
.getTypesProviding(entry.getKey(), entry.getValue());
if (sources.isEmpty()) {
// no type provides the required ability
unbuildableTypes.add(unitType);
continue loop;
} else {
lockReason.add(Messages.message(sources.get(0).getNameKey()));
}
}
}
if (lockReason.isEmpty()) {
lockReasons.put(unitType, null);
} else {
lockReasons.put(unitType, Messages.message(StringTemplate.template("colonyPanel.requires")
.addName("%string%", Utils.join("/", lockReason))));
}
if (lockReason.isEmpty() || showAll.isSelected()) {
units.addElement(unitType);
}
}
}
private void updateBuildingList() {
DefaultListModel buildings = (DefaultListModel) buildingList.getModel();
DefaultListModel current = (DefaultListModel) buildQueueList.getModel();
buildings.clear();
loop: for (BuildingType buildingType : getSpecification().getBuildingTypeList()) {
// compare colony.getNoBuildReason()
List<String> lockReason = new ArrayList<String>();
Building colonyBuilding = colony.getBuilding(buildingType);
if (current.contains(buildingType) || hasBuildingType(buildingType)) {
// only one building of any kind
continue;
} else if (unbuildableTypes.contains(buildingType)) {
continue;
} else if (buildingType.getGoodsRequired().isEmpty()) {
// pre-built
continue;
} else if (unbuildableTypes.contains(buildingType.getUpgradesFrom())) {
// impossible upgrade path
unbuildableTypes.add(buildingType);
continue;
}
if (buildingType.getPopulationRequired() > unitCount) {
lockReason.add(Messages.message(StringTemplate.template("colonyPanel.populationTooSmall")
.addAmount("%number%", buildingType.getPopulationRequired())));
}
Map<String, Boolean> requiredAbilities = buildingType.getAbilitiesRequired();
for (Entry<String, Boolean> entry : requiredAbilities.entrySet()) {
if (colony.hasAbility(entry.getKey()) != entry.getValue()
&& featureContainer.hasAbility(entry.getKey()) != entry.getValue()) {
List<FreeColGameObjectType> sources = getSpecification()
.getTypesProviding(entry.getKey(), entry.getValue());
if (sources.isEmpty()) {
// no type provides the required ability
unbuildableTypes.add(buildingType);
continue loop;
} else {
lockReason.add(Messages.message(sources.get(0).getNameKey()));
}
}
}
if (buildingType.getLimits() != null) {
for (Limit limit : buildingType.getLimits()) {
if (!limit.evaluate(colony)) {
lockReason.add(Messages.message(limit.getDescriptionKey()));
}
}
}
if (buildingType.getUpgradesFrom() != null
&& !current.contains(buildingType.getUpgradesFrom())) {
if (colonyBuilding == null
|| colonyBuilding.getType() != buildingType.getUpgradesFrom()) {
lockReason.add(Messages.message(buildingType.getUpgradesFrom().getNameKey()));
}
}
if (lockReason.isEmpty()) {
lockReasons.put(buildingType, null);
} else {
lockReasons.put(buildingType, Messages.message(StringTemplate.template("colonyPanel.requires")
.addName("%string%", Utils.join("/", lockReason))));
}
if (lockReason.isEmpty() || showAll.isSelected()) {
buildings.addElement(buildingType);
}
}
}
private void updateAllLists() {
DefaultListModel current = (DefaultListModel) buildQueueList.getModel();
featureContainer = new FeatureContainer();
for (Object type: current.toArray()) {
if (getMinimumIndex((BuildableType) type) >= 0) {
featureContainer.add(((BuildableType) type).getFeatureContainer());
} else {
current.removeElement(type);
}
}
// ATTENTION: buildings must be updated first, since units
// might depend on the build ability of an unbuildable
// building
updateBuildingList();
updateUnitList();
updateBuyBuildingButton();
// work-around to re-initialize construction panel
PropertyChangeEvent event = new PropertyChangeEvent(colony, ConstructionPanel.EVENT, null,
getBuildableTypes(buildQueueList));
constructionPanel.propertyChange(event);
}
private void updateBuyBuildingButton() {
DefaultListModel current = (DefaultListModel) buildQueueList.getModel();
if (current.getSize() == 0) {
buyBuilding.setEnabled(false);
} else {
buyBuilding.setEnabled(colony.canPayToFinishBuilding((BuildableType) current.getElementAt(0)));
}
}
private boolean hasBuildingType(BuildingType buildingType) {
if (colony.getBuilding(buildingType) == null) {
return false;
} else if (colony.getBuilding(buildingType).getType() == buildingType) {
return true;
} else if (buildingType.getUpgradesTo() != null) {
return hasBuildingType(buildingType.getUpgradesTo());
} else {
return false;
}
}
private List<BuildableType> getBuildableTypes(JList list) {
List<BuildableType> result = new ArrayList<BuildableType>();
if (list != null) {
ListModel model = list.getModel();
for (int index = 0; index < model.getSize(); index++) {
Object object = model.getElementAt(index);
if (object instanceof BuildableType) {
result.add((BuildableType) object);
}
}
}
return result;
}
private List<BuildableType> getBuildableTypes(Object[] objects) {
List<BuildableType> result = new ArrayList<BuildableType>();
if (objects != null) {
for (Object object : objects) {
if (object instanceof BuildableType) {
result.add((BuildableType) object);
}
}
}
return result;
}
private int getMinimumIndex(BuildableType buildableType) {
ListModel buildQueue = buildQueueList.getModel();
if (buildableType instanceof UnitType) {
if (colony.canBuild(buildableType)) {
return 0;
} else {
for (int index = 0; index < buildQueue.getSize(); index++) {
if (((BuildableType) buildQueue.getElementAt(index))
.hasAbility(Ability.BUILD, buildableType)) {
return index + 1;
}
}
}
} else if (buildableType instanceof BuildingType) {
BuildingType upgradesFrom = ((BuildingType) buildableType).getUpgradesFrom();
if (upgradesFrom == null) {
return 0;
} else {
Building building = colony.getBuilding((BuildingType) buildableType);
BuildingType buildingType = (building == null) ? null : building.getType();
if (buildingType == upgradesFrom) {
return 0;
} else {
for (int index = 0; index < buildQueue.getSize(); index++) {
if (upgradesFrom.equals(buildQueue.getElementAt(index))) {
return index + 1;
}
}
}
}
}
return UNABLE_TO_BUILD;
}
private int getMaximumIndex(BuildableType buildableType) {
ListModel buildQueue = buildQueueList.getModel();
final int buildQueueLastPos = buildQueue.getSize();
boolean canBuild = false;
if (colony.canBuild(buildableType)) {
canBuild = true;
}
if (buildableType instanceof UnitType) {
// does not depend on anything, nothing depends on it
// can be built at any time
if(canBuild){
return buildQueueLastPos;
}
// check for building in queue that allows builting this unit
for (int index = 0; index < buildQueue.getSize(); index++) {
BuildableType toBuild = (BuildableType) buildQueue.getElementAt(index);
if(toBuild == buildableType){
continue;
}
if (toBuild.hasAbility(Ability.BUILD, buildableType)) {
return buildQueueLastPos;
}
}
return UNABLE_TO_BUILD;
}
if (buildableType instanceof BuildingType) {
BuildingType upgradesFrom = ((BuildingType) buildableType).getUpgradesFrom();
BuildingType upgradesTo = ((BuildingType) buildableType).getUpgradesTo();
// does not depend on nothing, but still cannot be built
if (!canBuild && upgradesFrom == null) {
return UNABLE_TO_BUILD;
}
// if can be built and does not have any upgrade,
//then it can be built at any time
if(canBuild && upgradesTo == null){
return buildQueueLastPos;
}
// if can be built, does not depend on anything, mark upgradesfrom as found
boolean foundUpgradesFrom = canBuild? true:false;
for (int index = 0; index < buildQueue.getSize(); index++) {
BuildableType toBuild = (BuildableType) buildQueue.getElementAt(index);
if(toBuild == buildableType){
continue;
}
if (!canBuild && !foundUpgradesFrom && upgradesFrom.equals(toBuild)) {
foundUpgradesFrom = true;
// nothing else to upgrade this building to
if(upgradesTo == null){
return buildQueueLastPos;
}
}
// found a building it upgrades to, cannot go to or beyond this position
if (foundUpgradesFrom && upgradesTo != null && upgradesTo.equals(toBuild)) {
return index;
}
// Don't go past a unit this building can build.
if (buildableType.hasAbility(Ability.BUILD, toBuild)) {
return index;
}
}
return buildQueueLastPos;
}
return UNABLE_TO_BUILD;
}
/**
* This function analyses an event and calls the right methods to take
* care of the user's requests.
* @param event The incoming ActionEvent.
*/
@Override
public void actionPerformed(ActionEvent event) {
if (colony.getOwner() == getMyPlayer()) {
String command = event.getActionCommand();
List<BuildableType> buildables = getBuildableTypes(buildQueueList);
if (!buildables.isEmpty() && lockReasons.get(buildables.get(0)) != null) {
getGUI().showInformationMessage(buildables.get(0),
StringTemplate.template("colonyPanel.unbuildable")
.addName("%colony%", colony.getName())
.add("%object%", buildables.get(0).getNameKey()));
return;
}
getController().setBuildQueue(colony, buildables);
if (OK.equals(command)) {
// do nothing?
} else if (BUY.equals(command)) {
getController().payForBuilding(colony);
} else {
logger.warning("Unsupported command " + command);
}
}
getGUI().removeFromCanvas(this);
}
public void itemStateChanged(ItemEvent event) {
if (event.getSource() == compact) {
updateDetailView();
} else if (event.getSource() == showAll) {
updateAllLists();
}
}
private void updateDetailView() {
cellRenderer = getCellRenderer();
buildQueueList.setCellRenderer(cellRenderer);
buildingList.setCellRenderer(cellRenderer);
unitList.setCellRenderer(cellRenderer);
}
private ListCellRenderer getCellRenderer() {
if (compact.isSelected()) {
if (cellRenderer == null || cellRenderer instanceof DefaultBuildQueueCellRenderer) {
return new SimpleBuildQueueCellRenderer();
}
} else if (cellRenderer == null || cellRenderer instanceof SimpleBuildQueueCellRenderer) {
return new DefaultBuildQueueCellRenderer();
}
// return current one
return cellRenderer;
}
/**
* This class implements a transfer handler able to transfer
* <code>BuildQueueItem</code>s between the build queue list, the
* unit list and the building list.
*/
public class BuildQueueTransferHandler extends TransferHandler {
private final DataFlavor buildQueueFlavor = new DataFlavor(List.class, "BuildingQueueFlavor");
JList source = null;
int[] indices = null;
int numberOfItems = 0; // number of items to be added
/**
* Imports a build queue into the build queue list, the
* building list or the unit list, if possible.
* @param comp The list which imports data.
* @param data The build queue to import.
* @return Whether the import was successful.
*/
@Override
public boolean importData(JComponent comp, Transferable data) {
if (!canImport(comp, data.getTransferDataFlavors())) {
return false;
}
JList target = null;
List<BuildableType> buildQueue = new ArrayList<BuildableType>();
DefaultListModel targetModel;
try {
target = (JList) comp;
targetModel = (DefaultListModel) target.getModel();
Object transferData = data.getTransferData(buildQueueFlavor);
if (!(transferData instanceof List<?>)) {
return false;
}
for (Object object : (List<?>) transferData) {
// first we need to make sure all items are compatible
//with the drop zone
if (!(object instanceof BuildableType)) {
return false;
}
// check if trying to drop units in the Building list
//or buildings in the Unit List
if ((object instanceof BuildingType && target == unitList)
|| (object instanceof UnitType && target == buildingList)) {
return false;
}
// Object check complete
// Add object to the to-add list
buildQueue.add((BuildableType) object);
}
} catch (Exception e) {
logger.warning(e.toString());
return false;
}
// check if every Buildable can be added to the queue
for (BuildableType type : buildQueue) {
if (getMinimumIndex(type) < 0) {
return false;
}
}
// This is where the dragged target was dropped.
int preferredIndex = target.getSelectedIndex();
boolean isOrderingQueue = false;
int prevPos = -1;
if (source.equals(target)) {
// only the build queue allows ordering
if (target != buildQueueList && target.getParent() != buildQueueList) {
return false;
}
// find previous position
for(int i=0; i < targetModel.getSize(); i++){
if(targetModel.getElementAt(i) == buildQueue.get(0)){
prevPos = i;
break;
}
}
// don't drop selection on itself
if(preferredIndex != -1 && prevPos == preferredIndex){
indices = null;
return false;
}
int maximumIndex = getMaximumIndex(buildQueue.get(0));
if(preferredIndex > maximumIndex){
indices = null;
return false;
}
isOrderingQueue = true;
numberOfItems = buildQueue.size();
}
else if (source == buildQueueList) {
// Dragging out of build queue - just remove the element.
// updateAllLists takes care of the rest.
DefaultListModel sourceModel = (DefaultListModel) source.getModel();
for(Object obj : buildQueue) {
sourceModel.removeElement(obj);
}
return true;
}
int maxIndex = targetModel.size();
// Set index to add to the last position, if not set or out-of-bounds
if (preferredIndex < 0 || preferredIndex > maxIndex) {
preferredIndex = maxIndex;
}
if(isOrderingQueue){
// Remove all elements in the build queue from the target.
for(Object obj : buildQueue){
targetModel.removeElement(obj);
}
}
for (int index = 0; index < buildQueue.size(); index++) {
BuildableType toBuild = (BuildableType) buildQueue.get(index);
int minimumIndex = getMinimumIndex(toBuild);
// minimumIndex == targetModel.size() means it has to go at the
// end.
if (minimumIndex < targetModel.size())
{
int maximumIndex = getMaximumIndex(toBuild);
// minimumIndex == maximumIndex means there is only one
// place it can go.
if (minimumIndex != maximumIndex)
{
if (minimumIndex < preferredIndex + index) {
minimumIndex = preferredIndex + index;
}
if (minimumIndex > targetModel.size())
{
minimumIndex = targetModel.size();
}
if (minimumIndex > maximumIndex)
{
minimumIndex = maximumIndex;
}
}
}
targetModel.add(minimumIndex,buildQueue.get(index));
}
// update selected index to new position
if(isOrderingQueue){
buildQueueList.setSelectedIndex(preferredIndex);
}
return true;
}
/**
* Cleans up after a successful import.
* @param source The component that has exported data.
* @param data The data exported.
* @param action The transfer action, e.g. MOVE.
*/
@Override
protected void exportDone(JComponent source, Transferable data, int action) {
// clean up
indices = null;
numberOfItems = 0;
updateAllLists();
}
/**
* Returns <code>true</code> if the component can import this
* data flavor.
* @param comp The component to import data.
* @param flavors An array of data flavors.
*/
@Override
public boolean canImport(JComponent comp, DataFlavor[] flavors) {
if (flavors == null) {
return false;
} else {
for (DataFlavor flavor : flavors) {
if (flavor.equals(buildQueueFlavor)) {
return true;
}
}
return false;
}
}
/**
* Returns a <code>Transferable</code> suitable for wrapping
* the build queue.
* @param comp The source of the build queue.
* @return A Transferable suitable for wrapping the build
* queue.
*/
@Override
protected Transferable createTransferable(JComponent comp) {
if (comp instanceof JList) {
source = (JList) comp;
indices = source.getSelectedIndices();
List<BuildableType> buildQueue = getBuildableTypes(source.getSelectedValues());
return new BuildQueueTransferable(buildQueue);
} else {
return null;
}
}
/**
* Returns the possible source actions of the component.
* @param comp The source component.
* @return The possible source actions of the component.
*/
@Override
public int getSourceActions(JComponent comp) {
if (comp == unitList) {
return COPY;
} else {
return MOVE;
}
}
/**
* This class implements the <code>Transferable</code> interface.
*/
public class BuildQueueTransferable implements Transferable {
private List<BuildableType> buildQueue;
private final DataFlavor[] supportedFlavors = new DataFlavor[] {
buildQueueFlavor
};
/**
* Default constructor.
* @param buildQueue The build queue to transfer.
*/
public BuildQueueTransferable(List<BuildableType> buildQueue) {
this.buildQueue = buildQueue;
}
/**
* Returns the build queue from the <code>Transferable</code>.
* @param flavor The data flavor to use.
* @return The build queue from the <code>Transferable</code>.
*/
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException {
if (isDataFlavorSupported(flavor)) {
return buildQueue;
} else {
throw new UnsupportedFlavorException(flavor);
}
}
/**
* Returns the build queue from the <code>Transferable</code>.
*
* @return The build queue from the <code>Transferable</code>.
*/
public List<BuildableType> getBuildQueue() {
return buildQueue;
}
/**
* Returns an array of supported data flavors.
* @return An array of supported data flavors.
*/
public DataFlavor[] getTransferDataFlavors() {
return supportedFlavors;
}
/**
* Returns <code>true</code> if this data flavor is supported.
* @param flavor The data flavor.
* @return Whether this data flavor is supported.
*/
public boolean isDataFlavorSupported(DataFlavor flavor) {
for (DataFlavor myFlavor : supportedFlavors) {
if (myFlavor.equals(flavor)) {
return true;
}
}
return false;
}
}
}
class SimpleBuildQueueCellRenderer extends FreeColComboBoxRenderer {
@Override
public void setLabelValues(JLabel c, Object value) {
c.setText(Messages.message(((BuildableType) value).getNameKey()));
}
}
class DefaultBuildQueueCellRenderer implements ListCellRenderer {
JPanel itemPanel = new JPanel();
JPanel selectedPanel = new JPanel();
JLabel imageLabel = new JLabel(new ImageIcon());
JLabel nameLabel = new JLabel();
private JLabel lockLabel =
new JLabel(new ImageIcon(ResourceManager.getImage("lock.image", 0.5)));
private Dimension buildingDimension = new Dimension(-1, 48);
public DefaultBuildQueueCellRenderer() {
itemPanel.setOpaque(false);
itemPanel.setLayout(new MigLayout());
selectedPanel.setOpaque(false);
selectedPanel.setLayout(new MigLayout());
selectedPanel.setUI((PanelUI) FreeColSelectedPanelUI.createUI(selectedPanel));
}
public Component getListCellRendererComponent(JList list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus) {
BuildableType item = (BuildableType) value;
JPanel panel = isSelected ? selectedPanel : itemPanel;
panel.removeAll();
((ImageIcon) imageLabel.getIcon()).setImage(ResourceManager.getImage(item.getId() + ".image",
buildingDimension));
nameLabel.setText(Messages.message(item.getNameKey()));
panel.setToolTipText(lockReasons.get(item));
panel.add(imageLabel, "span 1 2");
if (lockReasons.get(item) == null) {
panel.add(nameLabel, "wrap");
} else {
panel.add(nameLabel, "split 2");
panel.add(lockLabel, "wrap");
}
List<AbstractGoods> goodsRequired = item.getGoodsRequired();
int size = goodsRequired.size();
for (int i = 0; i < size; i++) {
AbstractGoods goods = goodsRequired.get(i);
ImageIcon icon = new ImageIcon(ResourceManager.getImage(goods.getType().getId() + ".image", 0.66));
JLabel goodsLabel =
new JLabel(Integer.toString(goods.getAmount()), icon, SwingConstants.CENTER);
if (i == 0 && size > 1) {
panel.add(goodsLabel, "split " + size);
} else {
panel.add(goodsLabel);
}
}
return panel;
}
}
class BuildQueueMouseAdapter extends MouseAdapter {
private boolean add = true;
private boolean enabled = false;
public BuildQueueMouseAdapter(boolean add) {
this.add = add;
}
@Override
public void mousePressed(MouseEvent e) {
if (!enabled && e.getClickCount() == 1 && !e.isConsumed()) {
enabled = true;
}
if (enabled) {
JList source = (JList) e.getSource();
if ((e.getButton() == MouseEvent.BUTTON3 || e.isPopupTrigger())) {
int index = source.locationToIndex(e.getPoint());
BuildableType type = (BuildableType) source.getModel().getElementAt(index);
getGUI().showColopediaPanel(type.getId());
} else if ((e.getClickCount() > 1 && !e.isConsumed())) {
DefaultListModel model = (DefaultListModel) buildQueueList.getModel();
if (source.getSelectedIndex() == -1) {
source.setSelectedIndex(source.locationToIndex(e.getPoint()));
}
for (Object type : source.getSelectedValues()) {
if (add) {
model.addElement(type);
} else {
model.removeElement(type);
}
}
updateAllLists();
}
}
}
}
}