diff --git a/megameklab/data/images/widgets/moveBottom.png b/megameklab/data/images/widgets/moveBottom.png new file mode 100644 index 000000000..5c21f674f Binary files /dev/null and b/megameklab/data/images/widgets/moveBottom.png differ diff --git a/megameklab/data/images/widgets/moveDown.png b/megameklab/data/images/widgets/moveDown.png new file mode 100644 index 000000000..53d73286e Binary files /dev/null and b/megameklab/data/images/widgets/moveDown.png differ diff --git a/megameklab/data/images/widgets/moveTop.png b/megameklab/data/images/widgets/moveTop.png new file mode 100644 index 000000000..5e10d14d6 Binary files /dev/null and b/megameklab/data/images/widgets/moveTop.png differ diff --git a/megameklab/data/images/widgets/moveUp.png b/megameklab/data/images/widgets/moveUp.png new file mode 100644 index 000000000..b43309e6c Binary files /dev/null and b/megameklab/data/images/widgets/moveUp.png differ diff --git a/megameklab/resources/megameklab/resources/Dialogs.properties b/megameklab/resources/megameklab/resources/Dialogs.properties index 1593b1b72..16267f956 100644 --- a/megameklab/resources/megameklab/resources/Dialogs.properties +++ b/megameklab/resources/megameklab/resources/Dialogs.properties @@ -36,7 +36,7 @@ ConfigurationDialog.chkShowCondensedTables.text=Print Mech hit location and clus ConfigurationDialog.chkShowCondensedTables.tooltip=Include hit location and cluster hits tables in the space where the fluff image normally goes. ConfigurationDialog.chkShowQuirks.text=Print design quirks ConfigurationDialog.chkShowQuirks.tooltip=Displays featured design quirks -ConfigurationDialog.chkShowPilotData.text=Include pilot data when printing from a MUL +ConfigurationDialog.chkShowPilotData.text=Include pilot and force data when printing from a MUL ConfigurationDialog.chkShowPilotData.tooltip=When using a MUL file for batch printing, unchecking this option ignores the generated crew information. ConfigurationDialog.chkShowEraIcon.text=Print era icon ConfigurationDialog.chkShowEraIcon.tooltip=Includes the icon associated with the era the unit was constructed. diff --git a/megameklab/src/megameklab/ui/MenuBar.java b/megameklab/src/megameklab/ui/MenuBar.java index 22dad71f4..7603ddf6a 100644 --- a/megameklab/src/megameklab/ui/MenuBar.java +++ b/megameklab/src/megameklab/ui/MenuBar.java @@ -23,7 +23,6 @@ import megamek.common.annotations.Nullable; import megamek.common.loaders.BLKFile; import megamek.common.templates.TROView; -import megamek.common.util.ImageUtil; import megameklab.MMLConstants; import megameklab.ui.dialog.MMLFileChooser; import megameklab.ui.dialog.MegaMekLabUnitSelectorDialog; @@ -394,15 +393,9 @@ private JMenu createPDFUnitExportMenu() { final JMenuItem miExportUnitsFromMULFileToPDF = new JMenuItem(resources.getString("FromMUL.text")); miExportUnitsFromMULFileToPDF.setName("miExportUnitsFromMULFileToPDF"); miExportUnitsFromMULFileToPDF.setMnemonic(KeyEvent.VK_M); - miExportUnitsFromMULFileToPDF.addActionListener(evt -> UnitPrintManager.exportMUL(owner.getFrame(), false)); + miExportUnitsFromMULFileToPDF.addActionListener(evt -> UnitPrintManager.printMUL(owner.getFrame(), true)); pdfUnitExportMenu.add(miExportUnitsFromMULFileToPDF); - final JMenuItem miExportUnitsFromMULFileToSinglePDFPages = new JMenuItem(resources.getString("FromMULSingle.text")); - miExportUnitsFromMULFileToSinglePDFPages.setName("miExportUnitsFromMULFileToSinglePDFPages"); - miExportUnitsFromMULFileToSinglePDFPages.setMnemonic(KeyEvent.VK_L); - miExportUnitsFromMULFileToSinglePDFPages.addActionListener(evt -> UnitPrintManager.exportMUL(owner.getFrame(), true)); - pdfUnitExportMenu.add(miExportUnitsFromMULFileToSinglePDFPages); - return pdfUnitExportMenu; } @@ -509,12 +502,6 @@ private JMenu createPrintMenu() { miPrintUnitsFromMULFile.addActionListener(evt -> UnitPrintManager.printMUL(owner.getFrame(), false)); printMenu.add(miPrintUnitsFromMULFile); - final JMenuItem miPrintUnitsFromMULFileToSinglePages = new JMenuItem(resources.getString("FromMULSingle.text")); - miPrintUnitsFromMULFileToSinglePages.setName("miPrintUnitsFromMULFileToSinglePages"); - miPrintUnitsFromMULFileToSinglePages.setMnemonic(KeyEvent.VK_L); - miPrintUnitsFromMULFileToSinglePages.addActionListener(evt -> UnitPrintManager.printMUL(owner.getFrame(), true)); - printMenu.add(miPrintUnitsFromMULFileToSinglePages); - return printMenu; } diff --git a/megameklab/src/megameklab/ui/dialog/PrintQueueDialog.java b/megameklab/src/megameklab/ui/dialog/PrintQueueDialog.java index 055eef46a..b92fc0ba8 100644 --- a/megameklab/src/megameklab/ui/dialog/PrintQueueDialog.java +++ b/megameklab/src/megameklab/ui/dialog/PrintQueueDialog.java @@ -21,6 +21,7 @@ import megamek.client.ui.baseComponents.MMButton; import megamek.client.ui.swing.UnitLoadingDialog; import megamek.common.BTObject; +import megamek.common.Configuration; import megamek.common.Entity; import megamek.common.MechFileParser; import megameklab.printing.PageBreak; @@ -30,14 +31,18 @@ import javax.swing.*; import javax.swing.border.EmptyBorder; import javax.swing.border.TitledBorder; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; import javax.swing.filechooser.FileNameExtensionFilter; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.io.File; +import java.net.MalformedURLException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.stream.IntStream; import static java.util.stream.Collectors.toList; @@ -48,22 +53,47 @@ * @author Simon (Juliez) */ public class PrintQueueDialog extends AbstractMMLButtonDialog { - private final boolean printToPdf; private final JButton addFromFileButton = new JButton("Add From File"); private final JButton addFromCacheButton = new JButton("Add From Cache"); private final JButton addPageBreakButton = new JButton("Add Page Break"); private final JButton removeButton = new JButton("Remove Selected"); + + private final JButton moveTopButton = new JButton(icon("moveTop.png")); + private final JButton moveUpButton = new JButton(icon("moveUp.png")); + private final JButton moveDownButton = new JButton(icon("moveDown.png")); + private final JButton moveBottomButton = new JButton(icon("moveBottom.png")); + private final JCheckBox oneUnitPerSheetCheck = new JCheckBox("Print each unit to a separate page"); private final JFrame parent; private final List units = new ArrayList<>(); private final JList queuedUnitList = new JList<>(); - public PrintQueueDialog(JFrame parent, boolean printToPdf) { + private final boolean fromMul; + + public PrintQueueDialog(JFrame parent, boolean printToPdf, List units, boolean fromMul) { super(parent, true, "PrintQueueDialog", "PrintQueueDialog.windowName.text"); this.parent = parent; this.printToPdf = printToPdf; + this.fromMul = fromMul; initialize(); + if (units != null) { + this.units.addAll(units); + refresh(); + } + } + + public PrintQueueDialog(JFrame parent, boolean printToPdf) { + this(parent, printToPdf, null, false); + } + + private static ImageIcon icon(String name) { + var path = Configuration.widgetsDir().toPath().resolve(name); + try { + return new ImageIcon(path.toUri().toURL()); + } catch (MalformedURLException e) { + return null; + } } @Override @@ -77,19 +107,43 @@ protected Container createCenterPane() { removeButton.addActionListener(e -> removeSelectedUnits()); removeButton.setEnabled(false); removeButton.setMnemonic(KeyEvent.VK_R); + + moveTopButton.addActionListener(e -> moveTop()); + moveTopButton.setMnemonic(KeyEvent.VK_PAGE_UP); + moveTopButton.setEnabled(false); + moveBottomButton.addActionListener(e -> moveBottom()); + moveBottomButton.setMnemonic(KeyEvent.VK_PAGE_DOWN); + moveBottomButton.setEnabled(false); + moveUpButton.addActionListener(e -> moveUp()); + moveUpButton.setMnemonic(KeyEvent.VK_UP); + moveUpButton.setEnabled(false); + moveDownButton.addActionListener(e -> moveDown()); + moveDownButton.setMnemonic(KeyEvent.VK_DOWN); + moveDownButton.setEnabled(false); + oneUnitPerSheetCheck.setAlignmentX(JComponent.CENTER_ALIGNMENT); oneUnitPerSheetCheck.setToolTipText("When unchecked, the record sheets for some unit types may be printed on the same page. " + "Note that the result may depend on whether reference tables are printed. This can be changed in the Settings."); queuedUnitList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); - queuedUnitList.addListSelectionListener(e -> removeButton.setEnabled(!queuedUnitList.isSelectionEmpty())); + queuedUnitList.addListSelectionListener(new OnSelectionChanged()); queuedUnitList.setVisibleRowCount(15); JPanel buttonPanel = new FixedXYPanel(new GridLayout(4, 1)); - buttonPanel.add(addFromCacheButton); - buttonPanel.add(addFromFileButton); + if (!fromMul) { + buttonPanel.add(addFromCacheButton); + buttonPanel.add(addFromFileButton); + } buttonPanel.add(addPageBreakButton); buttonPanel.add(removeButton); buttonPanel.setAlignmentY(JComponent.TOP_ALIGNMENT); + + JPanel moveButtonPanel = new FixedXYPanel(new GridLayout(4, 1)); + moveButtonPanel.add(moveTopButton); + moveButtonPanel.add(moveUpButton); + moveButtonPanel.add(moveDownButton); + moveButtonPanel.add(moveBottomButton); + moveButtonPanel.setAlignmentY(JComponent.TOP_ALIGNMENT); + JScrollPane queuedUnitListScrollPane = new JScrollPane(queuedUnitList); queuedUnitListScrollPane.setAlignmentY(JComponent.TOP_ALIGNMENT); queuedUnitListScrollPane.setBorder(new TitledBorder("Selected Units:")); @@ -97,6 +151,7 @@ protected Container createCenterPane() { Box centerPanel = Box.createHorizontalBox(); centerPanel.add(buttonPanel); centerPanel.add(Box.createHorizontalStrut(30)); + centerPanel.add(moveButtonPanel); centerPanel.add(queuedUnitListScrollPane); centerPanel.setBorder(new EmptyBorder(20, 30, 20, 30)); @@ -122,7 +177,14 @@ protected JPanel createButtonPanel() { private void refresh() { List nameList = units.stream() - .map(unit -> ' ' + unit.generalName() + ' ' + unit.specificName()) + .map(unit -> { + String title = String.format(" %s %s", unit.generalName(), unit.specificName()); + if (fromMul && unit instanceof Entity) { + var crew = ((Entity) unit).getCrew(); + title += String.format(" {%s %d/%d}", crew.getName(), crew.getGunnery(), crew.getPiloting()); + } + return title; + }) .collect(toList()); var replacementModel = new DefaultListModel(); @@ -215,6 +277,110 @@ private void removeSelectedUnits() { refresh(); } + private void moveTop() { + List newListTop = new ArrayList<>(); + List newListBottom = new ArrayList<>(); + boolean state = false; + for (int i = 0; i < units.size(); i++) { + if (i == topSelectedIndex()) { + state = true; + } else if (i > bottomSelectedIndex()) { + state = false; + } + (state ? newListTop : newListBottom).add(units.get(i)); + } + units.clear(); + units.addAll(newListTop); + units.addAll(newListBottom); + refresh(); + queuedUnitList.setSelectedIndices(IntStream.range(0, newListTop.size()).toArray()); + } + + private void moveBottom() { + List newListBottom = new ArrayList<>(); + List newListTop = new ArrayList<>(); + boolean state = false; + for (int i = 0; i < units.size(); i++) { + if (i == topSelectedIndex()) { + state = true; + } else if (i > bottomSelectedIndex()) { + state = false; + } + (state ? newListBottom : newListTop).add(units.get(i)); + } + units.clear(); + units.addAll(newListTop); + units.addAll(newListBottom); + refresh(); + queuedUnitList.setSelectedIndices(IntStream.range(newListTop.size(), newListTop.size() + newListBottom.size()).toArray()); + } + + private void moveUp() { + var unit = units.remove(topSelectedIndex() - 1); + units.add(bottomSelectedIndex(), unit); + var indices = queuedUnitList.getSelectedIndices(); + refresh(); + queuedUnitList.setSelectedIndices(Arrays.stream(indices).map(i -> i - 1).toArray()); + } + + private void moveDown() { + var unit = units.remove(bottomSelectedIndex() + 1); + units.add(topSelectedIndex(), unit); + var indices = queuedUnitList.getSelectedIndices(); + refresh(); + queuedUnitList.setSelectedIndices(Arrays.stream(indices).map(i -> i + 1).toArray()); + } + + private int topSelectedIndex() { + return queuedUnitList.getSelectedIndex(); + } + + private int bottomSelectedIndex() { + var indices = queuedUnitList.getSelectedIndices(); + return indices[indices.length - 1]; + } + + private class OnSelectionChanged implements ListSelectionListener { + @Override + public void valueChanged(ListSelectionEvent e) { + removeButton.setEnabled(!queuedUnitList.isSelectionEmpty()); + + if (!isSelectionContiguous()) { + moveTopButton.setEnabled(false); + moveUpButton.setEnabled(false); + moveDownButton.setEnabled(false); + moveBottomButton.setEnabled(false); + } else { + if (topSelectedIndex() == 0) { + moveTopButton.setEnabled(false); + moveUpButton.setEnabled(false); + } else { + moveTopButton.setEnabled(true); + moveUpButton.setEnabled(true); + } + if (bottomSelectedIndex() == units.size() - 1) { + moveBottomButton.setEnabled(false); + moveDownButton.setEnabled(false); + } else { + moveBottomButton.setEnabled(true); + moveDownButton.setEnabled(true); + } + } + } + + private boolean isSelectionContiguous() { + // getSelectedIndices is guaranteed to return the indices in ascending order + var indices = queuedUnitList.getSelectedIndices(); + if (indices.length == 0) { + return false; + } + + var start = indices[0]; + var end = indices[indices.length - 1]; + return end - start == indices.length - 1; + } + } + // TODO: Move to UIUtil public static class FixedXYPanel extends JPanel { diff --git a/megameklab/src/megameklab/util/UnitPrintManager.java b/megameklab/src/megameklab/util/UnitPrintManager.java index b821d4f82..b09c7c479 100644 --- a/megameklab/src/megameklab/util/UnitPrintManager.java +++ b/megameklab/src/megameklab/util/UnitPrintManager.java @@ -17,9 +17,10 @@ import megamek.client.ui.swing.UnitLoadingDialog; import megamek.common.*; +import megamek.common.util.C3Util; import megameklab.printing.*; import megameklab.ui.dialog.MegaMekLabUnitSelectorDialog; -import org.apache.commons.io.FilenameUtils; +import megameklab.ui.dialog.PrintQueueDialog; import org.apache.logging.log4j.LogManager; import javax.print.attribute.HashPrintRequestAttributeSet; @@ -50,7 +51,7 @@ public static void exportEntity(Entity entity, JFrame parent) { } } - public static void printMUL(Frame parent, boolean singlePrint) { + public static void printMUL(JFrame parent, boolean printToPdf) { JFileChooser f = new JFileChooser(System.getProperty("user.dir")); f.setLocation(parent.getLocation().x + 150, parent.getLocation().y + 100); f.setDialogTitle("Print From MUL"); @@ -75,39 +76,16 @@ public static void printMUL(Frame parent, boolean singlePrint) { return; } - printAllUnits(loadedUnits, singlePrint); - } - - public static void exportMUL(Frame parent, boolean singlePrint) { - JFileChooser f = new JFileChooser(System.getProperty("user.dir")); - f.setLocation(parent.getLocation().x + 150, parent.getLocation().y + 100); - f.setDialogTitle("Export from MUL"); - f.setMultiSelectionEnabled(false); - - FileNameExtensionFilter filter = new FileNameExtensionFilter("Mul Files", "mul"); - - // Add a filter for mul files - f.setFileFilter(filter); - - int returnVal = f.showOpenDialog(parent); - if ((returnVal != JFileChooser.APPROVE_OPTION) || (f.getSelectedFile() == null)) { - // I want a file, y'know! - return; - } - File mulFile = f.getSelectedFile(); - final Vector loadedUnits; - try { - loadedUnits = new MULParser(mulFile, null).getEntities(); - loadedUnits.trimToSize(); - } catch (Exception ex) { - LogManager.getLogger().error("", ex); - return; + // Dummy player and game allow bonus BV from C3 and TAG to be calculated + Game g = new Game(); + Player p = new Player(1, "Nobody"); + for (Entity e : loadedUnits) { + e.setOwner(p); + g.addEntity(e); + C3Util.wireC3(g, e); } - File exportFile = getExportFile(parent, FilenameUtils.removeExtension(mulFile.getPath()) + ".pdf"); - if (exportFile != null) { - exportUnits(loadedUnits, exportFile, singlePrint); - } + new PrintQueueDialog(parent, printToPdf, loadedUnits, true).setVisible(true); } public static File getExportFile(Frame parent) {