package magic.ui.screen.deck.editor; import java.nio.file.Files; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JOptionPane; import magic.data.DeckType; import magic.data.GeneralConfig; import magic.data.MagicIcon; import magic.data.MagicSetDefinitions; import magic.model.MagicDeck; import magic.translate.MText; import magic.ui.MagicLogs; import magic.ui.ScreenController; import magic.ui.WikiPage; import magic.ui.screen.HeaderFooterScreen; import magic.ui.screen.MScreen; import magic.ui.screen.interfaces.IDeckConsumer; import magic.ui.screen.widget.MenuButton; import magic.ui.widget.deck.DeckStatusPanel; import magic.utility.DeckUtils; import magic.utility.MagicFileSystem; @SuppressWarnings("serial") public class DeckEditorScreen extends HeaderFooterScreen implements IDeckConsumer { // translatable strings private static final String _S1 = "Cancel"; private static final String _S2 = "Close"; private static final String _S3 = "Use this deck"; private static final String _S4 = "Select Deck"; private static final String _S5 = "Select an existing prebuilt or player deck."; private static final String _S6 = "Save Deck"; private static final String _S7 = "Save deck to file."; private static final String _S8 = "Sample Hand"; private static final String _S9 = "See what kind of Hand you might be dealt from this deck."; private static final String _S10 = "A deck with a minimum of 7 cards is required first."; private static final String _S11 = "Deck View"; private static final String _S12 = "Shows complete deck using tiled card images."; private static final String _S13 = "Deck is empty! Nothing to show."; private static final String _S14 = "Deck Editor"; private static final String _S15 = "Deck is empty! Nothing to save."; private static final String _S17 = "Overwrite existing deck file?"; private static final String _S18 = "Overwrite file"; private static final String _S20 = "There was a problem saving the deck file!"; private static final String _S21 = "Deck editor has unsaved changes which will be lost.\nDo you wish to continue?"; private static final String _S22 = "Confirmation required..."; private static final String _S30 = "Invalid deck filename"; private static final String _S31 = "Deck name (must be a valid filename)"; private static final String _S32 = "Save player deck"; private ContentPanel contentPanel; private final DeckStatusPanel deckStatusPanel = new DeckStatusPanel(); private final IDeckEditorClient deckClient; private final DeckEditorController controller = DeckEditorController.instance; public DeckEditorScreen(IDeckEditorClient client) { super(MText.get(_S14)); this.deckClient = client; controller.init(this, client.getDeck()); useLoadingScreen(this::initUI); } // CTR : open Deck Editor in standalone mode starting with an empty deck. public DeckEditorScreen() { super(MText.get(_S14)); this.deckClient = null; controller.init(this, getMostRecentEditedDeck()); useLoadingScreen(this::initUI); } public DeckEditorScreen(MagicDeck aDeck) { super(MText.get(_S14)); this.deckClient = null; controller.init(this, aDeck); useLoadingScreen(this::initUI); } @Override protected boolean isCardDataRequired() { return true; } private boolean isStandaloneMode() { return deckClient == null; } private void initUI() { contentPanel = new ContentPanel(this); contentPanel.setIsStandalone(isStandaloneMode()); doRefreshViews(); setMainContent(contentPanel); setHeaderContent(deckStatusPanel); setLeftFooter(getLeftActionButton()); setRightFooter(getRightActionButton()); setFooterButtons(); setWikiPage(WikiPage.DECK_EDITOR); } private void showSampleHandScreen() { if (contentPanel.getDeck().size() >= 7) { ScreenController.showSampleHandScreen(contentPanel.getDeck()); } else { ScreenController.showWarningMessage(MText.get(_S10)); } } private void showDeckTiledCardsScreen() { if (contentPanel.getDeck().size() > 0) { ScreenController.showDeckTiledCardsScreen(contentPanel.getDeck()); } else { ScreenController.showWarningMessage(MText.get(_S13)); } } private void setFooterButtons() { addToFooter( MenuButton.build(this::showDecksScreen, MagicIcon.OPEN, MText.get(_S4), MText.get(_S5) ), MenuButton.build(this::saveDeck, MagicIcon.SAVE, MText.get(_S6), MText.get(_S7) ), MenuButton.build(this::showSampleHandScreen, MagicIcon.HAND_ICON, MText.get(_S8), MText.get(_S9) ), MenuButton.build(this::showDeckTiledCardsScreen, MagicIcon.TILED, MText.get(_S11), MText.get(_S12) ) ); } private static MagicDeck getMostRecentEditedDeck() { Path deckFilePath = GeneralConfig.getInstance().getMostRecentDeckFilePath(); if (deckFilePath != null) { MagicDeck newDeck = tryLoadDeck(deckFilePath); if (newDeck.isValid()) { return newDeck; } } return new MagicDeck(); } private static MagicDeck tryLoadDeck(final Path deckFilePath) { try { return DeckUtils.loadDeckFromFile(deckFilePath); } catch (RuntimeException ex) { // if the most recent deck is invalid for some reason then I think it suffices // to log the error to console and open the deck editor with an empty deck. Logger.getLogger(DeckEditorScreen.class.getName()).log(Level.WARNING, null, ex); return new MagicDeck(); } } private MenuButton getLeftActionButton() { return MenuButton.getCloseScreenButton(!isStandaloneMode() ? MText.get(_S1) : MText.get(_S2)); } private void doUseDeckAction() { if (contentPanel.validateDeck(true)) { if (controller.hasDeckChanged()) { controller.setDeckStatusToUnsaved(); } if (deckClient.setDeck(controller.getDeck())) { ScreenController.closeActiveScreen(false); } } } private MenuButton getRightActionButton() { return !isStandaloneMode() ? MenuButton.build(this::doUseDeckAction, MText.get(_S3)) : null; } void showDecksScreen() { ScreenController.showDecksScreen(this); } private Path tryGetDeckFilePath(String filename) { try { return MagicFileSystem.getDataPath(MagicFileSystem.DataPath.DECKS).resolve(filename); } catch (InvalidPathException ex) { System.err.println(ex); ScreenController.showWarningMessage(MText.get(_S30) + " :-\n" + ex.getMessage()); return null; } } private void saveDeck() { final MagicDeck deck = contentPanel.getDeck(); if (deck.isEmpty()) { ScreenController.showWarningMessage(MText.get(_S15)); return; } // Prompt for name of deck (which is also used as the filename). final String deckName = (String) JOptionPane.showInputDialog( ScreenController.getFrame(), MText.get(_S31), MText.get(_S32), JOptionPane.QUESTION_MESSAGE, null, null, deck.getName() ); if (deckName == null || deckName.trim().isEmpty()) { return; } // add '.dec' file extension to end of filename if not present. String filename = deckName.trim(); if (!filename.endsWith(DeckUtils.DECK_EXTENSION)) { filename += DeckUtils.DECK_EXTENSION; } // create deck file path - returns null if not valid (eg invalid char in filename). Path deckFilePath = tryGetDeckFilePath(filename); if (deckFilePath == null) { return; } // if deck file already exists ask for overwrite confirmation. if (Files.exists(deckFilePath)) { int response = JOptionPane.showConfirmDialog( ScreenController.getFrame(), MText.get(_S17), MText.get(_S18), JOptionPane.YES_NO_OPTION ); if (response != JOptionPane.YES_OPTION) { return; } } // finally can try to save deck to file. if (DeckUtils.saveDeck(deckFilePath.toString(), deck)) { setDeck(DeckUtils.loadDeckFromFile(deckFilePath)); setMostRecentDeck(deckFilePath.toString()); } else { ScreenController.showWarningMessage(MText.get(_S20)); } } private void setMostRecentDeck(final String filename) { if (isStandaloneMode()) { GeneralConfig.getInstance().setMostRecentDeckFilename(filename); GeneralConfig.getInstance().save(); } } private boolean isUserReadyToClose() { if (controller.hasDeckChanged()) { int response = JOptionPane.showConfirmDialog( ScreenController.getFrame(), MText.get(_S21), MText.get(_S22), JOptionPane.YES_NO_OPTION ); return response == JOptionPane.YES_OPTION; } return true; } @Override public boolean isScreenReadyToClose(MScreen nextScreen) { if (super.isScreenReadyToClose(nextScreen)) { if (contentPanel == null) { return true; } if (isStandaloneMode() && !isUserReadyToClose()) { return false; } MagicSetDefinitions.clearLoadedSets(); MagicLogs.clearLoadedLogs(); return true; } return false; } void doRefreshViews() { contentPanel.doRefreshView(); deckStatusPanel.setDeck(controller.getDeck(), false); } @Override public void setDeck(final MagicDeck deck) { controller.setDeck(deck); } @Override public boolean setDeck(MagicDeck newDeck, Path deckPath) { if (controller.hasDeckChanged()) { int response = JOptionPane.showConfirmDialog( ScreenController.getFrame(), MText.get(_S21), MText.get(_S22), JOptionPane.YES_NO_OPTION ); if (response == JOptionPane.NO_OPTION) { return false; } } setDeck(newDeck); setMostRecentDeck(deckPath.toString()); return true; } @Override public void setDeck(String deckName, DeckType deckType) { } void deckUpdated(MagicDeck deck) { deckStatusPanel.setDeck(deck, false); } }