/** * 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(); } } } } }