diff --git a/megameklab/src/megameklab/ui/MegaMekLabMainUI.java b/megameklab/src/megameklab/ui/MegaMekLabMainUI.java index b2fd32fbf..237cc1e14 100644 --- a/megameklab/src/megameklab/ui/MegaMekLabMainUI.java +++ b/megameklab/src/megameklab/ui/MegaMekLabMainUI.java @@ -20,6 +20,7 @@ import megamek.common.Entity; import megamek.common.Mounted; import megamek.common.preference.PreferenceManager; +import megameklab.util.EntityChangedUtil; import megameklab.MMLConstants; import megameklab.MegaMekLab; import megameklab.ui.util.ExitOnWindowClosingListener; @@ -83,7 +84,7 @@ public void setVisible(boolean b) { @Override public boolean safetyPrompt() { - if (CConfig.getBooleanParam(CConfig.MISC_SKIP_SAFETY_PROMPTS)) { + if (CConfig.getBooleanParam(CConfig.MISC_SKIP_SAFETY_PROMPTS) || !EntityChangedUtil.hasEntityChanged(this)) { return true; } else { int savePrompt = JOptionPane.showConfirmDialog(this, diff --git a/megameklab/src/megameklab/ui/MegaMekLabTabbedUI.java b/megameklab/src/megameklab/ui/MegaMekLabTabbedUI.java index b18f2a09b..c20f44d09 100644 --- a/megameklab/src/megameklab/ui/MegaMekLabTabbedUI.java +++ b/megameklab/src/megameklab/ui/MegaMekLabTabbedUI.java @@ -23,12 +23,13 @@ import megamek.client.ui.swing.util.UIUtil; import megamek.common.Entity; import megamek.common.preference.PreferenceManager; -import megameklab.EntityChangedUtil; +import megameklab.util.EntityChangedUtil; import megameklab.MMLConstants; import megameklab.MegaMekLab; import megameklab.ui.dialog.UiLoader; import megameklab.ui.mek.BMMainUI; import megameklab.ui.util.ExitOnWindowClosingListener; +import megameklab.ui.util.MegaMekLabFileSaver; import megameklab.ui.util.TabStateUtil; import megameklab.util.CConfig; import megameklab.util.MMLFileDropTransferHandler; @@ -235,9 +236,36 @@ public void addUnit(Entity entity, String filename) { tabs.setTabComponentAt(tabs.getSelectedIndex(), new EditorTab(entity.getDisplayName(), currentEditor())); } + private boolean exitPrompt() { + if (CConfig.getBooleanParam(CConfig.MISC_SKIP_SAFETY_PROMPTS)) { + return true; + } + if (editors.stream().limit(editors.size() - 1).noneMatch(EntityChangedUtil::hasEntityChanged)) { + return true; + } + int savePrompt = JOptionPane.showConfirmDialog(this, + "All unsaved changes to open units will be discarded. Save the units first?", + "Save Units Before Proceeding?", + JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.WARNING_MESSAGE); + if (savePrompt == JOptionPane.NO_OPTION) { + return true; + } + if (savePrompt == JOptionPane.YES_OPTION) { + return editors.stream().limit(editors.size() - 1) + .filter(EntityChangedUtil::hasEntityChanged) + .noneMatch(editor -> { + tabs.setSelectedComponent(editor.getContentPane()); + tabs.paintImmediately(tabs.getBounds()); + return !editor.getMMLMenuBar().saveUnit(); + }); + } + return false; + } + @Override public boolean exit() { - if (!currentEditor().safetyPrompt()) { + if (!exitPrompt()) { return false; } @@ -348,6 +376,11 @@ public MenuBar getMMLMenuBar() { return menuBar; } + @Override + public boolean safetyPrompt() { + return currentEditor().safetyPrompt(); + } + @Override public void stateChanged(ChangeEvent e) { if (e.getSource() == tabs) { diff --git a/megameklab/src/megameklab/ui/MenuBar.java b/megameklab/src/megameklab/ui/MenuBar.java index e399d5b74..f3c76b187 100644 --- a/megameklab/src/megameklab/ui/MenuBar.java +++ b/megameklab/src/megameklab/ui/MenuBar.java @@ -44,7 +44,6 @@ import megamek.common.annotations.Nullable; import megamek.common.templates.TROView; import megamek.logging.MMLogger; -import megameklab.EntityChangedUtil; import megameklab.MMLConstants; import megameklab.ui.dialog.MMLFileChooser; import megameklab.ui.dialog.MegaMekLabUnitSelectorDialog; @@ -1171,7 +1170,8 @@ public void loadFile(File unitFile) { throw new Exception(); } - if (!owner.safetyPrompt()) { + // TabbedUi loads a unit into a new tab, no safety prompt needed. + if (!(owner instanceof MegaMekLabTabbedUI) || !owner.safetyPrompt()) { return; } diff --git a/megameklab/src/megameklab/ui/util/MegaMekLabFileSaver.java b/megameklab/src/megameklab/ui/util/MegaMekLabFileSaver.java index 1cc522747..2f25cbe3b 100644 --- a/megameklab/src/megameklab/ui/util/MegaMekLabFileSaver.java +++ b/megameklab/src/megameklab/ui/util/MegaMekLabFileSaver.java @@ -19,9 +19,11 @@ import megamek.common.loaders.BLKFile; import megamek.logging.MMLogger; import megameklab.ui.FileNameManager; +import megameklab.ui.MegaMekLabMainUI; import megameklab.ui.PopupMessages; import megameklab.ui.dialog.MMLFileChooser; import megameklab.util.CConfig; +import megameklab.util.EntityChangedUtil; import javax.swing.*; import javax.swing.filechooser.FileNameExtensionFilter; @@ -119,6 +121,12 @@ private String saveUnitTo(JFrame ownerFrame, File file, Entity entity) { } else { BLKFile.encode(file.getPath(), entity); } + + if (ownerFrame instanceof MegaMekLabMainUI mui) { + // Since we've saved the entity, update the entity being compared against to determine if the user has unsaved work. + EntityChangedUtil.editorSaved(mui); + } + PopupMessages.showUnitSavedMessage(ownerFrame, entity, file); return file.toString(); } catch (Exception ex) { diff --git a/megameklab/src/megameklab/EntityChangedUtil.java b/megameklab/src/megameklab/util/EntityChangedUtil.java similarity index 54% rename from megameklab/src/megameklab/EntityChangedUtil.java rename to megameklab/src/megameklab/util/EntityChangedUtil.java index 4d8d9fed4..066bbceaf 100644 --- a/megameklab/src/megameklab/EntityChangedUtil.java +++ b/megameklab/src/megameklab/util/EntityChangedUtil.java @@ -1,21 +1,42 @@ -package megameklab; +/* + * Copyright (c) 2024 - The MegaMek Team. All Rights Reserved. + * + * This file is part of MegaMekLab. + * + * 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 megameklab.util; import megamek.common.Entity; import megamek.common.Mek; import megamek.common.MekFileParser; import megamek.common.loaders.BLKFile; +import megamek.common.loaders.EntityLoadingException; import megamek.common.loaders.EntitySavingException; import megamek.logging.MMLogger; import megameklab.ui.MegaMekLabMainUI; +import java.io.ByteArrayInputStream; import java.io.File; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class EntityChangedUtil { - private static MMLogger logger = MMLogger.create(EntityChangedUtil.class); + private static final MMLogger logger = MMLogger.create(EntityChangedUtil.class); - private static Map cache = new ConcurrentHashMap<>(); + private static final Map cache = new ConcurrentHashMap<>(); public static boolean hasEntityChanged(MegaMekLabMainUI editor) { var filename = editor.getFileName(); @@ -32,14 +53,15 @@ public static boolean hasEntityChanged(MegaMekLabMainUI editor) { try { var e = new MekFileParser(f).getEntity(); - cache.put(filename, encode(e)); + cache.put(filename, e); } catch (Exception ex) { logger.error("Entity loading failure:", ex); + cache.put(filename, null); } } try { - var o = cache.get(filename); + var o = encode(cache.get(filename)); var n = encode(editor.getEntity()); return !o.equals(n); } catch (EntitySavingException e) { @@ -55,13 +77,19 @@ public static void editorSaved(MegaMekLabMainUI editor) { } try { - cache.put(editor.getFileName(), encode(editor.getEntity())); - } catch (EntitySavingException e) { + var bis = new ByteArrayInputStream(encode(editor.getEntity()).getBytes()); + cache.put(editor.getFileName(), new MekFileParser(bis, editor.getFileName()).getEntity()); + } catch (EntitySavingException | EntityLoadingException e) { + cache.remove(filename); logger.error("Entity encoding failure:", e); } } private static String encode(Entity e) throws EntitySavingException { + if (e == null) { + return ""; + } + if (e instanceof Mek m) { return m.getMtf(); } else {