/** * 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.Color; import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Logger; import javax.swing.AbstractCellEditor; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JTable; import javax.swing.ListCellRenderer; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.JTableHeader; import javax.swing.table.TableCellEditor; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import net.sf.freecol.client.FreeColClient; import net.sf.freecol.client.control.PreGameController; import net.sf.freecol.client.gui.GUI; import net.sf.freecol.client.gui.ImageLibrary; import net.sf.freecol.client.gui.action.ColopediaAction.PanelType; import net.sf.freecol.client.gui.i18n.Messages; import net.sf.freecol.common.model.EuropeanNationType; import net.sf.freecol.common.model.Nation; import net.sf.freecol.common.model.NationOptions; import net.sf.freecol.common.model.NationOptions.NationState; import net.sf.freecol.common.model.NationType; import net.sf.freecol.common.model.Player; import net.sf.freecol.common.resources.ResourceManager; public final class PlayersTable extends JTable { @SuppressWarnings("unused") private static final Logger logger = Logger.getLogger(PlayersTable.class.getName()); public static final int NATION_COLUMN = 0, AVAILABILITY_COLUMN = 1, ADVANTAGE_COLUMN = 2, COLOR_COLUMN = 3, PLAYER_COLUMN = 4; private static final String[] columnNames = { Messages.message("nation"), Messages.message("availability"), Messages.message("advantage"), Messages.message("color"), Messages.message("player") }; private static final NationState[] allStates = new NationState[] { NationState.AVAILABLE, NationState.AI_ONLY, NationState.NOT_AVAILABLE }; private static final NationState[] aiStates = new NationState[] { NationState.AI_ONLY, NationState.NOT_AVAILABLE }; private final ImageLibrary library; /** * Creates a players table. * * @param freeColClient The <code>FreeColClient</code> for the game. * @param gui The <code>GUI</code> to display on. * @param nationOptions The <code>NationOptions</code> for this game. * @param myPlayer The client <code>Player</code>. */ public PlayersTable(final FreeColClient freeColClient, final GUI gui, NationOptions nationOptions, Player myPlayer) { super(); library = gui.getImageLibrary(); setModel(new PlayersTableModel(freeColClient.getPreGameController(), nationOptions, myPlayer)); setRowHeight(47); JButton nationButton = new JButton(Messages.message("nation")); JLabel availabilityLabel = new JLabel(Messages.message("availability")); JButton advantageButton = new JButton(Messages.message("advantage")); JLabel colorLabel = new JLabel(Messages.message("color")); JLabel playerLabel = new JLabel(Messages.message("player")); nationButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { gui.showColopediaPanel(PanelType.NATIONS.toString()); } }); advantageButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) { gui.showColopediaPanel(PanelType.NATION_TYPES.toString()); } }); DefaultTableCellRenderer dtcr = new DefaultTableCellRenderer(); dtcr.setOpaque(false); HeaderRenderer renderer = new HeaderRenderer(nationButton, availabilityLabel, advantageButton, colorLabel, playerLabel); JTableHeader header = getTableHeader(); header.addMouseListener(new HeaderListener(header, renderer)); TableColumn nationColumn = getColumnModel().getColumn(NATION_COLUMN); nationColumn.setCellRenderer(new NationCellRenderer()); nationColumn.setHeaderRenderer(renderer); TableColumn availableColumn = getColumnModel().getColumn(AVAILABILITY_COLUMN); availableColumn.setCellRenderer(new AvailableCellRenderer()); availableColumn.setCellEditor(new AvailableCellEditor()); TableColumn advantagesColumn = getColumnModel().getColumn(ADVANTAGE_COLUMN); if (nationOptions.getNationalAdvantages() == NationOptions.Advantages.SELECTABLE) { advantagesColumn.setCellEditor(new AdvantageCellEditor(freeColClient.getGame().getSpecification().getEuropeanNationTypes())); } advantagesColumn.setCellRenderer(new AdvantageCellRenderer(nationOptions.getNationalAdvantages())); advantagesColumn.setHeaderRenderer(renderer); TableColumn colorsColumn = getColumnModel().getColumn(COLOR_COLUMN); colorsColumn.setCellRenderer(new ColorCellRenderer(true)); TableColumn playerColumn = getColumnModel().getColumn(PLAYER_COLUMN); playerColumn.setCellEditor(new PlayerCellEditor()); playerColumn.setCellRenderer(new PlayerCellRenderer()); } public void update() { ((PlayersTableModel) getModel()).update(); } private class HeaderRenderer implements TableCellRenderer { private static final int NO_COLUMN = -1; private int pressedColumn = NO_COLUMN; private Component[] components; public HeaderRenderer(Component... components) { this.components = components; } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (components[column] instanceof JButton) { boolean isPressed = (column == pressedColumn); ((JButton) components[column]).getModel().setPressed(isPressed); ((JButton) components[column]).getModel().setArmed(isPressed); } return components[column]; } public void setPressedColumn(int column) { pressedColumn = column; } } private class HeaderListener extends MouseAdapter { JTableHeader header; HeaderRenderer renderer; HeaderListener(JTableHeader header, HeaderRenderer renderer) { this.header = header; this.renderer = renderer; } public void mousePressed(MouseEvent e) { int col = header.columnAtPoint(e.getPoint()); renderer.setPressedColumn(col); header.repaint(); } public void mouseReleased(MouseEvent e) { renderer.setPressedColumn(HeaderRenderer.NO_COLUMN); header.repaint(); } } class NationCellRenderer extends JLabel implements TableCellRenderer { /** * Returns the component used to render the cell's value. * @param table The table whose cell needs to be rendered. * @param value The value of the cell being rendered. * @param hasFocus Indicates whether or not the cell in question has focus. * @param row The row index of the cell that is being rendered. * @param column The column index of the cell that is being rendered. * @return The component used to render the cell's value. */ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Nation nation = (Nation) value; setText(Messages.message(nation.getNameKey())); setIcon(new ImageIcon(library.getCoatOfArmsImage(nation, 0.5))); return this; } } class AvailableCellRenderer implements TableCellRenderer { @SuppressWarnings("unchecked") // FIXME in Java7 private JComboBox box = new JComboBox(allStates); @SuppressWarnings("unchecked") // FIXME in Java7 public AvailableCellRenderer() { box.setRenderer(new NationStateRenderer()); } /** * Returns the component used to render the cell's value. * @param table The table whose cell needs to be rendered. * @param value The value of the cell being rendered. * @param hasFocus Indicates whether or not the cell in question has focus. * @param row The row index of the cell that is being rendered. * @param column The column index of the cell that is being rendered. * @return The component used to render the cell's value. */ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { box.setSelectedItem(value); return box; } } class NationStateRenderer extends JLabel implements ListCellRenderer { public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { setText(Messages.message("nationState." + ((NationState) value).toString())); return this; } } public final class AvailableCellEditor extends AbstractCellEditor implements TableCellEditor { @SuppressWarnings("unchecked") // FIXME in Java7 private JComboBox aiStateBox = new JComboBox(aiStates); @SuppressWarnings("unchecked") // FIXME in Java7 private JComboBox allStateBox = new JComboBox(allStates); private JComboBox activeBox; private ActionListener listener = new ActionListener() { public void actionPerformed(ActionEvent e) { stopCellEditing(); } }; @SuppressWarnings("unchecked") // FIXME in Java7 public AvailableCellEditor() { aiStateBox.setRenderer(new NationStateRenderer()); aiStateBox.addActionListener(listener); allStateBox.setRenderer(new NationStateRenderer()); allStateBox.addActionListener(listener); } public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { NationType nationType = ((Nation) getValueAt(row, NATION_COLUMN)).getType(); if (nationType instanceof EuropeanNationType) { activeBox = allStateBox; } else { activeBox = aiStateBox; } return activeBox; } public Object getCellEditorValue() { return activeBox.getSelectedItem(); } } class PlayerCellRenderer implements TableCellRenderer { JLabel label = new JLabel(); JButton button = new JButton(Messages.message("select")); public PlayerCellRenderer() { label.setHorizontalAlignment(JLabel.CENTER); button.setBorder(BorderFactory .createCompoundBorder(BorderFactory .createEmptyBorder(5, 10, 5, 10), button.getBorder())); } /** * Returns the component used to render the cell's value. * @param table The table whose cell needs to be rendered. * @param value The value of the cell being rendered. * @param hasFocus Indicates whether or not the cell in question has focus. * @param row The row index of the cell that is being rendered. * @param column The column index of the cell that is being rendered. * @return The component used to render the cell's value. */ public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Player player = (Player) value; if (player == null) { NationType nationType = (NationType) table.getValueAt(row, ADVANTAGE_COLUMN); if (nationType instanceof EuropeanNationType) { NationState nationState = (NationState) table .getValueAt(row, AVAILABILITY_COLUMN); if (nationState == NationState.AVAILABLE) { return button; } } Nation nation = (Nation) table.getValueAt(row, NATION_COLUMN); label.setText(Messages.message(nation.getRulerNameKey())); } else { label.setText(player.getDisplayName()); } return label; } } public final class PlayerCellEditor extends AbstractCellEditor implements TableCellEditor { private JButton button = new JButton(Messages.message("select")); public PlayerCellEditor() { button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { fireEditingStopped(); } }); } public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { return button; } public Object getCellEditorValue() { return true; } } /** * The TableModel for the players table. */ private class PlayersTableModel extends AbstractTableModel { private List<Nation> nations; private Map<Nation, Player> players; private Player thisPlayer; private final PreGameController preGameController; private NationOptions nationOptions; /** * A standard constructor. * * @param pgc The PreGameController to use when updates need to be notified * across the network. * @param nationOptions a <code>NationOptions</code> value * @param owningPlayer a <code>Player</code> value */ public PlayersTableModel(PreGameController pgc, NationOptions nationOptions, Player owningPlayer) { nations = new ArrayList<Nation>(); players = new HashMap<Nation, Player>(); for (Nation nation : owningPlayer.getSpecification().getNations()) { NationState state = nationOptions.getNations().get(nation); if (state != null) { nations.add(nation); players.put(nation, null); } } thisPlayer = owningPlayer; players.put(thisPlayer.getNation(), thisPlayer); preGameController = pgc; this.nationOptions = nationOptions; } public void update() { for (Nation nation : nations) { players.put(nation, null); } for (Player player : thisPlayer.getGame().getPlayers()) { players.put(player.getNation(), player); } fireTableDataChanged(); } /** * Returns the Class of the objects in the given column. * * @param column The column to return the Class of. * @return The Class of the objects in the given column. */ public Class<?> getColumnClass(int column) { switch(column) { case NATION_COLUMN: return Nation.class; case AVAILABILITY_COLUMN: return NationOptions.NationState.class; case ADVANTAGE_COLUMN: return NationType.class; case COLOR_COLUMN: return Color.class; case PLAYER_COLUMN: return Player.class; } return String.class; } /** * Returns the amount of columns in this statesTable. * * @return The amount of columns in this statesTable. */ public int getColumnCount() { return columnNames.length; } /** * Returns the name of the specified column. * * @return The name of the specified column. */ public String getColumnName(int column) { return columnNames[column]; } /** * Returns the amount of rows in this statesTable. * * @return The amount of rows in this statesTable. */ public int getRowCount() { return nations.size(); } /** * Returns the value at the requested location. * * @param row The requested row. * @param column The requested column. * @return The value at the requested location. */ public Object getValueAt(int row, int column) { if ((row < getRowCount()) && (column < getColumnCount()) && (row >= 0) && (column >= 0)) { Nation nation = nations.get(row); switch (column) { case NATION_COLUMN: return nation; case AVAILABILITY_COLUMN: return nationOptions.getNationState(nation); case ADVANTAGE_COLUMN: if (players.get(nation) == null) { return nation.getType(); } else { return players.get(nation).getNationType(); } case COLOR_COLUMN: return ResourceManager.getColor(nation.getId() + ".color"); case PLAYER_COLUMN: return players.get(nation); } } return null; } /** * Returns 'true' if the specified cell is editable, 'false' otherwise. * * @param row The specified row. * @param column The specified column. * @return 'true' if the specified cell is editable, 'false' otherwise. */ public boolean isCellEditable(int row, int column) { if ((row >= 0) && (row < nations.size())) { Nation nation = nations.get(row); boolean ownRow = (thisPlayer == players.get(nation) && !thisPlayer.isReady()); switch(column) { case AVAILABILITY_COLUMN: return (!ownRow && thisPlayer.isAdmin()); case ADVANTAGE_COLUMN: case COLOR_COLUMN: return (nation.getType() instanceof EuropeanNationType && ownRow); case PLAYER_COLUMN: return (nation.getType() instanceof EuropeanNationType && players.get(nation) == null); } } return false; } /** * Sets the value at the specified location. * * @param value The new value. * @param row The specified row. * @param column The specified column. */ public void setValueAt(Object value, int row, int column) { if ((row < getRowCount()) && (column < getColumnCount()) && (row >= 0) && (column >= 0)) { // Column 0 can't be updated. switch(column) { case ADVANTAGE_COLUMN: preGameController.setNationType((NationType) value); break; case AVAILABILITY_COLUMN: preGameController.setAvailable(nations.get(row), (NationState) value); update(); break; case PLAYER_COLUMN: Nation nation = nations.get(row); if (nationOptions.getNationState(nation) == NationState.AVAILABLE) { preGameController.setNation(nation); preGameController.setNationType(nation.getType()); update(); } break; } fireTableCellUpdated(row, column); } } } }