package net.sf.colossus.gui; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.Rectangle; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.BorderFactory; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.SwingConstants; import javax.swing.WindowConstants; import net.sf.colossus.common.Options; import net.sf.colossus.guiutil.KDialog; import net.sf.colossus.variant.CreatureType; /** * Viewer for a collection, say the graveyard or the creature keeper * * @author Tom Fruchterman * @author David Ripton */ class CreatureCollectionView extends KDialog { private static final Logger LOGGER = Logger .getLogger(CreatureCollectionView.class.getName()); private final ClientGUI gui; private final EditLegion editLegion; private static final int CHIT_SIZE = 60; private boolean gone; /** * Maps each creature type to the bottom label with all counts. */ Map<CreatureType, JLabel> countMap = new HashMap<CreatureType, JLabel>(); /** * Maps each creature type to the top label with the total count. */ Map<CreatureType, JLabel> topCountMap = new HashMap<CreatureType, JLabel>(); /** * Maps each creature type to the chit (for crossing out). */ Map<CreatureType, Chit> chitMap = new HashMap<CreatureType, Chit>(); private JScrollPane scrollPane; private JFrame parentFrame; private final static Font countFont = new Font("Monospaced", Font.PLAIN, 12); private final static String baseString = "--/--/--"; private final static JLabel baseLabel = new JLabel(baseString, SwingConstants.CENTER); private final static JLabel legendLabel = new JLabel( htmlizeOnly(htmlColorizeOnly("Values are: ", "black") + htmlColorizeOnly("Total", "blue") + htmlColorizeOnly("/", "black") + htmlColorizeOnly("In Stack", "black") + htmlColorizeOnly("/", "black") + htmlColorizeOnly("In Game", "green") + htmlColorizeOnly("/", "black") + htmlColorizeOnly("Dead", "red"))); static { baseLabel.setFont(countFont); } CreatureCollectionView(JFrame frame, ClientGUI clientGui) { this(frame, clientGui, null); } CreatureCollectionView(JFrame frame, ClientGUI clientGui, EditLegion editLegion) { super(frame, "Caretaker's Stacks", false); this.parentFrame = frame; setFocusable(false); this.gui = clientGui; this.editLegion = editLegion; this.gone = false; getContentPane().setLayout(new BorderLayout()); this.scrollPane = new JScrollPane( javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); JPanel panel = makeCreaturePanel(scrollPane); scrollPane.setViewportView(panel); JLabel infoText = new JLabel("Click on a creature for details!"); getContentPane().add(infoText, BorderLayout.NORTH); getContentPane().add(scrollPane, BorderLayout.CENTER); getContentPane().add(legendLabel, BorderLayout.SOUTH); addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { CreatureCollectionView.this.gui.getOptions().setOption( Options.showCaretaker, false); } }); pack(); setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE); useSaveWindow(gui.getOptions(), "CreatureCollectionView", null); update(); setVisible(true); } /** the count for an individual creature */ class CreatureCount extends JPanel { private final JLabel label; private final JLabel topLabel; private final Chit chit; CreatureCount(final CreatureType type) { super(new BorderLayout()); setBorder(BorderFactory.createLineBorder(Color.black)); if (type.isTitan()) { chit = Chit.newCreatureChit(CHIT_SIZE, "Titan-0-Black"); } else { chit = Chit.newCreatureChit(CHIT_SIZE, type); } chitMap.put(type, chit); label = new JLabel(baseString, SwingConstants.CENTER); topLabel = new JLabel(htmlizeOnly(htmlColorizeOnly( Integer.toString(type.getMaxCount()), "blue")), SwingConstants.CENTER); label.setFont(countFont); topLabel.setFont(countFont); countMap.put(type, label); topCountMap.put(type, topLabel); // clicking the creature icon invokes the details view this.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { if (e.getButton() == MouseEvent.BUTTON1) { if (editLegion != null) { editLegion.selectedCreature(type); dispose(); } else { new ShowCreatureDetails( CreatureCollectionView.this.parentFrame, type, null, CreatureCollectionView.this.scrollPane, gui.getGame().getVariant(), gui); } } } }); // jikes whines because add is defined in both JPanel and JDialog. this.add(topLabel, BorderLayout.NORTH); this.add(chit, BorderLayout.CENTER); this.add(label, BorderLayout.SOUTH); } @Override public Dimension getPreferredSize() { Dimension labelDim = label.getPreferredSize(); Rectangle chitDim = chit.getBounds(); int minX = chitDim.width + 1; int minY = chitDim.height + (2 * (int)labelDim.getHeight()) + 1; if (minX < (int)labelDim.getWidth() + 2) { minX = (int)labelDim.getWidth() + 2; } return new Dimension(minX, minY); } } private JPanel makeCreaturePanel(JScrollPane scrollPane) { JPanel creaturePanel = new JPanel(); creaturePanel.setLayout(new CCVFlowLayout(scrollPane, creaturePanel, FlowLayout.LEFT, 2, 2)); for (CreatureType type : gui.getGame().getVariant().getCreatureTypes()) { creaturePanel.add(new CreatureCount(type)); } return creaturePanel; } public void update() { try { for (Entry<CreatureType, JLabel> entry : countMap.entrySet()) { CreatureType type = entry.getKey(); JLabel label = entry.getValue(); int count = gui.getGame().getCaretaker() .getAvailableCount(type); int maxcount = type.getMaxCount(); int deadCount = gui.getGame().getCaretaker() .getDeadCount(type); int inGameCount = maxcount - (deadCount + count); // safety check if ((inGameCount < 0) || (inGameCount > maxcount)) { LOGGER.log(Level.SEVERE, "Something went wrong:" + " discrepancy between total (" + maxcount + "), remaining (" + count + ") and dead (" + deadCount + ") count for creature " + type); return; } boolean immortal = type.isImmortal(); String color; if (count == 0) { color = "yellow"; if (!immortal) { Chit chit = chitMap.get(type); chit.setDead(true); } } else { Chit chit = chitMap.get(type); chit.setDead(false); if (count == maxcount) { color = "green"; } else { color = "black"; } } String htmlCount = htmlColorizeOnly((count < 10 ? "0" : "") + Integer.toString(count), color); String htmlDeadCount = htmlColorizeOnly(immortal && deadCount == 0 ? "--" : (deadCount < 10 ? "0" : "") + Integer.toString(deadCount), "red"); String htmlInGameCount = htmlColorizeOnly( (inGameCount < 10 ? "0" : "") + Integer.toString(inGameCount), "green"); String htmlSlash = htmlColorizeOnly("/", "black"); label.setText(htmlizeOnly(htmlCount + htmlSlash + htmlInGameCount + htmlSlash + htmlDeadCount)); JLabel topLabel = topCountMap.get(type); topLabel.setText(htmlizeOnly(htmlColorizeOnly( Integer.toString(maxcount), "blue"))); } repaint(); } catch (NullPointerException ex) { // If we try updating this dialog before creatures are loaded, // just ignore the exception and let it retry later. } } private static String htmlColorizeOnly(String input, String color) { StringBuilder sb = new StringBuilder("<font color="); sb.append(color); sb.append(">"); sb.append(input); sb.append("</font>"); return sb.toString(); } private static String htmlizeOnly(String input) { StringBuilder sb = new StringBuilder("<html>"); sb.append(input); sb.append("</html>"); return sb.toString(); } @Override public void dispose() { // Don't do anything if dispose already done. if (gone) { return; } gone = true; setVisible(false); // We MUST remove this. Otherwise the object does not get // garbage-collected. getContentPane().remove(legendLabel); parentFrame = null; scrollPane = null; countMap.clear(); countMap = null; topCountMap.clear(); topCountMap = null; chitMap.clear(); chitMap = null; super.dispose(); } @Override public Dimension getMinimumSize() { // default : 5 creatures wide int minSingleX = CHIT_SIZE + 8; if (minSingleX < (int)baseLabel.getPreferredSize().getWidth() + 8) { minSingleX = (int)baseLabel.getPreferredSize().getWidth() + 8; } int minX = minSingleX * 5; int minY = ((CHIT_SIZE + 8 + (2 * (int)baseLabel.getPreferredSize() .getHeight())) * ((gui.getGame().getVariant().getCreatureTypes() .size() + 4) / 5)) + CHIT_SIZE; return new Dimension(minX, minY); } @Override public Dimension getPreferredSize() { return getMinimumSize(); } }