/*
* LanceAssignmentView.java
*
* Copyright (c) 2014 Carl Spain. All rights reserved.
*
* This file is part of MekHQ.
*
* MekHQ 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 3 of the License, or
* (at your option) any later version.
*
* MekHQ 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 MekHQ. If not, see <http://www.gnu.org/licenses/>.
*/
package mekhq.gui.view;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.Comparator;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.DefaultCellEditor;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.RowFilter;
import javax.swing.RowSorter;
import javax.swing.SortOrder;
import javax.swing.SwingConstants;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableRowSorter;
import mekhq.campaign.Campaign;
import mekhq.campaign.force.Force;
import mekhq.campaign.force.Lance;
import mekhq.campaign.mission.AtBContract;
import mekhq.campaign.mission.Mission;
import mekhq.campaign.personnel.SkillType;
import mekhq.gui.model.DataTableModel;
import mekhq.gui.model.UnitMarketTableModel;
import mekhq.gui.model.XTableColumnModel;
/**
* Against the Bot
* Shows how many lances are required to be deployed on active contracts and
* in what roles and allows the player to assign units to those roles.
*
* @author Neoancient
*
*/
public class LanceAssignmentView extends JPanel {
/**
*
*/
private static final long serialVersionUID = 7280552346074838142L;
public static final int ROLE_NONE = 0;
public static final int ROLE_FIGHT = 1;
public static final int ROLE_DEFEND = 2;
public static final int ROLE_SCOUT = 3;
public static final int ROLE_TRAINING = 4;
public static final int ROLE_NUM = 5;
private Campaign campaign;
private JTable tblRequiredLances;
private JTable tblAssignments;
private JPanel panRequiredLances;
private JPanel panAssignments;
private JComboBox<AtBContract> cbContract;
private JComboBox<String> cbRole;
public LanceAssignmentView(Campaign c) {
campaign = c;
initComponents();
}
@SuppressWarnings("serial")
private void initComponents() {
cbContract = new JComboBox<AtBContract>();
cbContract.setRenderer(new DefaultListCellRenderer() {
@Override
public Component getListCellRendererComponent(JList<?> list,
Object value, int index, boolean isSelected,
boolean cellHasFocus) {
return new JLabel((null == value)?"None":((AtBContract)value).getName());
}
});
cbRole = new JComboBox<String>(Lance.roleNames);
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
RequiredLancesTableModel rlModel = new RequiredLancesTableModel(campaign);
tblRequiredLances = new JTable(rlModel);
tblRequiredLances.setColumnModel(new XTableColumnModel());
tblRequiredLances.createDefaultColumnsFromModel();
tblRequiredLances.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
TableColumn column = null;
for (int i = 0; i < UnitMarketTableModel.COL_NUM; i++) {
column = ((XTableColumnModel)tblRequiredLances.getColumnModel()).getColumnByModelIndex(i);
column.setPreferredWidth(rlModel.getColumnWidth(i));
column.setCellRenderer(new DefaultTableCellRenderer() {
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
super.getTableCellRendererComponent(table, value, isSelected,
hasFocus, row, column);
setHorizontalAlignment(((RequiredLancesTableModel)table.getModel()).
getAlignment(table.convertColumnIndexToModel(column)));
if (table.convertColumnIndexToModel(column) > RequiredLancesTableModel.COL_CONTRACT) {
if (((String)value).indexOf('/') >= 0) {
setForeground(Color.RED);
} else {
setForeground(Color.BLACK);
}
}
return this;
}
});
}
TableRowSorter<RequiredLancesTableModel>sorter = new TableRowSorter<RequiredLancesTableModel>(rlModel);
tblRequiredLances.setRowSorter(sorter);
tblRequiredLances.setIntercellSpacing(new Dimension(0, 0));
tblRequiredLances.setShowGrid(false);
LanceAssignmentTableModel laModel = new LanceAssignmentTableModel(campaign);
tblAssignments = new JTable(laModel);
tblAssignments.setColumnModel(new XTableColumnModel());
tblAssignments.createDefaultColumnsFromModel();
tblAssignments.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
column = null;
for (int i = 0; i < LanceAssignmentTableModel.COL_NUM; i++) {
column = ((XTableColumnModel)tblAssignments.getColumnModel()).getColumnByModelIndex(i);
column.setPreferredWidth(rlModel.getColumnWidth(i));
column.setCellRenderer(new DefaultTableCellRenderer() {
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
switch (column) {
case LanceAssignmentTableModel.COL_FORCE:
setText((((Force)value)).getFullName());
break;
case LanceAssignmentTableModel.COL_CONTRACT:
if (null == value) {
setText("None");
} else {
setText(((AtBContract)value).getName());
}
break;
default:
super.getTableCellRendererComponent(table, value, isSelected,
hasFocus, row, column);
}
return this;
}
});
if (i == LanceAssignmentTableModel.COL_CONTRACT) {
column.setCellEditor(new DefaultCellEditor(cbContract));
}
if (i == LanceAssignmentTableModel.COL_ROLE) {
column.setCellEditor(new DefaultCellEditor(cbRole));
}
}
RowFilter<LanceAssignmentTableModel, Integer> laFilter = null;
laFilter = new RowFilter<LanceAssignmentTableModel, Integer>() {
@Override
public boolean include(Entry<? extends LanceAssignmentTableModel, ? extends Integer> entry) {
Lance l = entry.getModel().getRow(entry.getIdentifier());
return l.isEligible(campaign);
}
};
TableRowSorter<LanceAssignmentTableModel>laSorter = new TableRowSorter<LanceAssignmentTableModel>(laModel);
laSorter.setRowFilter(laFilter);
laSorter.setComparator(LanceAssignmentTableModel.COL_FORCE, forceComparator);
ArrayList<RowSorter.SortKey>sortKeys = new ArrayList<RowSorter.SortKey>();
sortKeys.add(new RowSorter.SortKey(LanceAssignmentTableModel.COL_FORCE, SortOrder.ASCENDING));
sorter.setSortKeys(sortKeys);
tblAssignments.setRowSorter(laSorter);
tblAssignments.setIntercellSpacing(new Dimension(0, 0));
tblAssignments.setShowGrid(false);
panRequiredLances = new JPanel();
panRequiredLances.setLayout(new BoxLayout(panRequiredLances, BoxLayout.Y_AXIS));
panRequiredLances.setBorder(BorderFactory.createTitledBorder("Deployment Requirements"));
panRequiredLances.add(tblRequiredLances.getTableHeader());
panRequiredLances.add(tblRequiredLances);
add(panRequiredLances);
int cmdrStrategy = 0;
if (campaign.getFlaggedCommander() != null &&
campaign.getFlaggedCommander().getSkill(SkillType.S_STRATEGY) != null) {
cmdrStrategy = campaign.getFlaggedCommander().
getSkill(SkillType.S_STRATEGY).getLevel();
}
int maxDeployedLances =
campaign.getCampaignOptions().getBaseStrategyDeployment() +
campaign.getCampaignOptions().getAdditionalStrategyDeployment() *
cmdrStrategy;
add(new JLabel("Maximum Deployed Units: " + maxDeployedLances));
panAssignments = new JPanel();
panAssignments.setLayout(new BoxLayout(panAssignments, BoxLayout.Y_AXIS));
panAssignments.setBorder(BorderFactory.createTitledBorder("Current Assignments"));
panAssignments.add(tblAssignments.getTableHeader());
panAssignments.add(tblAssignments);
add(panAssignments);
refresh();
tblAssignments.getModel().addTableModelListener(assignmentTableListener);
}
public void refresh() {
cbContract.removeAllItems();
ArrayList<AtBContract> activeContracts = new ArrayList<AtBContract>();
for (Mission m : campaign.getMissions()) {
if (m instanceof AtBContract && m.isActive() &&
!((AtBContract)m).getStartDate().after(campaign.getDate())) {
activeContracts.add((AtBContract)m);
cbContract.addItem((AtBContract)m);
}
}
AtBContract defaultContract = null;
if (activeContracts.size() > 0) {
defaultContract = activeContracts.get(0);
}
for (Lance l : campaign.getLances().values()) {
if (null == l.getContract(campaign) || !l.getContract(campaign).isActive()) {
l.setContract(defaultContract);
}
}
((DataTableModel)tblRequiredLances.getModel()).setData(activeContracts);
((DataTableModel)tblAssignments.getModel()).setData(campaign.getLanceList());
panRequiredLances.setVisible(tblRequiredLances.getRowCount() > 0);
}
TableModelListener assignmentTableListener = new TableModelListener() {
public void tableChanged(TableModelEvent ev) {
((RequiredLancesTableModel)tblRequiredLances.getModel()).fireTableDataChanged();
}
};
/**
* Sorts Force objects according to where they appear on the TO&E
*/
public Comparator<Force> forceComparator = new Comparator<Force>() {
@Override
public int compare(Force f1, Force f2) {
/* Check whether they are the same or one is an ancestor of the other */
if (f1.getId() == f2.getId()) {
return 0;
}
if (f1.isAncestorOf(f2)) {
return -1;
}
if (f2.isAncestorOf(f1)) {
return 1;
}
/* Find closest common ancestor. They must be or descend from
* different subforces of this one. */
Force f = f1;
while (!f.isAncestorOf(f2)) {
f = f.getParentForce();
}
for (Force sf : f.getSubForces()) {
if (sf.isAncestorOf(f1) || sf.getId() == f1.getId()) {
return -1;
}
if (sf.isAncestorOf(f2) || sf.getId() == f2.getId()) {
return 1;
}
}
/* We should never get here. */
return 0;
}
};
}
class RequiredLancesTableModel extends DataTableModel {
/**
*
*/
private static final long serialVersionUID = -5007787884549927503L;
public static final int COL_CONTRACT = 0;
public static final int COL_TOTAL = 1;
public static final int COL_FIGHT = 2;
public static final int COL_DEFEND = 3;
public static final int COL_SCOUT = 4;
public static final int COL_TRAINING = 5;
public static final int COL_NUM = 6;
protected String[] columnNames = {"Contract", "Total", "Fight", "Defend", "Scout", "Training"};
private Campaign campaign;
public RequiredLancesTableModel(Campaign campaign) {
this.campaign = campaign;
data = new ArrayList<AtBContract>();
}
@Override
public int getColumnCount() {
return COL_NUM;
}
@Override
public String getColumnName(int column) {
return columnNames[column];
}
public int getColumnWidth(int col) {
if (col == COL_CONTRACT) {
return 100;
} else {
return 20;
}
}
public int getAlignment(int col) {
if (col == COL_CONTRACT) {
return SwingConstants.LEFT;
} else {
return SwingConstants.CENTER;
}
}
@Override
public Class<?> getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
@Override
public boolean isCellEditable(int row, int col) {
return false;
}
public AtBContract getRow(int row) {
return (AtBContract) data.get(row);
}
@Override
public Object getValueAt(int row, int column) {
if (row >= getRowCount()) {
return "";
}
if (COL_CONTRACT == column) {
return ((AtBContract)data.get(row)).getName();
}
if (data.get(row) instanceof AtBContract) {
AtBContract contract = (AtBContract)data.get(row);
if (column == COL_TOTAL) {
int t = 0;
for (Lance l : campaign.getLanceList()) {
if (l.getContract(campaign) == data.get(row)
&& l.getRole() > Lance.ROLE_UNASSIGNED
&& l.isEligible(campaign)) {
t++;
}
}
if (t < contract.getRequiredLances()) {
return t + "/" + contract.getRequiredLances();
}
return Integer.toString(contract.getRequiredLances());
} else if (contract.getRequiredLanceType() == column - 1) {
int t = 0;
for (Lance l : campaign.getLanceList()) {
if (l.getContract(campaign) == data.get(row)
&& l.getRole() == l.getContract(campaign).getRequiredLanceType()
&& l.isEligible(campaign)) {
t++;
}
}
int required = Math.max(contract.getRequiredLances() / 2, 1);
if (t < required) {
return t + "/" + required;
}
return Integer.toString(required);
}
}
return "";
}
}
class LanceAssignmentTableModel extends DataTableModel {
/**
*
*/
private static final long serialVersionUID = -2688617737510762878L;
public static final int COL_FORCE = 0;
public static final int COL_WEIGHT_CLASS = 1;
public static final int COL_CONTRACT = 2;
public static final int COL_ROLE = 3;
public static final int COL_NUM = 4;
protected String[] columnNames = {"Force", "Wt", "Mission", "Role"};
private Campaign campaign;
public LanceAssignmentTableModel(Campaign campaign) {
this.campaign = campaign;
data = new ArrayList<Lance>();
}
@Override
public int getColumnCount() {
return COL_NUM;
}
@Override
public String getColumnName(int column) {
return columnNames[column];
}
public int getColumnWidth(int col) {
switch (col) {
case COL_FORCE:
case COL_CONTRACT:
return 100;
case COL_WEIGHT_CLASS:
return 5;
default:
return 50;
}
}
@Override
public Class<?> getColumnClass(int c) {
switch (c) {
case COL_FORCE:
return Force.class;
case COL_CONTRACT:
return AtBContract.class;
case COL_ROLE:
return String.class;
}
return String.class;
}
@Override
public boolean isCellEditable(int row, int col) {
return col > COL_WEIGHT_CLASS;
}
public Lance getRow(int row) {
return (Lance) data.get(row);
}
@Override
public Object getValueAt(int row, int column) {
final String[] WEIGHT_CODES = {"UL", "L", "M", "H", "A", "SH"};
if (row >= getRowCount()) {
return "";
}
switch (column) {
case COL_FORCE:
return campaign.getForce(((Lance)data.get(row)).getForceId());
case COL_WEIGHT_CLASS:
return WEIGHT_CODES[((Lance)data.get(row)).getWeightClass(campaign)];
case COL_CONTRACT:
return (AtBContract)campaign.getMission(((Lance)data.get(row)).getMissionId());
case COL_ROLE:
return Lance.roleNames[((Lance)data.get(row)).getRole()];
default:
return "?";
}
}
public void setValueAt(Object value, int row, int col) {
if (col == COL_CONTRACT) {
((Lance)data.get(row)).setContract((AtBContract)value);
}
if (col == COL_ROLE) {
for (int i = 0; i < Lance.ROLE_NUM; i++) {
if (((String)value).equals(Lance.roleNames[i])) {
((Lance)data.get(row)).setRole(i);
}
}
}
fireTableDataChanged();
}
}