/**
* 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.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.logging.Logger;
import org.freecolandroid.repackaged.java.awt.Dimension;
import org.freecolandroid.repackaged.java.awt.Image;
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.java.awt.event.MouseListener;
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.JCheckBox;
import org.freecolandroid.repackaged.javax.swing.JComboBox;
import org.freecolandroid.repackaged.javax.swing.JLabel;
import org.freecolandroid.repackaged.javax.swing.JList;
import org.freecolandroid.repackaged.javax.swing.JScrollPane;
import org.freecolandroid.repackaged.javax.swing.KeyStroke;
import org.freecolandroid.repackaged.javax.swing.event.ChangeEvent;
import org.freecolandroid.repackaged.javax.swing.event.ChangeListener;
import net.miginfocom.swing.MigLayout;
import net.sf.freecol.client.FreeColClient;
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.plaf.FreeColComboBoxRenderer;
import net.sf.freecol.common.model.Colony;
import net.sf.freecol.common.model.Europe;
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.IndianSettlement;
import net.sf.freecol.common.model.Location;
import net.sf.freecol.common.model.Map;
import net.sf.freecol.common.model.Market;
import net.sf.freecol.common.model.PathNode;
import net.sf.freecol.common.model.Player;
import net.sf.freecol.common.model.Settlement;
import net.sf.freecol.common.model.StringTemplate;
import net.sf.freecol.common.model.Unit;
import net.sf.freecol.common.model.UnitType;
import net.sf.freecol.common.model.UnitTypeChange.ChangeType;
import net.sf.freecol.common.model.pathfinding.CostDeciders;
import net.sf.freecol.common.model.pathfinding.GoalDecider;
import net.sf.freecol.common.util.Utils;
/**
* Centers the map on a known settlement or colony.
*/
public final class SelectDestinationDialog extends FreeColDialog<Location>
implements ActionListener, ChangeListener, ItemListener {
@SuppressWarnings("unused")
private static final Logger logger = Logger.getLogger(SelectDestinationDialog.class.getName());
private static boolean showOnlyMyColonies = true;
private static Comparator<Destination> destinationComparator = null;
private final JCheckBox onlyMyColoniesBox;
private final JComboBox comparatorBox;
private final JList destinationList;
private final List<Destination> destinations = new ArrayList<Destination>();
/**
* The constructor to use.
* @param freeColClient
*/
public SelectDestinationDialog(FreeColClient freeColClient, GUI gui, Unit unit) {
super(freeColClient, gui);
// Collect the goods the unit is carrying.
final List<GoodsType> goodsTypes = new ArrayList<GoodsType>();
for (Goods goods : unit.getGoodsList()) {
if (!goodsTypes.contains(goods.getType())) {
goodsTypes.add(goods.getType());
}
}
destinations.clear();
if (unit.isInEurope()) {
collectDestinationsFromEurope(unit, goodsTypes);
} else {
collectDestinationsFromAmerica(unit, goodsTypes);
}
MigLayout layout = new MigLayout("wrap 1, fill", "[align center]", "");
setLayout(layout);
JLabel header = new JLabel(Messages.message("selectDestination.text"));
header.setFont(smallHeaderFont);
add(header);
DefaultListModel model = new DefaultListModel();
destinationList = new JList(model);
filterDestinations();
destinationList.setCellRenderer(new LocationRenderer());
destinationList.setFixedCellHeight(48);
Action selectAction = new AbstractAction(Messages.message("ok")) {
public void actionPerformed(ActionEvent e) {
Destination d = (Destination) destinationList.getSelectedValue();
if (d != null) {
setResponse((Location) d.location);
}
getGUI().removeFromCanvas(SelectDestinationDialog.this);
}
};
Action quitAction = new AbstractAction(Messages.message("selectDestination.cancel")) {
public void actionPerformed(ActionEvent e) {
getGUI().removeFromCanvas(SelectDestinationDialog.this);
setResponse(null);
}
};
destinationList.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "select");
destinationList.getActionMap().put("select", selectAction);
destinationList.getInputMap().put(KeyStroke.getKeyStroke("ESCAPE"), "quit");
destinationList.getActionMap().put("quit", quitAction);
MouseListener mouseListener = new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
Destination d = (Destination) destinationList.getSelectedValue();
if (d != null) {
setResponse((Location) d.location);
}
getGUI().removeFromCanvas(SelectDestinationDialog.this);
}
}
};
destinationList.addMouseListener(mouseListener);
JScrollPane listScroller = new JScrollPane(destinationList);
listScroller.setPreferredSize(new Dimension(250, 250));
add(listScroller, "newline 30, growx, growy");
onlyMyColoniesBox = new JCheckBox(Messages.message("selectDestination.onlyMyColonies"),
showOnlyMyColonies);
onlyMyColoniesBox.addChangeListener(this);
add(onlyMyColoniesBox, "left");
comparatorBox = new JComboBox(new String[] {
Messages.message("selectDestination.sortByOwner"),
Messages.message("selectDestination.sortByName"),
Messages.message("selectDestination.sortByDistance")
});
comparatorBox.addItemListener(this);
if (destinationComparator instanceof DestinationComparator) {
comparatorBox.setSelectedIndex(0);
} else if (destinationComparator instanceof NameComparator) {
comparatorBox.setSelectedIndex(1);
} else if (destinationComparator instanceof DistanceComparator) {
comparatorBox.setSelectedIndex(2);
}
add(comparatorBox, "left");
cancelButton.setAction(quitAction);
okButton.setAction(selectAction);
add(okButton, "newline 30, split 2, tag ok");
add(cancelButton, "tag cancel");
setSize(getPreferredSize());
}
private void collectDestinationsFromEurope(Unit unit,
List<GoodsType> goodsTypes) {
Game game = getGame();
Map map = game.getMap();
int sailTurns = unit.getSailTurns();
for (Player p : game.getPlayers()) {
for (Settlement s : p.getSettlements()) {
if (!s.isConnected()) continue;
PathNode path = unit.findPathToEurope(s.getTile());
if (path != null) {
String extras = (s.getOwner() != unit.getOwner())
? getExtras(unit, s, goodsTypes) : "";
destinations.add(new Destination(s,
sailTurns + path.getTurns(), extras));
}
}
}
if (destinationComparator == null) {
destinationComparator = new DestinationComparator(getMyPlayer());
}
Collections.sort(destinations, destinationComparator);
destinations.add(0, new Destination(map, sailTurns, ""));
}
private void collectDestinationsFromAmerica(Unit unit,
final List<GoodsType> goodsTypes) {
final Settlement inSettlement = unit.getSettlement();
unit.search(unit.getTile(), new GoalDecider() {
public PathNode getGoal() {
return null;
}
public boolean check(Unit u, PathNode p) {
Settlement settlement = p.getTile().getSettlement();
if (settlement != null && settlement != inSettlement) {
String extras = (settlement.getOwner() != u.getOwner())
? getExtras(u, settlement, goodsTypes) : "";
destinations.add(new Destination(settlement, p.getTurns(), extras));
}
return false;
}
public boolean hasSubGoals() {
return false;
}
}, CostDeciders.avoidIllegal(), Integer.MAX_VALUE, null);
if (destinationComparator == null) {
destinationComparator = new DestinationComparator(getMyPlayer());
}
Collections.sort(destinations, destinationComparator);
if (unit.isNaval() && unit.getOwner().canMoveToEurope()) {
PathNode path = unit.findPathToEurope();
int turns = (path != null) ? unit.getSailTurns()
+ path.getTotalTurns()
: (unit.getTile() != null
&& unit.getTile().canMoveToEurope()) ? unit.getSailTurns()
: -1;
if (turns >= 0) {
Europe europe = getMyPlayer().getEurope();
destinations.add(0,
new Destination(europe, turns,
getExtras(unit, europe, goodsTypes)));
}
}
}
@Override
public void requestFocus() {
destinationList.requestFocus();
}
public void stateChanged(ChangeEvent event) {
showOnlyMyColonies = onlyMyColoniesBox.isSelected();
filterDestinations();
}
public void itemStateChanged(ItemEvent event) {
switch(comparatorBox.getSelectedIndex()) {
case 0:
default:
destinationComparator = new DestinationComparator(getMyPlayer());
break;
case 1:
destinationComparator = new NameComparator();
break;
case 2:
destinationComparator = new DistanceComparator();
break;
}
Collections.sort(destinations, destinationComparator);
filterDestinations();
}
/**
* Collected extra annotations of interest to a unit proposing to
* visit a location.
*
* @param unit The <code>Unit</code> proposing to visit.
* @param loc The <code>Location</code> to visit.
* @param goodsTypes A list of goods types the unit is carrying.
* @return A string containing interesting annotations about the visit
* or an empty string if nothing is of interest.
*/
private String getExtras(Unit unit, Location loc, List<GoodsType> goodsTypes) {
if (loc instanceof Europe && !goodsTypes.isEmpty()) {
Market market = unit.getOwner().getMarket();
List<String> sales = new ArrayList<String>();
for (GoodsType goodsType : goodsTypes) {
sales.add(Messages.message(goodsType.getNameKey()) + " "
+ Integer.toString(market.getSalePrice(goodsType, 1)));
}
if (!sales.isEmpty()) {
return "[" + Utils.join(", ", sales) + "]";
}
} else if (loc instanceof Settlement
&& ((Settlement)loc).getOwner().atWarWith(unit.getOwner())) {
return "[" + Messages.message("model.stance.war") + "]";
} else if (loc instanceof Settlement && !goodsTypes.isEmpty()) {
List<String> sales = new ArrayList<String>();
for (GoodsType goodsType : goodsTypes) {
String sale = unit.getOwner().getLastSaleString((Settlement) loc, goodsType);
if (sale != null) {
sales.add(Messages.message(goodsType.getNameKey())
+ " " + sale);
}
}
if (!sales.isEmpty()) {
return "[" + Utils.join(", ", sales) + "]";
}
} else if (loc instanceof IndianSettlement) {
IndianSettlement indianSettlement = (IndianSettlement) loc;
UnitType skill = indianSettlement.getLearnableSkill();
if (skill != null
&& unit.getType().canBeUpgraded(skill, ChangeType.NATIVES)) {
return "[" + Messages.message(skill.getNameKey()) + "]";
}
}
return "";
}
private void filterDestinations() {
DefaultListModel model = (DefaultListModel) destinationList.getModel();
Object selected = destinationList.getSelectedValue();
model.clear();
for (Destination d : destinations) {
if (showOnlyMyColonies) {
if (d.location instanceof Europe
|| d.location instanceof Map
|| (d.location instanceof Colony
&& ((Colony) d.location).getOwner() == getMyPlayer())) {
model.addElement(d);
}
} else {
model.addElement(d);
}
}
destinationList.setSelectedValue(selected, true);
if (destinationList.getSelectedIndex() == -1) {
destinationList.setSelectedIndex(0);
}
}
public int compareNames(Location dest1, Location dest2) {
Player player = getMyPlayer();
String name1 = "";
if (dest1 instanceof Settlement) {
name1 = ((Settlement) dest1).getNameFor(player);
} else if (dest1 instanceof Europe || dest1 instanceof Map) {
return -1;
}
String name2 = "";
if (dest2 instanceof Settlement) {
name2 = ((Settlement) dest2).getNameFor(player);
} else if (dest2 instanceof Europe || dest2 instanceof Map) {
return 1;
}
return name1.compareTo(name2);
}
private class Destination {
public Location location;
public int turns;
public String extras;
public Destination(Location location, int turns, String extras) {
this.location = location;
this.turns = turns;
this.extras = extras;
}
}
private class LocationRenderer extends FreeColComboBoxRenderer {
@Override
public void setLabelValues(JLabel label, Object value) {
Destination d = (Destination) value;
Location location = d.location;
Player player = getMyPlayer();
String name = "";
ImageLibrary lib = getLibrary();
if (location instanceof Europe) {
Europe europe = (Europe) location;
name = Messages.message(europe.getNameKey());
label.setIcon(new ImageIcon(lib.getCoatOfArmsImage(europe.getOwner().getNation())
.getScaledInstance(-1, 48, Image.SCALE_SMOOTH)));
} else if (location instanceof Map) {
name = Messages.message(location.getLocationNameFor(player));
label.setIcon(lib.getMiscImageIcon(ImageLibrary.LOST_CITY_RUMOUR));
} else if (location instanceof Settlement) {
Settlement settlement = (Settlement) location;
name = Messages.message(settlement.getNameFor(player));
label.setIcon(new ImageIcon(lib.getSettlementImage(settlement)
.getScaledInstance(64, -1, Image.SCALE_SMOOTH)));
}
label.setText(Messages.message(StringTemplate.template("selectDestination.destinationTurns")
.addName("%location%", name)
.addAmount("%turns%", d.turns)
.addName("%extras%", d.extras)));
}
}
private class DestinationComparator implements Comparator<Destination> {
private Player owner;
public DestinationComparator(Player player) {
this.owner = player;
}
public int compare(Destination choice1, Destination choice2) {
Location dest1 = choice1.location;
Location dest2 = choice2.location;
int score1 = 100;
if (dest1 instanceof Europe || dest1 instanceof Map) {
score1 = 10;
} else if (dest1 instanceof Colony) {
if (((Colony) dest1).getOwner() == owner) {
score1 = 20;
} else {
score1 = 30;
}
} else if (dest1 instanceof IndianSettlement) {
score1 = 40;
}
int score2 = 100;
if (dest2 instanceof Europe || dest2 instanceof Map) {
score2 = 10;
} else if (dest2 instanceof Colony) {
if (((Colony) dest2).getOwner() == owner) {
score2 = 20;
} else {
score2 = 30;
}
} else if (dest2 instanceof IndianSettlement) {
score2 = 40;
}
if (score1 == score2) {
return compareNames(dest1, dest2);
} else {
return score1 - score2;
}
}
}
private class NameComparator implements Comparator<Destination> {
public int compare(Destination choice1, Destination choice2) {
return compareNames(choice1.location, choice2.location);
}
}
private class DistanceComparator implements Comparator<Destination> {
public int compare(Destination choice1, Destination choice2) {
int result = choice1.turns - choice2.turns;
if (result == 0) {
return compareNames(choice1.location, choice2.location);
} else {
return result;
}
}
}
}