diff --git a/megamek/i18n/megamek/client/messages.properties b/megamek/i18n/megamek/client/messages.properties index 2ed6ffa0373..99db501900a 100644 --- a/megamek/i18n/megamek/client/messages.properties +++ b/megamek/i18n/megamek/client/messages.properties @@ -124,6 +124,7 @@ tglForceClose.toolTipText=Force the secondary skill, normally Piloting, to be eq ### EntityViewPane Class Summary.title=Summary TRO.title=TRO +ASCard.title=AS Card diff --git a/megamek/src/megamek/MegaMek.java b/megamek/src/megamek/MegaMek.java index 7b3976a6b2b..1160d109f68 100644 --- a/megamek/src/megamek/MegaMek.java +++ b/megamek/src/megamek/MegaMek.java @@ -18,6 +18,7 @@ import megamek.client.ui.preferences.SuitePreferences; import megamek.client.ui.swing.ButtonOrderPreferences; import megamek.client.ui.swing.MegaMekGUI; +import megamek.client.ui.swing.util.FontHandler; import megamek.common.annotations.Nullable; import megamek.common.commandline.AbstractCommandLineParser; import megamek.common.commandline.ClientServerCommandLineParser; @@ -29,7 +30,6 @@ import org.apache.logging.log4j.LogManager; import javax.swing.*; -import java.awt.*; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; @@ -340,7 +340,7 @@ public static File getQuickSaveFile() */ public static void initializeSuiteGraphicalSetups(final String currentProject) { // Setup Fonts - parseFontDirectory(new File(MMConstants.FONT_DIRECTORY)); + FontHandler.initialize(); // Setup Themes UIManager.installLookAndFeel("Flat Light", "com.formdev.flatlaf.FlatLightLaf"); @@ -356,32 +356,4 @@ public static void initializeSuiteGraphicalSetups(final String currentProject) { // Setup Button Order Preferences ButtonOrderPreferences.getInstance().setButtonPriorities(); } - - /** - * Recursively search the provided directory, attempting to create and then register truetype - * fonts from .ttf files - * @param directory the directory to parse - */ - private static void parseFontDirectory(final File directory) { - final String[] filenames = directory.list(); - if (filenames == null) { - return; - } - - for (final String filename : filenames) { - if (filename.toLowerCase().endsWith(MMConstants.TRUETYPE_FONT)) { - try (InputStream fis = new FileInputStream(directory.getPath() + '/' + filename)) { - GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont( - Font.createFont(Font.TRUETYPE_FONT, fis)); - } catch (Exception ex) { - LogManager.getLogger().error("Failed to parse font", ex); - } - } else { - final File file = new File(directory, filename); - if (file.isDirectory()) { - parseFontDirectory(file); - } - } - } - } -} +} \ No newline at end of file diff --git a/megamek/src/megamek/client/ui/dialogs/SBFStatsDialog.java b/megamek/src/megamek/client/ui/dialogs/SBFStatsDialog.java index 1b0f328e5c2..08323ec3734 100644 --- a/megamek/src/megamek/client/ui/dialogs/SBFStatsDialog.java +++ b/megamek/src/megamek/client/ui/dialogs/SBFStatsDialog.java @@ -23,6 +23,7 @@ import megamek.client.ui.swing.GUIPreferences; import megamek.client.ui.swing.MMToggleButton; import megamek.client.ui.swing.SBFStatsTablePanel; +import megamek.client.ui.swing.util.FontHandler; import megamek.client.ui.swing.util.UIUtil; import megamek.codeUtilities.StringUtility; import megamek.common.Game; @@ -40,6 +41,7 @@ import java.awt.print.PrinterJob; import java.util.Collection; import java.util.Objects; +import java.util.Vector; import java.util.stream.Collectors; /** @@ -57,9 +59,9 @@ public class SBFStatsDialog extends AbstractDialog { private final JButton clipBoardButton = new JButton(Messages.getString("SBFStatsDialog.copy")); private final JButton printButton = new JButton(Messages.getString("SBFStatsDialog.print")); private final JLabel headerFontLabel = new JLabel(Messages.getString("SBFStatsDialog.headerFont")); - private final JComboBox headerFontChooser = new JComboBox<>(); + private JComboBox headerFontChooser; private final JLabel valueFontLabel = new JLabel(Messages.getString("SBFStatsDialog.valueFont")); - private final JComboBox valueFontChooser = new JComboBox<>(); + private JComboBox valueFontChooser; private final JScrollPane scrollPane = new JScrollPane(); private final JPanel centerPanel = new JPanel(); private SBFStatsTablePanel statsPanel; @@ -91,6 +93,15 @@ protected Container createCenterPane() { optionsPanel.add(elementsToggle); optionsPanel.add(clipBoardButton); + headerFontChooser = new JComboBox<>(new Vector<>(FontHandler.getAvailableNonSymbolFonts())); + headerFontChooser.addItem(""); + valueFontChooser = new JComboBox<>(new Vector<>(FontHandler.getAvailableNonSymbolFonts())); + valueFontChooser.addItem(""); + headerFontChooser.setSelectedItem(GUIPreferences.getInstance().getSbfSheetHeaderFont()); + valueFontChooser.setSelectedItem(GUIPreferences.getInstance().getSbfSheetValueFont()); + headerFontChooser.setFont(UIUtil.getScaledFont()); + valueFontChooser.setFont(UIUtil.getScaledFont()); + var printPanel = new UIUtil.FixedYPanel(new FlowLayout(FlowLayout.LEFT)); printPanel.add(Box.createHorizontalStrut(25)); printPanel.add(printButton); @@ -110,17 +121,6 @@ protected Container createCenterPane() { headerFontLabel.setFont(UIUtil.getScaledFont()); valueFontLabel.setFont(UIUtil.getScaledFont()); - headerFontChooser.addItem(""); - valueFontChooser.addItem(""); - for (String family : GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()) { - headerFontChooser.addItem(family); - valueFontChooser.addItem(family); - } - headerFontChooser.setSelectedItem(GUIPreferences.getInstance().getSbfSheetHeaderFont()); - valueFontChooser.setSelectedItem(GUIPreferences.getInstance().getSbfSheetValueFont()); - headerFontChooser.setFont(UIUtil.getScaledFont()); - valueFontChooser.setFont(UIUtil.getScaledFont()); - scrollPane.getVerticalScrollBar().setUnitIncrement(16); centerPanel.add(Box.createVerticalStrut(15)); centerPanel.add(optionsPanel); diff --git a/megamek/src/megamek/client/ui/displayWrappers/FontDisplay.java b/megamek/src/megamek/client/ui/displayWrappers/FontDisplay.java index b409aaf003d..2428c9a1a05 100644 --- a/megamek/src/megamek/client/ui/displayWrappers/FontDisplay.java +++ b/megamek/src/megamek/client/ui/displayWrappers/FontDisplay.java @@ -18,12 +18,12 @@ */ package megamek.client.ui.displayWrappers; +import megamek.client.ui.swing.util.FontHandler; import megamek.common.annotations.Nullable; import java.awt.*; import java.util.List; import java.util.stream.Collectors; -import java.util.stream.Stream; /** * FontDisplay is a display wrapper around a Font, primarily to be used in ComboBoxes. This is @@ -54,9 +54,7 @@ public Font getFont() { //endregion Getters/Setters public static List getSortedFontDisplays() { - return Stream.of(GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()) - .map(FontDisplay::new) - .collect(Collectors.toList()); + return FontHandler.getAvailableFonts().stream().map(FontDisplay::new).collect(Collectors.toList()); } @Override diff --git a/megamek/src/megamek/client/ui/panes/ConfigurableMechViewPanel.java b/megamek/src/megamek/client/ui/panes/ConfigurableMechViewPanel.java index 097249595a8..06004849468 100644 --- a/megamek/src/megamek/client/ui/panes/ConfigurableMechViewPanel.java +++ b/megamek/src/megamek/client/ui/panes/ConfigurableMechViewPanel.java @@ -23,6 +23,7 @@ import megamek.client.ui.WrapLayout; import megamek.client.ui.swing.GUIPreferences; import megamek.client.ui.swing.MechViewPanel; +import megamek.client.ui.swing.util.FontHandler; import megamek.client.ui.swing.util.UIUtil; import megamek.common.Entity; import megamek.common.MechView; @@ -32,7 +33,7 @@ import java.awt.*; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; -import java.util.Locale; +import java.util.Vector; /** * This class wraps the MechView / MechViewPanel and gives it a toolbar to choose font, open the MUL @@ -40,7 +41,7 @@ */ public class ConfigurableMechViewPanel extends JPanel { - private final JComboBox fontChooser = new JComboBox<>(); + private final JComboBox fontChooser; private final JButton copyHtmlButton = new JButton(Messages.getString("CMVPanel.copyHTML")); private final JButton copyTextButton = new JButton(Messages.getString("CMVPanel.copyText")); private final JButton mulButton = new JButton(Messages.getString("CMVPanel.MUL")); @@ -56,13 +57,8 @@ public class ConfigurableMechViewPanel extends JPanel { public ConfigurableMechViewPanel(@Nullable Entity entity) { setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); + fontChooser = new JComboBox<>(new Vector<>(FontHandler.getAvailableNonSymbolFonts())); fontChooser.addItem(""); - for (String family : GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames(Locale.US)) { - Font f = Font.decode(family); - if (f.canDisplayUpTo("anzANZ150/[]") == -1) { - fontChooser.addItem(family); - } - } fontChooser.addActionListener(ev -> updateFont()); fontChooser.setSelectedItem(GUIPreferences.getInstance().getSummaryFont()); diff --git a/megamek/src/megamek/client/ui/panes/EntityViewPane.java b/megamek/src/megamek/client/ui/panes/EntityViewPane.java index 146679e7048..9597ab75867 100644 --- a/megamek/src/megamek/client/ui/panes/EntityViewPane.java +++ b/megamek/src/megamek/client/ui/panes/EntityViewPane.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - The MegaMek Team. All Rights Reserved. + * Copyright (c) 2021-2023 - The MegaMek Team. All Rights Reserved. * * This file is part of MegaMek. * @@ -23,6 +23,9 @@ import megamek.client.ui.swing.alphaStrike.ConfigurableASCardPanel; import megamek.client.ui.swing.calculationReport.FlexibleCalculationReport; import megamek.common.Entity; +import megamek.common.GunEmplacement; +import megamek.common.alphaStrike.ASCardDisplayable; +import megamek.common.alphaStrike.AlphaStrikeElement; import megamek.common.alphaStrike.conversion.ASConverter; import megamek.common.annotations.Nullable; import megamek.common.templates.TROView; @@ -33,8 +36,8 @@ * The EntityViewPane displays the entity summary, TRO and AS card panels within a TabbedPane. */ public class EntityViewPane extends AbstractTabbedPane { - private ConfigurableMechViewPanel entityPanel; - private MechViewPanel troPanel; + private final ConfigurableMechViewPanel summaryPanel = new ConfigurableMechViewPanel(); + private final MechViewPanel troPanel = new MechViewPanel(); private final ConfigurableASCardPanel cardPanel = new ConfigurableASCardPanel(getFrame()); public EntityViewPane(final JFrame frame, final @Nullable Entity entity) { @@ -43,22 +46,6 @@ public EntityViewPane(final JFrame frame, final @Nullable Entity entity) { updateDisplayedEntity(entity); } - public ConfigurableMechViewPanel getEntityPanel() { - return entityPanel; - } - - public void setEntityPanel(final ConfigurableMechViewPanel entityPanel) { - this.entityPanel = entityPanel; - } - - public MechViewPanel getTROPanel() { - return troPanel; - } - - public void setTROPanel(final MechViewPanel troPanel) { - this.troPanel = troPanel; - } - /** * This purposefully does not set preferences, as it may be used on differing panes for * differing uses and thus you don't want to remember the selected tab between the different @@ -66,34 +53,42 @@ public void setTROPanel(final MechViewPanel troPanel) { */ @Override protected void initialize() { - setEntityPanel(new ConfigurableMechViewPanel()); - getEntityPanel().setName("entityPanel"); - addTab(resources.getString("Summary.title"), getEntityPanel()); + summaryPanel.setName("entityPanel"); + troPanel.setName("troPanel"); - setTROPanel(new MechViewPanel()); - getTROPanel().setName("troPanel"); - addTab(resources.getString("TRO.title"), getTROPanel()); - - addTab("AS Card", cardPanel); + addTab(resources.getString("Summary.title"), summaryPanel); + addTab(resources.getString("TRO.title"), troPanel); + addTab(resources.getString("ASCard.title"), cardPanel); } /** - * Updates the pane's currently displayed entity. + * Updates the pane's currently displayed entity in all tabs. Performs Alpha Strike conversion if possible. * * @param entity the entity to update to, or null if the panels are to be emptied. */ public void updateDisplayedEntity(final @Nullable Entity entity) { - if (entity == null) { - getEntityPanel().reset(); - getTROPanel().reset(); - } else { - getEntityPanel().setEntity(entity); - getTROPanel().setMech(entity, TROView.createView(entity, true)); - } + AlphaStrikeElement asUnit = null; if (ASConverter.canConvert(entity)) { - cardPanel.setASElement(ASConverter.convert(entity, new FlexibleCalculationReport())); + asUnit = ASConverter.convert(entity, new FlexibleCalculationReport()); + } + updateDisplayedEntity(entity, asUnit); + } + + /** + * Updates the pane's currently displayed entity / AS unit to the respective given units. The method + * assumes that asUnit corresponds to entity and does no conversion. When the AS Element or MechSummary + * is available, passing it in as asUnit saves the time for AS conversion. + * + * @param entity the entity to update to, or null if the panels are to be emptied. + * @param asUnit the Alpha Strike unit corresponding to entity (may be a MechSummary) + */ + public void updateDisplayedEntity(final @Nullable Entity entity, @Nullable ASCardDisplayable asUnit) { + if (entity == null) { + troPanel.reset(); } else { - cardPanel.setASElement(null); + troPanel.setMech(entity, TROView.createView(entity, true)); } + summaryPanel.setEntity(entity); + cardPanel.setASElement(ASConverter.canConvert(entity) ? asUnit : null); } } \ No newline at end of file diff --git a/megamek/src/megamek/client/ui/swing/CommonSettingsDialog.java b/megamek/src/megamek/client/ui/swing/CommonSettingsDialog.java index ce6203efdd8..5094c860e71 100644 --- a/megamek/src/megamek/client/ui/swing/CommonSettingsDialog.java +++ b/megamek/src/megamek/client/ui/swing/CommonSettingsDialog.java @@ -27,6 +27,7 @@ import megamek.client.ui.baseComponents.MMComboBox; import megamek.client.ui.swing.StatusBarPhaseDisplay.PhaseCommand; import megamek.client.ui.swing.unitDisplay.UnitDisplay; +import megamek.client.ui.swing.util.FontHandler; import megamek.client.ui.swing.util.KeyCommandBind; import megamek.client.ui.swing.util.PlayerColour; import megamek.client.ui.swing.util.UIUtil; @@ -199,7 +200,7 @@ private void moveElement(DefaultListModel srcModel, int srcIndex, int trg private ColourSelectorButton csbMoveBackColor; private ColourSelectorButton csbMoveSprintColor; - private final JComboBox fontTypeChooserMoveFont = new JComboBox<>(); + private JComboBox fontTypeChooserMoveFont = new JComboBox<>(); private JTextField moveFontSize; private final JComboBox fontStyleChooserMoveFont = new JComboBox<>(); @@ -674,10 +675,7 @@ private JPanel getGameBoardPanel() { addLineSpacer(comps); - for (String family : GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()) { - fontTypeChooserMoveFont.addItem(family); - } - + fontTypeChooserMoveFont = new JComboBox<>(new Vector<>(FontHandler.getAvailableNonSymbolFonts())); fontTypeChooserMoveFont.setSelectedItem(GUIP.getMoveFontType()); JLabel moveFontTypeLabel = new JLabel(Messages.getString("CommonSettingsDialog.moveFontType")); diff --git a/megamek/src/megamek/client/ui/swing/alphaStrike/ConfigurableASCardPanel.java b/megamek/src/megamek/client/ui/swing/alphaStrike/ConfigurableASCardPanel.java index 8537e89b3a5..68a530618cd 100644 --- a/megamek/src/megamek/client/ui/swing/alphaStrike/ConfigurableASCardPanel.java +++ b/megamek/src/megamek/client/ui/swing/alphaStrike/ConfigurableASCardPanel.java @@ -23,6 +23,7 @@ import megamek.client.ui.WrapLayout; import megamek.client.ui.dialogs.ASConversionInfoDialog; import megamek.client.ui.swing.GUIPreferences; +import megamek.client.ui.swing.util.FontHandler; import megamek.client.ui.swing.util.UIUtil; import megamek.common.alphaStrike.ASCardDisplayable; import megamek.common.alphaStrike.ASStatsExporter; @@ -34,6 +35,7 @@ import java.awt.*; import java.awt.datatransfer.*; import java.util.List; +import java.util.Vector; /** * This is a JPanel that displays an AlphaStrike unit card and elements to configure the display of @@ -42,7 +44,7 @@ */ public class ConfigurableASCardPanel extends JPanel { - private final JComboBox fontChooser = new JComboBox<>(); + private final JComboBox fontChooser; private final JComboBox sizeChooser = new JComboBox<>(); private final JButton copyButton = new JButton(Messages.getString("CASCardPanel.copyCard")); private final JButton copyStatsButton = new JButton(Messages.getString("CASCardPanel.copyStats")); @@ -63,10 +65,8 @@ public ConfigurableASCardPanel(@Nullable ASCardDisplayable element, JFrame paren this.parent = parent; setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); + fontChooser = new JComboBox<>(new Vector<>(FontHandler.getAvailableNonSymbolFonts())); fontChooser.addItem(""); - for (String family : GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()) { - fontChooser.addItem(family); - } fontChooser.addActionListener(ev -> updateFont()); fontChooser.setSelectedItem(GUIPreferences.getInstance().getAsCardFont()); diff --git a/megamek/src/megamek/client/ui/swing/dialog/AbstractUnitSelectorDialog.java b/megamek/src/megamek/client/ui/swing/dialog/AbstractUnitSelectorDialog.java index 5abfa8daa2b..289d990d478 100644 --- a/megamek/src/megamek/client/ui/swing/dialog/AbstractUnitSelectorDialog.java +++ b/megamek/src/megamek/client/ui/swing/dialog/AbstractUnitSelectorDialog.java @@ -601,7 +601,7 @@ protected boolean matchesTextFilter(MechSummary unit) { */ protected Entity refreshUnitView() { Entity selectedEntity = getSelectedEntity(); - panePreview.updateDisplayedEntity(selectedEntity); + panePreview.updateDisplayedEntity(selectedEntity, getSelectedMechSummary()); // Empty the unit preview icon if there's no entity selected if (selectedEntity == null) { labelImage.setIcon(null); @@ -613,13 +613,11 @@ protected Entity refreshUnitView() { * @return the selected entity */ public @Nullable Entity getSelectedEntity() { - int view = tableUnits.getSelectedRow(); - if (view < 0) { - // selection got filtered away + MechSummary ms = getSelectedMechSummary(); + if (ms == null) { return null; } - int selected = tableUnits.convertRowIndexToModel(view); - MechSummary ms = mechs[selected]; + try { // For some unknown reason the base path gets screwed up after you // print so this sets the source file to the full path. @@ -631,6 +629,17 @@ protected Entity refreshUnitView() { } } + /** @return The MechSummary for the selected unit. */ + public @Nullable MechSummary getSelectedMechSummary() { + int view = tableUnits.getSelectedRow(); + if (view < 0) { + // selection got filtered away + return null; + } + int selected = tableUnits.convertRowIndexToModel(view); + return mechs[selected]; + } + @Override public void run() { // Loading mechs can take a while, so it will have its own thread for MegaMek diff --git a/megamek/src/megamek/client/ui/swing/lobby/LobbyMekPopup.java b/megamek/src/megamek/client/ui/swing/lobby/LobbyMekPopup.java index 7d8a1806d5b..abcb4140e89 100644 --- a/megamek/src/megamek/client/ui/swing/lobby/LobbyMekPopup.java +++ b/megamek/src/megamek/client/ui/swing/lobby/LobbyMekPopup.java @@ -134,12 +134,11 @@ static ScalingPopup getPopup(List entities, List forces, ActionLi boolean accessibleFighters = accessibleEntities.stream().anyMatch(Entity::isFighter); boolean accessibleTransportBays = accessibleEntities.stream().anyMatch(e -> !e.getTransportBays().isEmpty()); boolean accessibleCarriers = accessibleEntities.stream().anyMatch(e -> !e.getLoadedUnits().isEmpty()); - boolean accessibleProtomeks = accessibleEntities.stream().anyMatch(e -> e.hasETypeFlag(Entity.ETYPE_PROTOMECH)); // Find what can be done with the selected entities incl. those in selected forces boolean anyCarrier = joinedEntities.stream().anyMatch(e -> !e.getLoadedUnits().isEmpty()); boolean noneEmbarked = joinedEntities.stream().allMatch(e -> e.getTransportId() == Entity.NONE); - boolean allProtomeks = joinedEntities.stream().allMatch(e -> e.hasETypeFlag(Entity.ETYPE_PROTOMECH)); + boolean allProtomeks = !joinedEntities.isEmpty() && joinedEntities.stream().allMatch(e -> e instanceof Protomech); boolean anyRFMGOn = joinedEntities.stream().anyMatch(LobbyMekPopup::hasRapidFireMG); boolean anyRFMGOff = joinedEntities.stream().anyMatch(LobbyMekPopup::hasNormalFireMG); boolean anyHLOn = joinedEntities.stream().anyMatch(LobbyMekPopup::hasHotLoaded); diff --git a/megamek/src/megamek/client/ui/swing/util/FontHandler.java b/megamek/src/megamek/client/ui/swing/util/FontHandler.java new file mode 100644 index 00000000000..97682d3d63e --- /dev/null +++ b/megamek/src/megamek/client/ui/swing/util/FontHandler.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2023 - The MegaMek Team. All Rights Reserved. + * + * This file is part of MegaMek. + * + * MegaMek 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. + * + * MegaMek 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 MegaMek. If not, see . + */ +package megamek.client.ui.swing.util; + +import megamek.MMConstants; +import org.apache.logging.log4j.LogManager; + +import java.awt.*; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +/** + * This singleton class reads in the available fonts when first called, including those in + * {@link MMConstants#FONT_DIRECTORY}. Lists of available fonts can be obtained by + * calling {@link #getAvailableNonSymbolFonts()} and {@link #getAvailableFonts()}. + */ +public final class FontHandler { + + private static final FontHandler instance = new FontHandler(); + private static final String SYMBOL_TEST_STRING = "abcdefgnzABCDEFGNZ1234567890/()[]"; + + private final List nonSymbolFontNames = new ArrayList<>(); + private final List allFontNames = new ArrayList<>(); + volatile boolean initialized = false; + + /** + * Returns a list of available font names excluding some symbol fonts (specifically, excluding fonts that + * cannot display any of the characters "abcdefgnzABCDEFGNZ1234567890/()[]"). This list is only + * read from the GraphicsEnvironment once and then not updated while the application is running. + */ + public static List getAvailableNonSymbolFonts() { + if (!instance.initialized) { + initialize(); + } + return instance.nonSymbolFontNames; + } + + /** + * Returns a list of available font names. This list is only read from the GraphicsEnvironment once and + * then not updated while the application is running. + */ + public static List getAvailableFonts() { + if (!instance.initialized) { + initialize(); + } + return instance.allFontNames; + } + + /** + * Initializes the FontHandler, reading in and storing the available fonts for retrieval. Also reads in any + * fonts in {@link MMConstants#FONT_DIRECTORY}. + */ + public static void initialize() { + synchronized(instance) { + if (!instance.initialized) { + instance.initializeFonts(); + } + } + } + + private void initializeFonts() { + parseFontsInDirectory(new File(MMConstants.FONT_DIRECTORY)); + for (String fontName : GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames()) { + allFontNames.add(fontName); + Font font = Font.decode(fontName); + if (font.canDisplayUpTo(SYMBOL_TEST_STRING) == -1) { + nonSymbolFontNames.add(fontName); + } + } + initialized = true; + } + + /** + * Searches the provided directory and all subdirectories and registers any truetype + * fonts from .ttf files it finds. + * + * @param directory the directory to parse + */ + public static void parseFontsInDirectory(final File directory) { + final String[] filenames = directory.list(); + if (filenames == null) { + return; + } + + for (final String filename : filenames) { + if (filename.toLowerCase().endsWith(MMConstants.TRUETYPE_FONT)) { + try (InputStream fis = new FileInputStream(directory.getPath() + '/' + filename)) { + GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont( + Font.createFont(Font.TRUETYPE_FONT, fis)); + } catch (Exception ex) { + LogManager.getLogger().error("Failed to parse font", ex); + } + } else { + final File file = new File(directory, filename); + if (file.isDirectory()) { + parseFontsInDirectory(file); + } + } + } + } +} \ No newline at end of file diff --git a/megamek/src/megamek/common/alphaStrike/cardDrawer/ASCard.java b/megamek/src/megamek/common/alphaStrike/cardDrawer/ASCard.java index cb4fe476c2a..c133b0c3711 100644 --- a/megamek/src/megamek/common/alphaStrike/cardDrawer/ASCard.java +++ b/megamek/src/megamek/common/alphaStrike/cardDrawer/ASCard.java @@ -67,6 +67,7 @@ public class ASCard { private static final String FILENAME_BT_LOGO = "BT_Logo_BW.png"; private static final Image btLogo = ImageUtil.loadImageFromFile( new MegaMekFile(Configuration.miscImagesDir(), FILENAME_BT_LOGO).toString()); + private static ImageIcon scaledBtLogo; protected final ASCardDisplayable element; protected final Image fluffImage; @@ -511,8 +512,10 @@ protected void paintCardBackground(Graphics2D g, boolean isFlipSide) { g.drawPolyline(pointsX, pointsY, 3); // Logo - ImageIcon icon = new ImageIcon(btLogo.getScaledInstance(445, 77, Image.SCALE_AREA_AVERAGING)); - g.drawImage(icon.getImage(), 568, 646, null); + if (scaledBtLogo == null) { + scaledBtLogo = new ImageIcon(btLogo.getScaledInstance(445, 77, Image.SCALE_AREA_AVERAGING)); + } + g.drawImage(scaledBtLogo.getImage(), 568, 646, null); } new StringDrawer("(C) " + LocalDate.now().getYear() + " The Topps Company. All rights reserved.").at(1014, copyrightY).rotate(-Math.PI / 2) diff --git a/megamek/src/megamek/common/templates/TROView.java b/megamek/src/megamek/common/templates/TROView.java index 4028386d4d5..a05d268fc4d 100644 --- a/megamek/src/megamek/common/templates/TROView.java +++ b/megamek/src/megamek/common/templates/TROView.java @@ -216,6 +216,8 @@ protected void addMechVeeAeroFluff(Entity entity) { if (entity.hasEngine()) { model.put("engineDesc", formatSystemFluff(EntityFluff.System.ENGINE, entity.getFluff(), () -> stripNotes(entity.getEngine().getEngineName()))); + } else { + model.put("engineDesc", "None"); } if (!entity.isAero()) { model.put("cruisingSpeed", entity.getWalkMP() * 10.8);