/** * 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.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Logger; import javax.swing.ImageIcon; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import net.sf.freecol.client.FreeColClient; import net.sf.freecol.client.control.InGameController; import net.sf.freecol.client.gui.GUI; import net.sf.freecol.client.gui.ImageLibrary; import net.sf.freecol.client.gui.i18n.Messages; import net.sf.freecol.client.gui.panel.UnitLabel.UnitAction; import net.sf.freecol.common.model.Ability; import net.sf.freecol.common.model.AbstractGoods; import net.sf.freecol.common.model.Building; import net.sf.freecol.common.model.Colony; import net.sf.freecol.common.model.EquipmentType; import net.sf.freecol.common.model.Europe; import net.sf.freecol.common.model.GameOptions; import net.sf.freecol.common.model.Goods; import net.sf.freecol.common.model.GoodsType; import net.sf.freecol.common.model.Specification; import net.sf.freecol.common.model.StringTemplate; import net.sf.freecol.common.model.Unit; import net.sf.freecol.common.model.Unit.Role; import net.sf.freecol.common.model.Unit.UnitState; import net.sf.freecol.common.model.UnitType; import net.sf.freecol.common.model.UnitTypeChange.ChangeType; import net.sf.freecol.common.model.WorkLocation; /** * Handles the generation of popup menu's generated by * draglistner objects attached to units within the * Colony and Europe panels. * @author Brian */ public final class QuickActionMenu extends JPopupMenu { private static final Logger logger = Logger.getLogger(QuickActionMenu.class.getName()); private final FreeColPanel parentPanel; private FreeColClient freeColClient; private GUI gui; /** * Creates a standard empty menu */ public QuickActionMenu(FreeColClient freeColClient, GUI gui, FreeColPanel freeColPanel) { this.freeColClient = freeColClient; this.gui = gui; this.parentPanel = freeColPanel; } /** * Creates a popup menu for a Unit. */ public void createUnitMenu(final UnitLabel unitLabel) { ImageLibrary imageLibrary = parentPanel.getLibrary(); final Unit tempUnit = unitLabel.getUnit(); this.setLabel("Unit"); ImageIcon unitIcon = imageLibrary.getUnitImageIcon(tempUnit, 0.66); JMenuItem name = new JMenuItem(Messages.message(tempUnit.getLabel()) + " (" + Messages.message("menuBar.colopedia") + ")", unitIcon); name.setActionCommand(UnitAction.COLOPEDIA.toString()); name.addActionListener(unitLabel); this.add(name); this.addSeparator(); if (tempUnit.isCarrier()) { if (addCarrierItems(unitLabel)) { this.addSeparator(); } } if (tempUnit.getLocation().getTile() != null && tempUnit.getLocation().getTile().getColony() != null) { if (addWorkItems(unitLabel)) { this.addSeparator(); } if (addEducationItems(unitLabel)) { this.addSeparator(); } if (tempUnit.getLocation() instanceof WorkLocation) { if (tempUnit.getColony().canReducePopulation()) { JMenuItem menuItem = new JMenuItem(Messages.message("leaveTown")); menuItem.setActionCommand(UnitAction.LEAVE_TOWN.toString()); menuItem.addActionListener(unitLabel); this.add(menuItem); } if(!tempUnit.isCarrier()){ if(tempUnit.getColony() != null){ for (Unit unit : tempUnit.getColony().getTile().getUnitList()) { if (unit.isCarrier() && unit.canCarryUnits() && unit.canAdd(tempUnit) && !(unit.getUnitList().contains(tempUnit))) { final Unit funit = unit; JMenuItem menuItem = new JMenuItem(Messages.message("Board " + Messages.message(unit.getLabel()))); menuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e){ if (freeColClient.getInGameController().putOutsideColony(tempUnit)) { freeColClient.getInGameController().boardShip(tempUnit, funit); } } }); this.add(menuItem); } } } } } else { if (addCommandItems(unitLabel)) { this.addSeparator(); } } } else if (tempUnit.getLocation() instanceof Europe) { if (addCommandItems(unitLabel)) { this.addSeparator(); } } if (tempUnit.hasAbility(Ability.CAN_BE_EQUIPPED)) { if (addEquipmentItems(unitLabel)) { this.addSeparator(); } } } private boolean addCarrierItems(final UnitLabel unitLabel) { final Unit tempUnit = unitLabel.getUnit(); if (tempUnit.hasCargo()) { JMenuItem cargo = new JMenuItem(Messages.message("cargoOnCarrier")); this.add(cargo); for (Unit passenger : tempUnit.getUnitList()) { JMenuItem menuItem = new JMenuItem(" " + Messages.message(passenger.getLabel())); menuItem.setFont(menuItem.getFont().deriveFont(Font.ITALIC)); this.add(menuItem); } for (Goods goods : tempUnit.getGoodsList()) { JMenuItem menuItem = new JMenuItem(" " + Messages.message(goods.getLabel(true))); menuItem.setFont(menuItem.getFont().deriveFont(Font.ITALIC)); this.add(menuItem); } return true; } else { return false; } } private List<JMenuItem> descendingList(final Map<JMenuItem, Integer> map) { List<JMenuItem> ret = new ArrayList<JMenuItem>(map.keySet()); Collections.sort(ret, new Comparator<JMenuItem>() { public int compare(JMenuItem m1, JMenuItem m2) { Integer i1 = map.get(m1); Integer i2 = map.get(m2); int cmp = i2.compareTo(i1); if (cmp == 0) cmp = m1.getText().compareTo(m2.getText()); return cmp; } }); return ret; } private JMenuItem makeProductionItem(GoodsType type, WorkLocation wl, int amount, UnitLabel unitLabel, boolean claim) { StringTemplate t = StringTemplate.template(type.getId() + ".workAs") .addAmount("%amount%", amount); if (claim) { t.addStringTemplate("%claim%", wl.getClaimTemplate()); } else { t.addName("%claim%", ""); } JMenuItem menuItem = new JMenuItem(Messages.message(t), parentPanel.getLibrary().getScaledGoodsImageIcon(type, 0.66f)); menuItem.setActionCommand(UnitLabel.getWorkLabel(wl) + "/" + wl.getId() + "/" + type.getId() + "/" + ((claim) ? "!" : "")); menuItem.addActionListener(unitLabel); return menuItem; } private boolean addWorkItems(final UnitLabel unitLabel) { final int currentItems = this.getComponentCount(); final Unit unit = unitLabel.getUnit(); final UnitType unitType = unit.getType(); final GoodsType expertGoods = unitType.getExpertProduction(); final Colony colony = unit.getLocation().getColony(); final Specification spec = freeColClient.getGame().getSpecification(); WorkLocation current = (unit.getLocation() instanceof WorkLocation) ? (WorkLocation)unit.getLocation() : null; final int bonusChange = (current != null) ? 0 : colony.governmentChange(colony.getWorkLocationUnitCount() + 1); Map<JMenuItem, Integer> items = new HashMap<JMenuItem, Integer>(); Map<JMenuItem, Integer> extras = new HashMap<JMenuItem, Integer>(); JMenuItem expertOwned = null; JMenuItem expertUnowned = null; for (GoodsType type : spec.getGoodsTypeList()) { int bestOwnedProd = 0; int bestUnownedProd = 0; WorkLocation bestOwned = null; WorkLocation bestUnowned = null; for (WorkLocation wl : colony.getAllWorkLocations()) { int prod = bonusChange; switch (wl.getNoAddReason(unit)) { case NONE: prod += wl.getPotentialProduction(type, unitType); if (prod > bestOwnedProd) { bestOwnedProd = prod; bestOwned = wl; } break; case ALREADY_PRESENT: prod += wl.getPotentialProduction(type, unitType); if (prod > bestOwnedProd) { bestOwnedProd = prod; bestOwned = (unit.getWorkType() == type) ? null : wl; } break; case CLAIM_REQUIRED: prod += wl.getPotentialProduction(type, unitType); if (prod > bestUnownedProd) { bestUnownedProd = prod; bestUnowned = wl; } break; default: break; } } if (bestOwned != null) { JMenuItem ji = makeProductionItem(type, bestOwned, bestOwnedProd, unitLabel, false); if (type == expertGoods) { expertOwned = ji; } else { items.put(ji, new Integer(bestOwnedProd)); } } if (bestUnowned != null && bestUnownedProd > bestOwnedProd) { JMenuItem ji = makeProductionItem(type, bestUnowned, bestUnownedProd, unitLabel, true); if (type == expertGoods) { expertUnowned = ji; } else { extras.put(ji, new Integer(bestUnownedProd)); } } } List<JMenuItem> owned = descendingList(items); if (expertOwned != null) owned.add(0, expertOwned); for (JMenuItem j : owned) this.add(j); List<JMenuItem> unowned = descendingList(extras); if (expertUnowned != null) unowned.add(0, expertUnowned); if (!unowned.isEmpty()) { if (!owned.isEmpty()) this.addSeparator(); for (JMenuItem j : unowned) this.add(j); } if (current != null) { JMenuItem ji = new JMenuItem(Messages.message("showProductivity")); ji.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { gui.showWorkProductionPanel(unit); } }); this.add(ji); } return this.getComponentCount() != currentItems; } private boolean addEducationItems(final UnitLabel unitLabel) { boolean separatorNeeded = false; Unit unit = unitLabel.getUnit(); ImageLibrary imageLibrary = parentPanel.getLibrary(); if (unit.getSpecification().getBoolean(GameOptions.ALLOW_STUDENT_SELECTION)) { for (Unit teacher : unit.getColony().getTeachers()) { if (unit.canBeStudent(teacher) && (unit.getLocation() instanceof WorkLocation)) { JMenuItem menuItem = null; ImageIcon teacherIcon = imageLibrary.getUnitImageIcon(teacher, 0.5); if (teacher.getStudent() != unit) { String assign = Messages.message("assignToTeacher"); if (teacher.getStudent() != null) { assign += " (" + teacher.getTurnsOfTraining() + "/" + teacher.getNeededTurnsOfTraining() + ")"; } menuItem = new JMenuItem(assign, teacherIcon); menuItem.setActionCommand(UnitAction.ASSIGN.toString() + "/" + teacher.getId()); menuItem.addActionListener(unitLabel); } else { String teacherName = Messages.message(teacher.getType().getNameKey()); menuItem = new JMenuItem(Messages.message(StringTemplate .template("menu.unit.apprentice") .addName("%unit%", teacherName)) + ": " + teacher.getTurnsOfTraining() + "/" + teacher.getNeededTurnsOfTraining(), teacherIcon); menuItem.setEnabled(false); } this.add(menuItem); separatorNeeded = true; } } } if (unit.getStudent() != null) { Unit student = unit.getStudent(); String studentName = Messages.message(student.getType().getNameKey()); JMenuItem menuItem = new JMenuItem(Messages.message(StringTemplate .template("menuBar.teacher") .addName("%unit%", studentName)) + ": " + unit.getTurnsOfTraining() + "/" + unit.getNeededTurnsOfTraining()); menuItem.setEnabled(false); this.add(menuItem); separatorNeeded = true; } int experience = unit.getExperience(); GoodsType goods = unit.getExperienceType(); if (experience > 0 && goods != null) { UnitType expertType = freeColClient.getGame().getSpecification().getExpertForProducing(goods); if (unit.getType().canBeUpgraded(expertType, ChangeType.EXPERIENCE)) { int maxExperience = unit.getType().getMaximumExperience(); double probability = unit.getType().getUnitTypeChange(expertType) .getProbability(ChangeType.EXPERIENCE) * experience / (double) maxExperience; String jobName = Messages.message(goods.getWorkingAsKey()); ImageIcon expertIcon = imageLibrary.getUnitImageIcon(expertType, 0.5); JMenuItem experienceItem = new JMenuItem(Messages.message(StringTemplate.template("menu.unit.experience") .addName("%job%", jobName)) + " " + experience + "/" + maxExperience + " (" + FreeColPanel.getModifierFormat().format(probability) + "%)", expertIcon); experienceItem.setEnabled(false); this.add(experienceItem); separatorNeeded = true; } } return separatorNeeded; } private boolean addCommandItems(final UnitLabel unitLabel) { final Unit tempUnit = unitLabel.getUnit(); final boolean isUnitAtSea = tempUnit.isAtSea(); JMenuItem menuItem = new JMenuItem(Messages.message("activateUnit")); menuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (tempUnit.getState() != Unit.UnitState.ACTIVE) { freeColClient.getInGameController() .changeState(tempUnit, Unit.UnitState.ACTIVE); } gui.setActiveUnit(tempUnit); } }); menuItem.setEnabled(!isUnitAtSea); this.add(menuItem); if (!(tempUnit.getLocation() instanceof Europe)) { menuItem = new JMenuItem(Messages.message("fortifyUnit")); menuItem.setActionCommand(UnitAction.FORTIFY.toString()); menuItem.addActionListener(unitLabel); menuItem.setEnabled((tempUnit.getMovesLeft() > 0) && !(tempUnit.getState() == Unit.UnitState.FORTIFIED || tempUnit.getState() == Unit.UnitState.FORTIFYING)); this.add(menuItem); } UnitState unitState = tempUnit.getState(); menuItem = new JMenuItem(Messages.message("sentryUnit")); menuItem.setActionCommand(UnitAction.SENTRY.toString()); menuItem.addActionListener(unitLabel); menuItem.setEnabled(unitState != Unit.UnitState.SENTRY && !isUnitAtSea); this.add(menuItem); boolean hasTradeRoute = tempUnit.getTradeRoute() != null; menuItem = new JMenuItem(Messages.message("clearUnitOrders")); menuItem.setActionCommand(UnitAction.CLEAR_ORDERS.toString()); menuItem.addActionListener(unitLabel); menuItem.setEnabled((unitState != Unit.UnitState.ACTIVE || hasTradeRoute) && !isUnitAtSea); this.add(menuItem); menuItem = new JMenuItem(Messages.message("assignTradeRoute")); menuItem.setActionCommand(UnitAction.ASSIGN_TRADE_ROUTE.toString()); menuItem.addActionListener(unitLabel); menuItem.setEnabled(tempUnit.isCarrier() && !hasTradeRoute); this.add(menuItem); if (tempUnit.canCarryTreasure() && tempUnit.canCashInTreasureTrain()) { menuItem = new JMenuItem(Messages.message("cashInTreasureTrain.order")); menuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { freeColClient.getInGameController() .checkCashInTreasureTrain(tempUnit); } }); this.add(menuItem); } if(tempUnit.getColony() != null){ if(!tempUnit.isCarrier()){ for (Unit unit : tempUnit.getColony().getTile().getUnitList()){ if (unit.isCarrier() && unit.canCarryUnits()) { if (tempUnit.isOnCarrier()) { menuItem = new JMenuItem(Messages.message("leaveTown")); menuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e){ freeColClient.getInGameController().putOutsideColony(tempUnit); } }); } this.add(menuItem); } } } } return true; } private boolean addEquipmentItems(final UnitLabel unitLabel) { final Unit tempUnit = unitLabel.getUnit(); final InGameController igc = freeColClient.getInGameController(); ImageLibrary imageLibrary = parentPanel.getLibrary(); boolean separatorNeeded = false; if (tempUnit.getEquipment().size() > 1) { JMenuItem newItem = new JMenuItem(Messages.message("model.equipment.removeAll")); newItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Map<EquipmentType, Integer> equipment = new HashMap<EquipmentType, Integer>(tempUnit.getEquipment().getValues()); for (Map.Entry<EquipmentType, Integer> entry: equipment.entrySet()) { igc.equipUnit(tempUnit, entry.getKey(), -entry.getValue()); } unitLabel.updateIcon(); } }); this.add(newItem); } EquipmentType horses = null; EquipmentType muskets = null; for (EquipmentType equipmentType : freeColClient.getGame().getSpecification().getEquipmentTypeList()) { int count = tempUnit.getEquipment().getCount(equipmentType); if (count > 0) { // "remove current equipment" action JMenuItem newItem = new JMenuItem(Messages.message(equipmentType.getId() + ".remove")); if (!equipmentType.getGoodsRequired().isEmpty()) { GoodsType goodsType = equipmentType.getGoodsRequired().get(0).getType(); newItem.setIcon(imageLibrary.getScaledGoodsImageIcon(goodsType, 0.66f)); } final int items = count; final EquipmentType type = equipmentType; newItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { igc.equipUnit(tempUnit, type, -items); unitLabel.updateIcon(); } }); this.add(newItem); } if (tempUnit.canBeEquippedWith(equipmentType) && count == 0) { // "add new equipment" action JMenuItem newItem = null; count = equipmentType.getMaximumCount() - count; if (equipmentType.getGoodsRequired().isEmpty()) { newItem = new JMenuItem(); newItem.setText(Messages.message(equipmentType.getId() + ".add")); } else if (tempUnit.isInEurope() && tempUnit.getOwner().getEurope().canBuildEquipment(equipmentType)) { int price = 0; newItem = new JMenuItem(); for (AbstractGoods goodsRequired : equipmentType.getGoodsRequired()) { price += tempUnit.getOwner().getMarket().getBidPrice(goodsRequired.getType(), goodsRequired.getAmount()); newItem.setIcon(imageLibrary.getScaledGoodsImageIcon(goodsRequired.getType(), 0.66f)); } while (!tempUnit.getOwner().checkGold(count * price)) { count--; } newItem.setText(Messages.message(equipmentType.getId() + ".add") + " (" + Messages.message(StringTemplate.template("goldAmount") .addAmount("%amount%", count * price)) + ")"); } else if (tempUnit.getColony() != null && tempUnit.getColony().canBuildEquipment(equipmentType)) { newItem = new JMenuItem(); for (AbstractGoods goodsRequired : equipmentType.getGoodsRequired()) { int present = tempUnit.getColony().getGoodsCount(goodsRequired.getType()) / goodsRequired.getAmount(); if (present < count) { count = present; } newItem.setIcon(imageLibrary.getScaledGoodsImageIcon(goodsRequired.getType(), 0.66f)); } newItem.setText(Messages.message(equipmentType.getId() + ".add")); } if (newItem != null) { // for convenience menu only if ("model.equipment.horses".equals(equipmentType.getId())) { horses = equipmentType; } else if ("model.equipment.muskets".equals(equipmentType.getId())) { muskets = equipmentType; } final int items = count; final EquipmentType type = equipmentType; newItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { igc.equipUnit(tempUnit, type, items); unitLabel.updateIcon(); } }); this.add(newItem); } } } // convenience menu for equipping dragoons if (horses != null && muskets != null && horses.isCompatibleWith(muskets)) { final EquipmentType horseType = horses; final EquipmentType musketType = muskets; JMenuItem newItem = new JMenuItem(Messages.message("model.equipment.dragoon"), imageLibrary.getUnitImageIcon(tempUnit.getType(), Role.DRAGOON, 1.0/3)); newItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { igc.equipUnit(tempUnit, horseType, 1); igc.equipUnit(tempUnit, musketType, 1); unitLabel.updateIcon(); } }); this.add(newItem); } separatorNeeded = true; if (separatorNeeded) { this.addSeparator(); separatorNeeded = false; } UnitType newUnitType = tempUnit.getType().getTargetType(ChangeType.CLEAR_SKILL, tempUnit.getOwner()); if (newUnitType != null) { JMenuItem menuItem = new JMenuItem(Messages.message("clearSpeciality"), imageLibrary.getUnitImageIcon(newUnitType, 1.0/3)); menuItem.setActionCommand(UnitAction.CLEAR_SPECIALITY.toString()); menuItem.addActionListener(unitLabel); this.add(menuItem); if(tempUnit.getLocation() instanceof Building && !((Building)tempUnit.getLocation()).canAddType(newUnitType)){ menuItem.setEnabled(false); } separatorNeeded = true; } return separatorNeeded; } /** * Creates a menu for a good. */ public void createGoodsMenu(final GoodsLabel goodsLabel) { final Goods goods = goodsLabel.getGoods(); final InGameController inGameController = freeColClient.getInGameController(); ImageLibrary imageLibrary = parentPanel.getLibrary(); this.setLabel("Cargo"); JMenuItem name = new JMenuItem(Messages.message(goods.getNameKey()) + " (" + Messages.message("menuBar.colopedia") + ")", imageLibrary.getScaledGoodsImageIcon(goods.getType(), 0.66f)); name.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { gui.showColopediaPanel(goods.getType().getId()); } }); this.add(name); if (!(goods.getLocation() instanceof Colony)) { if (freeColClient.getMyPlayer().canTrade(goods)) { JMenuItem unload = new JMenuItem(Messages.message("unload")); unload.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { inGameController.unloadCargo(goods, false); if (parentPanel instanceof CargoPanel) { CargoPanel cargoPanel = (CargoPanel) parentPanel; cargoPanel.initialize(); /* if (cargoPanel.getParentPanel() instanceof ColonyPanel) { ((ColonyPanel) cargoPanel.getParentPanel()).updateWarehouse(); } */ } parentPanel.revalidate(); } }); this.add(unload); } else { if (goods.getLocation() instanceof Unit && ((Unit)goods.getLocation()).isInEurope()) { JMenuItem pay = new JMenuItem(Messages.message("boycottedGoods.payArrears")); pay.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { inGameController.payArrears(goods.getType()); if (parentPanel instanceof CargoPanel) { CargoPanel cargoPanel = (CargoPanel) parentPanel; cargoPanel.initialize(); } parentPanel.revalidate(); } }); this.add(pay); } } JMenuItem dump = new JMenuItem(Messages.message("dumpCargo")); dump.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { inGameController.unloadCargo(goods, true); if (parentPanel instanceof CargoPanel) { ((CargoPanel) parentPanel).initialize(); } parentPanel.revalidate(); } }); this.add(dump); } } }