/* * UnitMarketDialog.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.dialog; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.event.ActionEvent; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.ArrayList; import java.util.Comparator; import java.util.ResourceBundle; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSpinner; import javax.swing.JSplitPane; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.RowFilter; import javax.swing.RowSorter; import javax.swing.ScrollPaneConstants; import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableRowSorter; import megamek.client.ui.swing.MechViewPanel; import megamek.common.Compute; import megamek.common.Entity; import megamek.common.MechFileParser; import megamek.common.MechSummary; import megamek.common.UnitType; import megamek.common.loaders.EntityLoadingException; import megamek.common.util.EncodeControl; import mekhq.MekHQ; import mekhq.campaign.Campaign; import mekhq.campaign.finances.Transaction; import mekhq.campaign.market.UnitMarket; import mekhq.gui.model.UnitMarketTableModel; import mekhq.gui.model.XTableColumnModel; import mekhq.gui.sorter.WeightClassSorter; /** * Code copied heavily from PersonnelMarketDialog * * @author Neoancient * */ public class UnitMarketDialog extends JDialog { /** * */ private static final long serialVersionUID = -7668601227249317220L; private static boolean showMeks = true; private static boolean showVees = true; private static boolean showAero = false; private static boolean pctThreshold = false; private static int threshold = 120; private UnitMarketTableModel marketModel; private Campaign campaign; private UnitMarket unitMarket; boolean addToCampaign; Entity selectedEntity = null; private JButton btnAdd; private JButton btnPurchase; private JButton btnClose; private JCheckBox chkShowMeks; private JCheckBox chkShowVees; private JCheckBox chkShowAero; private JCheckBox chkPctThreshold; private JLabel lblPctThreshold; private JSpinner spnThreshold; private JPanel panelOKBtns; private JPanel panelMain; private JPanel panelFilterBtns; private JTable tableUnits; private JLabel lblBlackMarketWarning; private MechViewPanel mechViewPanel; private JScrollPane scrollTableUnits; private JScrollPane scrollUnitView; private TableRowSorter<UnitMarketTableModel> sorter; ArrayList <RowSorter.SortKey> sortKeys; private JSplitPane splitMain; /** Creates new form UnitSelectorDialog */ public UnitMarketDialog(Frame frame, Campaign c) { super(frame, true); campaign = c; unitMarket = c.getUnitMarket(); marketModel = new UnitMarketTableModel(); marketModel.setData(unitMarket.getOffers()); initComponents(); filterOffers(); setLocationRelativeTo(frame); } @SuppressWarnings("serial") private void initComponents() { GridBagConstraints gbc = new GridBagConstraints(); scrollTableUnits = new JScrollPane(); scrollUnitView = new JScrollPane(); mechViewPanel = new MechViewPanel(); tableUnits = new JTable(); panelMain = new JPanel(); panelFilterBtns = new JPanel(); chkShowMeks = new JCheckBox(); chkShowVees = new JCheckBox(); chkShowAero = new JCheckBox(); chkPctThreshold = new JCheckBox(); lblPctThreshold = new JLabel(); spnThreshold = new JSpinner(new SpinnerNumberModel(threshold, 60, 130, 5)); lblBlackMarketWarning = new JLabel(); panelOKBtns = new JPanel(); btnPurchase = new JButton(); btnClose = new JButton(); ResourceBundle resourceMap = ResourceBundle.getBundle("mekhq.resources.UnitMarketDialog", new EncodeControl()); //$NON-NLS-1$ setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); setTitle(resourceMap.getString("Form.title")); // NOI18N setName("Form"); // NOI18N getContentPane().setLayout(new BorderLayout()); panelFilterBtns.setLayout(new GridBagLayout()); ItemListener checkboxListener = new ItemListener() { @Override public void itemStateChanged(ItemEvent arg0) { showMeks = chkShowMeks.isSelected(); showAero = chkShowAero.isSelected(); showVees = chkShowVees.isSelected(); pctThreshold = chkPctThreshold.isSelected(); spnThreshold.setEnabled(chkPctThreshold.isSelected()); filterOffers(); } }; chkShowMeks.setText(resourceMap.getString("chkShowMeks.text")); chkShowMeks.setSelected(showMeks); gbc.gridx = 0; gbc.gridy = 0; gbc.weightx = 0.0; gbc.anchor = java.awt.GridBagConstraints.WEST; gbc.insets = new java.awt.Insets(5, 5, 0, 0); panelFilterBtns.add(chkShowMeks, gbc); chkShowMeks.addItemListener(checkboxListener); chkShowVees.setText(resourceMap.getString("chkShowVees.text")); chkShowVees.setSelected(showVees); gbc.gridx = 1; gbc.gridy = 0; gbc.weightx = 0.0; gbc.anchor = java.awt.GridBagConstraints.WEST; gbc.insets = new java.awt.Insets(5, 5, 0, 0); panelFilterBtns.add(chkShowVees, gbc); chkShowVees.addItemListener(checkboxListener); chkShowAero.setText(resourceMap.getString("chkShowAero.text")); chkShowAero.setSelected(showAero); gbc.gridx = 2; gbc.gridy = 0; gbc.weightx = 1.0; gbc.anchor = java.awt.GridBagConstraints.WEST; gbc.insets = new java.awt.Insets(5, 5, 0, 0); panelFilterBtns.add(chkShowAero, gbc); chkShowAero.addItemListener(checkboxListener); JPanel panel = new JPanel(); chkPctThreshold.setText(resourceMap.getString("chkPctThreshold.text")); chkPctThreshold.setSelected(pctThreshold); spnThreshold.setEnabled(pctThreshold); lblPctThreshold.setText(resourceMap.getString("lblPctThreshold.text")); panel.add(chkPctThreshold); panel.add(spnThreshold); panel.add(lblPctThreshold); chkPctThreshold.addItemListener(checkboxListener); spnThreshold.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent arg0) { threshold = (Integer)spnThreshold.getValue(); filterOffers(); } }); gbc.gridx = 0; gbc.gridy = 1; gbc.weightx = 0.0; gbc.gridwidth = GridBagConstraints.REMAINDER; gbc.anchor = java.awt.GridBagConstraints.WEST; gbc.insets = new java.awt.Insets(5, 5, 0, 0); panelFilterBtns.add(panel, gbc); scrollTableUnits.setMinimumSize(new java.awt.Dimension(500, 400)); scrollTableUnits.setName("srcTablePersonnel"); // NOI18N scrollTableUnits.setPreferredSize(new java.awt.Dimension(500, 400)); gbc = new GridBagConstraints(); tableUnits.setModel(marketModel); tableUnits.setName("tableUnits"); // NOI18N tableUnits.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); tableUnits.setColumnModel(new XTableColumnModel()); tableUnits.createDefaultColumnsFromModel(); sorter = new TableRowSorter<UnitMarketTableModel>(marketModel); sorter.setComparator(UnitMarketTableModel.COL_WEIGHTCLASS, new WeightClassSorter()); Comparator<String> numComparator = new Comparator<String>() { public int compare(String arg0, String arg1) { if (arg0.length() != arg1.length()) { return arg0.length() - arg1.length(); } return arg0.compareTo(arg1); } }; sorter.setComparator(UnitMarketTableModel.COL_PRICE, numComparator); sorter.setComparator(UnitMarketTableModel.COL_PERCENT, numComparator); tableUnits.setRowSorter(sorter); tableUnits.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); tableUnits.getSelectionModel().addListSelectionListener(new ListSelectionListener() { public void valueChanged(javax.swing.event.ListSelectionEvent evt) { offerChanged(evt); } }); TableColumn column = null; for (int i = 0; i < UnitMarketTableModel.COL_NUM; i++) { column = ((XTableColumnModel)tableUnits.getColumnModel()).getColumnByModelIndex(i); column.setPreferredWidth(marketModel.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(((UnitMarketTableModel)table.getModel()). getAlignment(table.convertColumnIndexToModel(column))); return this; } }); } tableUnits.setIntercellSpacing(new Dimension(0, 0)); tableUnits.setShowGrid(false); scrollTableUnits.setViewportView(tableUnits); lblBlackMarketWarning.setText(resourceMap.getString("lblBlackMarketWarning.text")); scrollTableUnits.setMinimumSize(new java.awt.Dimension(500, 400)); scrollTableUnits.setName("scrollTableUnits"); // NOI18N scrollTableUnits.setPreferredSize(new java.awt.Dimension(500, 400)); panelMain.setLayout(new BorderLayout()); panelMain.add(panelFilterBtns, BorderLayout.PAGE_START); panelMain.add(scrollTableUnits, BorderLayout.CENTER); panelMain.add(lblBlackMarketWarning, BorderLayout.PAGE_END); scrollUnitView.setMinimumSize(new java.awt.Dimension(500, 600)); scrollUnitView.setPreferredSize(new java.awt.Dimension(500, 600)); scrollUnitView.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); scrollUnitView.setViewportView(mechViewPanel); splitMain = new JSplitPane(javax.swing.JSplitPane.HORIZONTAL_SPLIT,panelMain, scrollUnitView); splitMain.setOneTouchExpandable(true); splitMain.setResizeWeight(0.0); getContentPane().add(splitMain, BorderLayout.CENTER); panelOKBtns.setLayout(new java.awt.GridBagLayout()); btnPurchase.setText(resourceMap.getString("btnPurchase.text")); btnPurchase.setName("btnPurchase"); // NOI18N btnPurchase.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { purchaseUnit(evt); } }); panelOKBtns.add(btnPurchase, new java.awt.GridBagConstraints()); btnPurchase.setEnabled(null != selectedEntity); btnAdd = new JButton(resourceMap.getString("btnAdd.text")); btnAdd.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { addUnit(); } }); btnAdd.setEnabled(null != selectedEntity); panelOKBtns.add(btnAdd, new java.awt.GridBagConstraints()); btnClose.setText(resourceMap.getString("btnClose.text")); // NOI18N btnClose.setName("btnClose"); // NOI18N btnClose.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { btnCloseActionPerformed(evt); } }); panelOKBtns.add(btnClose, new java.awt.GridBagConstraints()); getContentPane().add(panelOKBtns, BorderLayout.PAGE_END); pack(); } public Entity getUnit() { return selectedEntity; } private void purchaseUnit(ActionEvent evt) { if(null != selectedEntity) { int transitDays = campaign.getCampaignOptions().getInstantUnitMarketDelivery()?0: campaign.calculatePartTransitTime(Compute.d6(2) - 2); UnitMarket.MarketOffer offer = marketModel.getOffer(tableUnits.convertRowIndexToModel(tableUnits.getSelectedRow())); long cost = (long)Math.ceil(offer.unit.getCost() * offer.pct / 100.0); if (campaign.getFunds() < cost) { campaign.addReport("<font color='red'><b> You cannot afford this unit. Transaction cancelled</b>.</font>"); return; } int roll = Compute.d6(); if (offer.market == UnitMarket.MARKET_BLACK && roll <= 2) { campaign.getFinances().debit(cost / roll, Transaction.C_UNIT, "Purchased " + selectedEntity.getShortName() + " (lost on black market)", campaign.getCalendar().getTime()); campaign.addReport("<font color='red'>Swindled! Money was paid, but no unit delivered.</font>"); } else { campaign.getFinances().debit(cost, Transaction.C_UNIT, "Purchased " + selectedEntity.getShortName(), campaign.getCalendar().getTime()); campaign.addUnit(selectedEntity, false, transitDays); if (!campaign.getCampaignOptions().getInstantUnitMarketDelivery()) { campaign.addReport("<font color='green'>Unit will be delivered in " + transitDays + " days.</font>"); } } UnitMarket.MarketOffer selected = ((UnitMarketTableModel)tableUnits.getModel()).getOffer(tableUnits.convertRowIndexToModel(tableUnits.getSelectedRow())); unitMarket.removeOffer(selected); ((UnitMarketTableModel)tableUnits.getModel()).setData(unitMarket.getOffers()); refreshOfferView(); } } private void addUnit() { if (null != selectedEntity) { campaign.addUnit(selectedEntity, false, 0); UnitMarket.MarketOffer selected = ((UnitMarketTableModel)tableUnits.getModel()).getOffer(tableUnits.convertRowIndexToModel(tableUnits.getSelectedRow())); unitMarket.removeOffer(selected); ((UnitMarketTableModel)tableUnits.getModel()).setData(unitMarket.getOffers()); refreshOfferView(); } } private void btnCloseActionPerformed(java.awt.event.ActionEvent evt) { selectedEntity = null; setVisible(false); } private void filterOffers() { RowFilter<UnitMarketTableModel, Integer> unitTypeFilter = null; unitTypeFilter = new RowFilter<UnitMarketTableModel,Integer>() { @Override public boolean include(Entry<? extends UnitMarketTableModel, ? extends Integer> entry) { UnitMarket.MarketOffer offer = marketModel.getOffer(entry.getIdentifier()); boolean underThreshold = !chkPctThreshold.isSelected() || offer.pct <= (Integer)spnThreshold.getValue(); if (offer.unitType == UnitType.MEK) { return underThreshold && chkShowMeks.isSelected(); } if (offer.unitType == UnitType.TANK) { return underThreshold && chkShowVees.isSelected(); } if (offer.unitType == UnitType.AERO) { return underThreshold && chkShowAero.isSelected(); } return false; } }; sorter.setRowFilter(unitTypeFilter); } private void offerChanged(ListSelectionEvent evt) { int view = tableUnits.getSelectedRow(); if(view < 0) { //selection got filtered away selectedEntity= null; refreshOfferView(); return; } MechSummary ms = marketModel.getOffer(tableUnits.convertRowIndexToModel(view)).unit; try { selectedEntity = new MechFileParser(ms.getSourceFile(), ms.getEntryName()).getEntity(); } catch (EntityLoadingException e) { selectedEntity = null; btnPurchase.setEnabled(false); MekHQ.logError("Unable to load mech: " + ms.getSourceFile() + ": " + ms.getEntryName() + ": " + e.getMessage()); MekHQ.logError(e); refreshOfferView(); return; } refreshOfferView(); } void refreshOfferView() { int row = tableUnits.getSelectedRow(); if(row < 0 || selectedEntity == null) { mechViewPanel.reset(); } else { mechViewPanel.setMech(selectedEntity, true); //This odd code is to make sure that the scrollbar stays at the top //I cant just call it here, because it ends up getting reset somewhere later javax.swing.SwingUtilities.invokeLater(new Runnable() { public void run() { scrollUnitView.getVerticalScrollBar().setValue(0); } }); } btnPurchase.setEnabled(null != selectedEntity); btnAdd.setEnabled(null != selectedEntity && campaign.isGM()); } @Override public void setVisible(boolean visible) { filterOffers(); super.setVisible(visible); } }