diff --git a/.gitattributes b/.gitattributes index 74f7e73cd45..261b4f13026 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13468,6 +13468,7 @@ forge-gui/res/defaults/gauntlet/LOCKED_Swimming[!!-~]With[!!-~]Sharks.dat -text forge-gui/res/defaults/home.xml svneol=native#text/xml forge-gui/res/defaults/match.xml svneol=native#text/xml forge-gui/res/defaults/window.xml -text +forge-gui/res/defaults/workshop.xml -text forge-gui/res/draft/cube_juzamjedi.draft -text forge-gui/res/draft/cube_skiera.draft -text forge-gui/res/draft/rankings.txt -text @@ -15457,6 +15458,15 @@ forge-gui/src/main/java/forge/gui/toolbox/special/PhaseIndicator.java -text forge-gui/src/main/java/forge/gui/toolbox/special/PhaseLabel.java -text forge-gui/src/main/java/forge/gui/toolbox/special/PlayerDetailsPanel.java -text forge-gui/src/main/java/forge/gui/toolbox/special/package-info.java -text +forge-gui/src/main/java/forge/gui/workshop/CWorkshopUI.java -text +forge-gui/src/main/java/forge/gui/workshop/SWorkshopIO.java -text +forge-gui/src/main/java/forge/gui/workshop/VWorkshopUI.java -text +forge-gui/src/main/java/forge/gui/workshop/controllers/CCardDesigner.java -text +forge-gui/src/main/java/forge/gui/workshop/controllers/CCardScript.java -text +forge-gui/src/main/java/forge/gui/workshop/controllers/CWorkshopCatalog.java -text +forge-gui/src/main/java/forge/gui/workshop/views/VCardDesigner.java -text +forge-gui/src/main/java/forge/gui/workshop/views/VCardScript.java -text +forge-gui/src/main/java/forge/gui/workshop/views/VWorkshopCatalog.java -text forge-gui/src/main/java/forge/item/BoosterPack.java -text forge-gui/src/main/java/forge/item/FatPack.java -text forge-gui/src/main/java/forge/item/IPaperCard.java -text diff --git a/forge-gui/res/defaults/workshop.xml b/forge-gui/res/defaults/workshop.xml new file mode 100644 index 00000000000..8d63d994d70 --- /dev/null +++ b/forge-gui/res/defaults/workshop.xml @@ -0,0 +1,18 @@ + + + + WORKSHOP_CATALOG + + + WORKSHOP_CARDDESIGNER + + + WORKSHOP_CARDSCRIPT + + + CARD_DETAIL + + + CARD_PICTURE + + diff --git a/forge-gui/src/main/java/forge/card/CardRules.java b/forge-gui/src/main/java/forge/card/CardRules.java index 411f59b026c..6e40c0b556b 100644 --- a/forge-gui/src/main/java/forge/card/CardRules.java +++ b/forge-gui/src/main/java/forge/card/CardRules.java @@ -17,7 +17,9 @@ */ package forge.card; +import java.io.File; import java.util.List; + import forge.card.mana.ManaCost; /** @@ -34,8 +36,8 @@ public final class CardRules implements ICardCharacteristics { //private final Map setsPrinted = new TreeMap(String.CASE_INSENSITIVE_ORDER); private CardAiHints aiHints; - private ColorSet colorIdentity = null; + private File sourceFile; public CardRules(ICardFace[] faces, CardSplitType altMode, CardAiHints cah) { splitType = altMode; @@ -65,8 +67,7 @@ public final class CardRules implements ICardCharacteristics { colorIdentity = ColorSet.fromMask(colMask); } - private byte calculateColorIdentity(ICardFace face) - { + private byte calculateColorIdentity(ICardFace face) { byte res = face.getColor().getColor(); boolean isReminder = false; boolean isSymbol = false; @@ -216,7 +217,14 @@ public final class CardRules implements ICardCharacteristics { } public ColorSet getColorIdentity() { - return colorIdentity; - + return this.colorIdentity; + } + + public File getSourceFile() { + return this.sourceFile; + } + + public void setSourceFile(File sourceFile0) { + this.sourceFile = sourceFile0; } } diff --git a/forge-gui/src/main/java/forge/card/CardRulesReader.java b/forge-gui/src/main/java/forge/card/CardRulesReader.java index d434c80cc0b..58af9ff48aa 100644 --- a/forge-gui/src/main/java/forge/card/CardRulesReader.java +++ b/forge-gui/src/main/java/forge/card/CardRulesReader.java @@ -47,8 +47,6 @@ public class CardRulesReader { private boolean removedFromRandomDecks = false; private DeckHints hints = null; private DeckHints needs = null; - - // Reset all fields to parse next card (to avoid allocating new // CardRulesReader N times) diff --git a/forge-gui/src/main/java/forge/card/cardfactory/CardStorageReader.java b/forge-gui/src/main/java/forge/card/cardfactory/CardStorageReader.java index c6b8ef73a88..3e1d98f1a4e 100644 --- a/forge-gui/src/main/java/forge/card/cardfactory/CardStorageReader.java +++ b/forge-gui/src/main/java/forge/card/cardfactory/CardStorageReader.java @@ -329,15 +329,17 @@ public class CardStorageReader { * * @return a new Card instance */ - protected final CardRules loadCard(final CardRulesReader reader, final File pathToTxtFile) { + protected final CardRules loadCard(final CardRulesReader reader, final File file) { FileInputStream fileInputStream = null; try { - fileInputStream = new FileInputStream(pathToTxtFile); - return this.loadCard(reader, fileInputStream); + fileInputStream = new FileInputStream(file); + CardRules rules = this.loadCard(reader, fileInputStream); + rules.setSourceFile(file); + return rules; } catch (final FileNotFoundException ex) { - BugReporter.reportException(ex, "File \"%s\" exception", pathToTxtFile.getAbsolutePath()); + BugReporter.reportException(ex, "File \"%s\" exception", file.getAbsolutePath()); throw new RuntimeException("CardReader : run error -- file exception -- filename is " - + pathToTxtFile.getPath(), ex); + + file.getPath(), ex); } finally { try { fileInputStream.close(); diff --git a/forge-gui/src/main/java/forge/gui/deckeditor/CDeckEditorUI.java b/forge-gui/src/main/java/forge/gui/deckeditor/CDeckEditorUI.java index f55f4aaed6f..87dfd2ce4f8 100644 --- a/forge-gui/src/main/java/forge/gui/deckeditor/CDeckEditorUI.java +++ b/forge-gui/src/main/java/forge/gui/deckeditor/CDeckEditorUI.java @@ -40,6 +40,8 @@ import javax.swing.PopupFactory; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.Timer; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; import org.apache.commons.lang.CharUtils; import org.apache.commons.lang.StringUtils; @@ -85,7 +87,7 @@ public enum CDeckEditorUI implements ICDoc, IMenuProvider { SINGLETON_INSTANCE; private final HashMap> screenChildControllers; - private ACEditorBase childController; + private ACEditorBase childController; private boolean isFindingAsYouType = false; private CDeckEditorUI() { @@ -358,7 +360,7 @@ public enum CDeckEditorUI implements ICDoc, IMenuProvider { removeSelectedCards(toAlternate, qty); } }; - + catTable.addMouseListener(new FMouseAdapter() { @Override public void onLeftDoubleClick(MouseEvent e) { @@ -407,6 +409,24 @@ public enum CDeckEditorUI implements ICDoc, IMenuProvider { catTable.addKeyListener(catFind); deckTable.addKeyListener(deckFind); + //set card when selection changes + catView.addSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + setCard(catView.getSelectedItem()); + } + }); + + deckView.addSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + setCard(deckView.getSelectedItem()); + } + }); + + catView.setAllowMultipleSelections(true); + deckView.setAllowMultipleSelections(true); + childController.listenersHooked = true; } diff --git a/forge-gui/src/main/java/forge/gui/framework/EDocID.java b/forge-gui/src/main/java/forge/gui/framework/EDocID.java index 540e98ba0c1..1e0bd95a82c 100644 --- a/forge-gui/src/main/java/forge/gui/framework/EDocID.java +++ b/forge-gui/src/main/java/forge/gui/framework/EDocID.java @@ -40,6 +40,9 @@ import forge.gui.match.views.VMessage; import forge.gui.match.views.VPicture; import forge.gui.match.views.VPlayers; import forge.gui.match.views.VStack; +import forge.gui.workshop.views.VCardDesigner; +import forge.gui.workshop.views.VCardScript; +import forge.gui.workshop.views.VWorkshopCatalog; /** * These are the identifiers for tabs found in the drag layout. @@ -59,6 +62,10 @@ public enum EDocID { /** */ EDITOR_CATALOG (VCardCatalog.SINGLETON_INSTANCE), /** */ EDITOR_CURRENTDECK (VCurrentDeck.SINGLETON_INSTANCE), /** */ EDITOR_DECKGEN (VDeckgen.SINGLETON_INSTANCE), /** */ + + WORKSHOP_CATALOG (VWorkshopCatalog.SINGLETON_INSTANCE), /** */ + WORKSHOP_CARDDESIGNER (VCardDesigner.SINGLETON_INSTANCE), /** */ + WORKSHOP_CARDSCRIPT (VCardScript.SINGLETON_INSTANCE), /** */ HOME_QUESTCHALLENGES (VSubmenuChallenges.SINGLETON_INSTANCE), /** */ HOME_QUESTDUELS (VSubmenuDuels.SINGLETON_INSTANCE), /** */ diff --git a/forge-gui/src/main/java/forge/gui/framework/FScreen.java b/forge-gui/src/main/java/forge/gui/framework/FScreen.java index aeefa7c26ed..78ace213e9b 100644 --- a/forge-gui/src/main/java/forge/gui/framework/FScreen.java +++ b/forge-gui/src/main/java/forge/gui/framework/FScreen.java @@ -15,6 +15,8 @@ import forge.gui.match.CMatchUI; import forge.gui.match.VMatchUI; import forge.gui.toolbox.FSkin; import forge.gui.toolbox.FSkin.SkinImage; +import forge.gui.workshop.CWorkshopUI; +import forge.gui.workshop.VWorkshopUI; import forge.properties.FileLocation; import forge.properties.NewConstants; @@ -35,10 +37,18 @@ public enum FScreen { VMatchUI.SINGLETON_INSTANCE, CMatchUI.SINGLETON_INSTANCE, "Game", - FSkin.getIcon(FSkin.DockIcons.ICO_ALPHASTRIKE), + FSkin.getIcon(FSkin.DockIcons.ICO_ALPHASTRIKE), //TODO: Create icon for match screen true, "Concede Game", NewConstants.MATCH_LAYOUT_FILE), + WORKSHOP_SCREEN( + VWorkshopUI.SINGLETON_INSTANCE, + CWorkshopUI.SINGLETON_INSTANCE, + "Workshop", + FSkin.getIcon(FSkin.DockIcons.ICO_SETTINGS), //TODO: Create icon for workshop screen + false, + "Back to Home", + NewConstants.WORKSHOP_LAYOUT_FILE), DECK_EDITOR_CONSTRUCTED( VDeckEditorUI.SINGLETON_INSTANCE, CDeckEditorUI.SINGLETON_INSTANCE, diff --git a/forge-gui/src/main/java/forge/gui/home/settings/CSubmenuPreferences.java b/forge-gui/src/main/java/forge/gui/home/settings/CSubmenuPreferences.java index 1853889133b..5d1dac1e72c 100644 --- a/forge-gui/src/main/java/forge/gui/home/settings/CSubmenuPreferences.java +++ b/forge-gui/src/main/java/forge/gui/home/settings/CSubmenuPreferences.java @@ -106,6 +106,13 @@ public enum CSubmenuPreferences implements ICDoc { CSubmenuPreferences.this.resetDeckEditorLayout(); } }); + + view.getBtnDeleteWorkshopUI().setCommand(new Command() { + @Override + public void run() { + CSubmenuPreferences.this.resetWorkshopLayout(); + } + }); view.getBtnDeleteMatchUI().setCommand(new Command() { @Override @@ -178,6 +185,19 @@ public enum CSubmenuPreferences implements ICDoc { } } + private void resetWorkshopLayout() { + String userPrompt = + "This will reset the Workshop screen layout.\n" + + "All tabbed views will be restored to their default positions.\n\n" + + "Reset layout?"; + int reply = JOptionPane.showConfirmDialog(JOptionPane.getRootFrame(), userPrompt, "Reset Workshop Layout", JOptionPane.YES_NO_OPTION); + if (reply == JOptionPane.YES_OPTION) { + if (FScreen.WORKSHOP_SCREEN.deleteLayoutFile()) { + JOptionPane.showMessageDialog(JOptionPane.getRootFrame(), "Workshop layout has been reset."); + } + } + } + private void resetMatchScreenLayout() { String userPrompt = "This will reset the layout of the Match screen.\n" + diff --git a/forge-gui/src/main/java/forge/gui/home/settings/VSubmenuPreferences.java b/forge-gui/src/main/java/forge/gui/home/settings/VSubmenuPreferences.java index 8e55e7cd05c..daa6dbfd2e7 100644 --- a/forge-gui/src/main/java/forge/gui/home/settings/VSubmenuPreferences.java +++ b/forge-gui/src/main/java/forge/gui/home/settings/VSubmenuPreferences.java @@ -62,6 +62,7 @@ public enum VSubmenuPreferences implements IVSubmenu { private final FLabel btnReset = new FLabel.Builder().opaque(true).hoverable(true).text("Reset to Default Settings").build(); private final FLabel btnDeleteMatchUI = new FLabel.Builder().opaque(true).hoverable(true).text("Reset Match Layout").build(); private final FLabel btnDeleteEditorUI = new FLabel.Builder().opaque(true).hoverable(true).text("Reset Editor Layout").build(); + private final FLabel btnDeleteWorkshopUI = new FLabel.Builder().opaque(true).hoverable(true).text("Reset Workshop Layout").build(); private final FLabel btnPlayerName = new FLabel.Builder().opaque(true).hoverable(true).text("").build(); private final JCheckBox cbRemoveSmall = new OptionsCheckBox("Remove Small Creatures"); @@ -103,18 +104,16 @@ public enum VSubmenuPreferences implements IVSubmenu { final String sectionConstraints = "w 80%!, h 42px!, gap 10% 0 10px 10px, span 2 1"; final String regularConstraints = "w 80%!, h 22px!, gap 10% 0 0 10px, span 2 1"; - // Troubleshooting pnlPrefs.add(new SectionLabel("Troubleshooting"), sectionConstraints); - //pnlPrefs.add(new SectionLabel(" "), sectionConstraints); - pnlPrefs.add(btnReset, regularConstraints + ", h 30px!"); - - final String twoButtonConstraints = "w 38%!, h 30px!, gap 10% 0 0 10px"; - pnlPrefs.add(btnDeleteMatchUI, twoButtonConstraints); - pnlPrefs.add(btnDeleteEditorUI, "w 38%!, h 30px!, gap 0 0 0 10px"); - // Reset button - + // Reset buttons + final String twoButtonConstraints1 = "w 38%!, h 30px!, gap 10% 0 0 10px"; + final String twoButtonConstraints2 = "w 38%!, h 30px!, gap 0 0 0 10px"; + pnlPrefs.add(btnReset, twoButtonConstraints1); + pnlPrefs.add(btnDeleteMatchUI, twoButtonConstraints2); + pnlPrefs.add(btnDeleteEditorUI, twoButtonConstraints1); + pnlPrefs.add(btnDeleteWorkshopUI, twoButtonConstraints2); // General Configuration pnlPrefs.add(new SectionLabel("General Configuration"), sectionConstraints + ", gaptop 2%"); @@ -508,6 +507,10 @@ public enum VSubmenuPreferences implements IVSubmenu { public final FLabel getBtnDeleteEditorUI() { return btnDeleteEditorUI; } + + public final FLabel getBtnDeleteWorkshopUI() { + return btnDeleteWorkshopUI; + } /* (non-Javadoc) * @see forge.gui.framework.IVDoc#getDocumentID() diff --git a/forge-gui/src/main/java/forge/gui/home/settings/VSubmenuReleaseNotes.java b/forge-gui/src/main/java/forge/gui/home/settings/VSubmenuReleaseNotes.java index 29d2c6c6a82..43c8b1376d7 100644 --- a/forge-gui/src/main/java/forge/gui/home/settings/VSubmenuReleaseNotes.java +++ b/forge-gui/src/main/java/forge/gui/home/settings/VSubmenuReleaseNotes.java @@ -74,8 +74,6 @@ public enum VSubmenuReleaseNotes implements IVSubmenu { scroller = new JScrollPane(tar); pnlMain.add(scroller, "w 100%!, h 100%!"); - - } /* (non-Javadoc) diff --git a/forge-gui/src/main/java/forge/gui/toolbox/itemmanager/ItemManager.java b/forge-gui/src/main/java/forge/gui/toolbox/itemmanager/ItemManager.java index 4515b52d22e..17926e816ac 100644 --- a/forge-gui/src/main/java/forge/gui/toolbox/itemmanager/ItemManager.java +++ b/forge-gui/src/main/java/forge/gui/toolbox/itemmanager/ItemManager.java @@ -29,6 +29,8 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.KeyStroke; +import javax.swing.ListSelectionModel; +import javax.swing.event.ListSelectionListener; import com.google.common.base.Predicate; @@ -68,8 +70,10 @@ public abstract class ItemManager extends JPanel { private final List> orderedFilters = new ArrayList>(); private boolean wantUnique = false; private boolean alwaysNonUnique = false; + private boolean allowMultipleSelections = false; private final Class genericType; private final Map statLabels; + private final ArrayList selectionListeners = new ArrayList(); private final FLabel btnAddFilter = new FLabel.ButtonBuilder() .text("Add") @@ -94,6 +98,7 @@ public abstract class ItemManager extends JPanel { //build table view this.table = new ItemTable(this, this.model); + this.table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); this.tableScroller = new JScrollPane(this.table); this.tableScroller.setOpaque(false); this.tableScroller.getViewport().setOpaque(false); @@ -133,8 +138,7 @@ public abstract class ItemManager extends JPanel { } @Override - public void doLayout() - { + public void doLayout() { //int number = 0; LayoutHelper helper = new LayoutHelper(this); /*for (ItemFilter filter : this.orderedFilters) { @@ -240,7 +244,7 @@ public abstract class ItemManager extends JPanel { * * @return InventoryItem */ - public InventoryItem getSelectedItem() { + public T getSelectedItem() { return this.table.getSelectedItem(); } @@ -250,7 +254,7 @@ public abstract class ItemManager extends JPanel { * * @return List */ - public List getSelectedItems() { + public List getSelectedItems() { return this.table.getSelectedItems(); } @@ -471,7 +475,7 @@ public abstract class ItemManager extends JPanel { * * setWantUnique. * - * @param unique if true, the editor will be set to the "unique item names only" mode. + * @param unique - if true, the editor will be set to the "unique item names only" mode. */ public void setWantUnique(boolean unique) { this.wantUnique = this.alwaysNonUnique ? false : unique; @@ -481,7 +485,7 @@ public abstract class ItemManager extends JPanel { * * getAlwaysNonUnique. * - * @return if ture, this editor must always show non-unique items (e.g. quest editor). + * @return if true, this editor must always show non-unique items (e.g. quest editor). */ public boolean getAlwaysNonUnique() { return this.alwaysNonUnique; @@ -491,11 +495,33 @@ public abstract class ItemManager extends JPanel { * * setAlwaysNonUnique. * - * @param nonUniqueOnly if true, this editor must always show non-unique items (e.g. quest editor). + * @param nonUniqueOnly - if true, this editor must always show non-unique items (e.g. quest editor). */ public void setAlwaysNonUnique(boolean nonUniqueOnly) { this.alwaysNonUnique = nonUniqueOnly; } + + /** + * + * getAllowMultipleSelections. + * + * @return if true, multiple items can be selected at once + */ + public boolean getAllowMultipleSelections() { + return this.allowMultipleSelections; + } + + /** + * + * getAllowMultipleSelections. + * + * @return allowMultipleSelections0 - if true, multiple items can be selected at once + */ + public void setAllowMultipleSelections(boolean allowMultipleSelections0) { + if (this.allowMultipleSelections == allowMultipleSelections0) { return; } + this.allowMultipleSelections = allowMultipleSelections0; + this.table.setSelectionMode(allowMultipleSelections0 ? ListSelectionModel.MULTIPLE_INTERVAL_SELECTION : ListSelectionModel.SINGLE_SELECTION); + } /** * @@ -509,4 +535,17 @@ public abstract class ItemManager extends JPanel { this.table.changeSelection(0, 0, false, false); } } + + public void addSelectionListener(ListSelectionListener listener) { + selectionListeners.remove(listener); //ensure listener not added multiple times + selectionListeners.add(listener); + } + + public void removeSelectionListener(ListSelectionListener listener) { + selectionListeners.remove(listener); + } + + public Iterable getSelectionListeners() { + return selectionListeners; + } } diff --git a/forge-gui/src/main/java/forge/gui/toolbox/itemmanager/table/ItemTable.java b/forge-gui/src/main/java/forge/gui/toolbox/itemmanager/table/ItemTable.java index dabafb3769b..9084178a8c1 100644 --- a/forge-gui/src/main/java/forge/gui/toolbox/itemmanager/table/ItemTable.java +++ b/forge-gui/src/main/java/forge/gui/toolbox/itemmanager/table/ItemTable.java @@ -58,6 +58,10 @@ public final class ItemTable extends JTable { private final FSkin.JTableSkin> skin; private final ItemManager itemManager; private final ItemTableModel tableModel; + + public ItemManager getItemManager() { + return this.itemManager; + } public ItemTableModel getTableModel() { return this.tableModel; @@ -283,7 +287,7 @@ public final class ItemTable extends JTable { * * @return InventoryItem */ - public InventoryItem getSelectedItem() { + public T getSelectedItem() { final int iRow = getSelectedRow(); return iRow >= 0 ? this.tableModel.rowToItem(iRow).getKey() : null; } @@ -294,8 +298,8 @@ public final class ItemTable extends JTable { * * @return List */ - public List getSelectedItems() { - List items = new ArrayList(); + public List getSelectedItems() { + List items = new ArrayList(); for (int row : getSelectedRows()) { items.add(tableModel.rowToItem(row).getKey()); } diff --git a/forge-gui/src/main/java/forge/gui/toolbox/itemmanager/table/ItemTableModel.java b/forge-gui/src/main/java/forge/gui/toolbox/itemmanager/table/ItemTableModel.java index bbdfd337161..d5fb6ae5653 100644 --- a/forge-gui/src/main/java/forge/gui/toolbox/itemmanager/table/ItemTableModel.java +++ b/forge-gui/src/main/java/forge/gui/toolbox/itemmanager/table/ItemTableModel.java @@ -28,7 +28,6 @@ import java.util.Enumeration; import java.util.List; import java.util.Map.Entry; -import javax.swing.JTable; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.event.TableModelEvent; @@ -38,7 +37,6 @@ import javax.swing.table.TableColumnModel; import org.apache.commons.lang3.ArrayUtils; -import forge.gui.deckeditor.CDeckEditorUI; import forge.gui.toolbox.FMouseAdapter; import forge.gui.toolbox.itemmanager.ItemManagerModel; import forge.gui.toolbox.itemmanager.SItemManagerIO; @@ -125,17 +123,13 @@ public final class ItemTableModel extends AbstractTable return (row >= 0) && (row < model.size()) ? model.get(row) : null; } - /** - * Show selected card. - * - * @param table - * the table - */ - public void showSelectedItem(final JTable table) { + public void onSelectionChange(final ItemTable table) { final int row = table.getSelectedRow(); if (row != -1) { - Entry card = this.rowToItem(row); - CDeckEditorUI.SINGLETON_INSTANCE.setCard(null != card ? card.getKey() : null); + ListSelectionEvent event = new ListSelectionEvent(table.getItemManager(), row, row, false); + for (ListSelectionListener listener : table.getItemManager().getSelectionListeners()) { + listener.valueChanged(event); + } } } @@ -143,7 +137,7 @@ public final class ItemTableModel extends AbstractTable @Override public void valueChanged(final ListSelectionEvent arg0) { if (table.isFocusOwner()) { - ItemTableModel.this.showSelectedItem(table); + ItemTableModel.this.onSelectionChange(table); } } }; @@ -151,7 +145,7 @@ public final class ItemTableModel extends AbstractTable private final FocusAdapter focusAdapter = new FocusAdapter() { @Override public void focusGained(final FocusEvent e) { - ItemTableModel.this.showSelectedItem(table); + ItemTableModel.this.onSelectionChange(table); } }; diff --git a/forge-gui/src/main/java/forge/gui/workshop/CWorkshopUI.java b/forge-gui/src/main/java/forge/gui/workshop/CWorkshopUI.java new file mode 100644 index 00000000000..29ae40e001b --- /dev/null +++ b/forge-gui/src/main/java/forge/gui/workshop/CWorkshopUI.java @@ -0,0 +1,89 @@ +/* + * Forge: Play Magic: the Gathering. + * Copyright (C) 2011 Forge Team + * + * This program 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. + * + * This program 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 this program. If not, see . + */ +package forge.gui.workshop; + +import java.util.List; + +import javax.swing.JMenu; + +import forge.Command; +import forge.Singletons; +import forge.gui.framework.ICDoc; +import forge.gui.menus.IMenuProvider; +import forge.gui.toolbox.itemmanager.CardManager; +import forge.gui.toolbox.itemmanager.SItemManagerIO; +import forge.gui.toolbox.itemmanager.SItemManagerIO.EditorPreference; +import forge.gui.toolbox.itemmanager.table.ItemTable; +import forge.gui.workshop.controllers.CWorkshopCatalog; +import forge.gui.workshop.views.VWorkshopCatalog; +import forge.item.PaperCard; + +/** + * Constructs instance of workshop UI controller, used as a single point of + * top-level control for child UIs. Tasks targeting the view of individual + * components are found in a separate controller for that component and + * should not be included here. + * + *

(C at beginning of class name denotes a control class.) + */ +public enum CWorkshopUI implements ICDoc, IMenuProvider { + /** */ + SINGLETON_INSTANCE; + + private CWorkshopUI() { + } + + /* (non-Javadoc) + * @see forge.gui.menubar.IMenuProvider#getMenus() + */ + @Override + public List getMenus() { + return null; //TODO: Create Workshop menus + } + + /* (non-Javadoc) + * @see forge.gui.framework.ICDoc#getCommandOnSelect() + */ + @Override + public Command getCommandOnSelect() { + return null; + } + + /* (non-Javadoc) + * @see forge.gui.framework.ICDoc#initialize() + */ + @Override + public void initialize() { + Singletons.getControl().getForgeMenu().setProvider(this); + final CardManager cardManager = VWorkshopCatalog.SINGLETON_INSTANCE.getCardManager(); + final ItemTable cardTable = cardManager.getTable(); + + boolean wantElastic = SItemManagerIO.getPref(EditorPreference.elastic_columns); + boolean wantUnique = SItemManagerIO.getPref(EditorPreference.display_unique_only); + cardTable.setWantElasticColumns(wantElastic); + cardManager.setWantUnique(wantUnique); + CWorkshopCatalog.SINGLETON_INSTANCE.applyCurrentFilter(); + } + + /* (non-Javadoc) + * @see forge.gui.framework.ICDoc#update() + */ + @Override + public void update() { } +} + diff --git a/forge-gui/src/main/java/forge/gui/workshop/SWorkshopIO.java b/forge-gui/src/main/java/forge/gui/workshop/SWorkshopIO.java new file mode 100644 index 00000000000..d32e12a514b --- /dev/null +++ b/forge-gui/src/main/java/forge/gui/workshop/SWorkshopIO.java @@ -0,0 +1,9 @@ +package forge.gui.workshop; + +/** + * Handles worksho saving and loading. + * + *

(S at beginning of class name denotes a static factory.) + */ +public class SWorkshopIO { +} diff --git a/forge-gui/src/main/java/forge/gui/workshop/VWorkshopUI.java b/forge-gui/src/main/java/forge/gui/workshop/VWorkshopUI.java new file mode 100644 index 00000000000..561026d0e3d --- /dev/null +++ b/forge-gui/src/main/java/forge/gui/workshop/VWorkshopUI.java @@ -0,0 +1,62 @@ +package forge.gui.workshop; + +import javax.swing.SwingUtilities; + +import forge.Singletons; +import forge.gui.framework.FScreen; +import forge.gui.framework.IVTopLevelUI; +import forge.gui.workshop.views.VWorkshopCatalog; + +/** +/** + * Top level view class; instantiates and assembles + * tabs used in deck editor UI drag layout.
+ * + *

(V at beginning of class name denotes a view class.) + * + */ +public enum VWorkshopUI implements IVTopLevelUI { + /** */ + SINGLETON_INSTANCE; + + //========== Overridden methods + + /* (non-Javadoc) + * @see forge.gui.framework.IVTopLevelUI#instantiate() + */ + @Override + public void instantiate() { + } + + /* (non-Javadoc) + * @see forge.gui.framework.IVTopLevelUI#populate() + */ + @Override + public void populate() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + VWorkshopCatalog.SINGLETON_INSTANCE.getCardManager().focus(); + } + }); + } + + /* (non-Javadoc) + * @see forge.gui.framework.IVTopLevelUI#onSwitching(forge.gui.framework.FScreen) + */ + @Override + public boolean onSwitching(FScreen fromScreen, FScreen toScreen) { + //TODO: ensure card saved before switching + return true; + } + + /* (non-Javadoc) + * @see forge.gui.framework.IVTopLevelUI#onClosing() + */ + @Override + public boolean onClosing(FScreen screen) { + //don't close tab, but return to home screen if this called + Singletons.getControl().setCurrentScreen(FScreen.HOME_SCREEN); + return false; + } +} diff --git a/forge-gui/src/main/java/forge/gui/workshop/controllers/CCardDesigner.java b/forge-gui/src/main/java/forge/gui/workshop/controllers/CCardDesigner.java new file mode 100644 index 00000000000..f249f4a60f1 --- /dev/null +++ b/forge-gui/src/main/java/forge/gui/workshop/controllers/CCardDesigner.java @@ -0,0 +1,50 @@ +package forge.gui.workshop.controllers; + +import forge.Command; +import forge.gui.framework.ICDoc; +import forge.gui.workshop.views.VCardDesigner; + + +/** + * Controls the "card designer" panel in the workshop UI. + * + *

(C at beginning of class name denotes a control class.) + * + */ +public enum CCardDesigner implements ICDoc { + /** */ + SINGLETON_INSTANCE; + + private CCardDesigner() { + VCardDesigner.SINGLETON_INSTANCE.getBtnSaveCard().setCommand(new Runnable() { + @Override + public void run() { + CCardScript.SINGLETON_INSTANCE.saveChanges(); + } + }); + } + + //========== Overridden methods + + /* (non-Javadoc) + * @see forge.gui.framework.ICDoc#getCommandOnSelect() + */ + @Override + public Command getCommandOnSelect() { + return null; + } + + /* (non-Javadoc) + * @see forge.gui.framework.ICDoc#initialize() + */ + @Override + public void initialize() { + } + + /* (non-Javadoc) + * @see forge.gui.framework.ICDoc#update() + */ + @Override + public void update() { + } +} diff --git a/forge-gui/src/main/java/forge/gui/workshop/controllers/CCardScript.java b/forge-gui/src/main/java/forge/gui/workshop/controllers/CCardScript.java new file mode 100644 index 00000000000..a2076b6823e --- /dev/null +++ b/forge-gui/src/main/java/forge/gui/workshop/controllers/CCardScript.java @@ -0,0 +1,126 @@ +package forge.gui.workshop.controllers; + +import java.io.File; +import java.io.PrintWriter; + +import javax.swing.JTextArea; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import forge.Command; +import forge.error.BugReporter; +import forge.gui.framework.ICDoc; +import forge.gui.workshop.views.VCardDesigner; +import forge.gui.workshop.views.VCardScript; +import forge.item.PaperCard; +import forge.util.FileUtil; + + +/** + * Controls the "card script" panel in the workshop UI. + * + *

(C at beginning of class name denotes a control class.) + * + */ +public enum CCardScript implements ICDoc { + /** */ + SINGLETON_INSTANCE; + + private PaperCard currentCard; + private String baseText; + private boolean isTextDirty; + + private CCardScript() { + VCardScript.SINGLETON_INSTANCE.getTarScript().getDocument().addDocumentListener(new DocumentListener() { + @Override + public void removeUpdate(DocumentEvent arg0) { + updateDirtyFlag(); + } + + @Override + public void insertUpdate(DocumentEvent arg0) { + updateDirtyFlag(); + } + + @Override + public void changedUpdate(DocumentEvent arg0) { + //Plain text components do not fire these events + } + }); + } + + private void updateDirtyFlag() { + isTextDirty = !VCardScript.SINGLETON_INSTANCE.getTarScript().getText().equals(baseText); + VCardDesigner.SINGLETON_INSTANCE.getBtnSaveCard().setEnabled(isTextDirty); + VCardScript.SINGLETON_INSTANCE.getTabLabel().setText((isTextDirty ? "*" : "") + "Card Script"); + } + + public void showCard(PaperCard card) { + if (this.currentCard == card) { return; } + this.currentCard = card; + + String text = ""; + boolean editable = false; + File sourceFile = card.getRules().getSourceFile(); + if (sourceFile != null) { + try { + text = FileUtil.readFileToString(sourceFile); + editable = true; + } + catch (final Exception ex) { + text = "Couldn't read file - " + sourceFile + "\n\nException:\n" + ex.toString(); + } + } + this.baseText = text; + + JTextArea tarScript = VCardScript.SINGLETON_INSTANCE.getTarScript(); + tarScript.setText(text); + tarScript.setEditable(editable); + tarScript.setCaretPosition(0); //keep scrolled to top + } + + public void saveChanges() { + if (this.currentCard == null || !this.isTextDirty) { return; } //not need if text hasn't been changed + + File sourceFile = this.currentCard.getRules().getSourceFile(); + if (sourceFile == null) { return; } + + try { + String text = VCardScript.SINGLETON_INSTANCE.getTarScript().getText(); + + PrintWriter p = new PrintWriter(sourceFile); + p.print(text); + p.close(); + + this.baseText = text; + updateDirtyFlag(); + } catch (final Exception ex) { + BugReporter.reportException(ex); + throw new RuntimeException("FileUtil : writeFile() error, problem writing file - " + sourceFile + " : " + ex); + } + } + + //========== Overridden methods + + /* (non-Javadoc) + * @see forge.gui.framework.ICDoc#getCommandOnSelect() + */ + @Override + public Command getCommandOnSelect() { + return null; + } + + /* (non-Javadoc) + * @see forge.gui.framework.ICDoc#initialize() + */ + @Override + public void initialize() { + } + + /* (non-Javadoc) + * @see forge.gui.framework.ICDoc#update() + */ + @Override + public void update() { + } +} diff --git a/forge-gui/src/main/java/forge/gui/workshop/controllers/CWorkshopCatalog.java b/forge-gui/src/main/java/forge/gui/workshop/controllers/CWorkshopCatalog.java new file mode 100644 index 00000000000..9d05454913d --- /dev/null +++ b/forge-gui/src/main/java/forge/gui/workshop/controllers/CWorkshopCatalog.java @@ -0,0 +1,555 @@ +package forge.gui.workshop.controllers; + +import java.awt.Toolkit; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.swing.JComponent; +import javax.swing.JMenu; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JSpinner; +import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.apache.commons.lang3.tuple.Pair; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; + +import forge.Command; +import forge.Singletons; +import forge.card.CardEdition; +import forge.card.EditionCollection; +import forge.game.GameFormat; +import forge.gui.GuiUtils; +import forge.gui.workshop.views.VWorkshopCatalog; +import forge.gui.deckeditor.views.VCardCatalog.RangeTypes; +import forge.gui.framework.ICDoc; +import forge.gui.home.quest.DialogChooseSets; +import forge.gui.toolbox.FLabel; +import forge.gui.toolbox.FSpinner; +import forge.gui.toolbox.itemmanager.SFilterUtil; +import forge.gui.toolbox.itemmanager.SItemManagerUtil; +import forge.gui.toolbox.itemmanager.SItemManagerUtil.StatTypes; +import forge.gui.toolbox.itemmanager.table.SColumnUtil; +import forge.gui.toolbox.itemmanager.table.TableColumnInfo; +import forge.gui.toolbox.itemmanager.table.SColumnUtil.ColumnName; +import forge.item.InventoryItem; +import forge.item.PaperCard; +import forge.item.ItemPredicate; +import forge.quest.QuestWorld; +import forge.quest.data.GameFormatQuest; + +/** + * Controls the "card catalog" panel in the workshop UI. + * + *

(C at beginning of class name denotes a control class.) + * + */ +public enum CWorkshopCatalog implements ICDoc { + /** */ + SINGLETON_INSTANCE; + + private final Set> activePredicates = new HashSet>(); + private final Set activeFormats = new HashSet(); + private final Set activeWorlds = new HashSet(); + private final Set activeRanges = EnumSet.noneOf(RangeTypes.class); + + private boolean disableFiltering = false; + + private CWorkshopCatalog() { + } + + //========== Overridden methods + + /* (non-Javadoc) + * @see forge.gui.framework.ICDoc#getCommandOnSelect() + */ + @Override + public Command getCommandOnSelect() { + return null; + } + + /* (non-Javadoc) + * @see forge.gui.framework.ICDoc#initialize() + */ + @Override + @SuppressWarnings("serial") + public void initialize() { + final Command updateFilterCommand = new Command() { + @Override + public void run() { + if (!disableFiltering) { + applyCurrentFilter(); + } + } + }; + + for (Map.Entry entry : VWorkshopCatalog.SINGLETON_INSTANCE.getStatLabels().entrySet()) { + final FLabel statLabel = entry.getValue(); + statLabel.setCommand(updateFilterCommand); + + //hook so right-clicking a filter in a group toggles itself off and toggles on all other filters in group + final SItemManagerUtil.StatTypes st = entry.getKey(); + final int group = st.group; + if (group > 0) { + statLabel.setRightClickCommand(new Command() { + @Override + public void run() { + if (!disableFiltering) { + disableFiltering = true; + + boolean foundSelected = false; + for (SItemManagerUtil.StatTypes s : SItemManagerUtil.StatTypes.values()) { + if (s.group == group && s != st) { + FLabel lbl = VWorkshopCatalog.SINGLETON_INSTANCE.getCardManager().getStatLabel(s); + if (lbl.getSelected()) { + foundSelected = true; + lbl.setSelected(false); + } + } + } + if (!statLabel.getSelected()) { + statLabel.setSelected(true); + } + else if (!foundSelected) { + //if statLabel only label in group selected, re-select all other labels in group + for (SItemManagerUtil.StatTypes s : SItemManagerUtil.StatTypes.values()) { + if (s.group == group && s != st) { + FLabel lbl = VWorkshopCatalog.SINGLETON_INSTANCE.getCardManager().getStatLabel(s); + if (!lbl.getSelected()) { + lbl.setSelected(true); + } + } + } + } + + disableFiltering = false; + applyCurrentFilter(); + } + } + }); + } + } + + VWorkshopCatalog.SINGLETON_INSTANCE.getStatLabels().get(SItemManagerUtil.StatTypes.TOTAL).setCommand(new Command() { + private boolean lastToggle = true; + + @Override + public void run() { + disableFiltering = true; + lastToggle = !lastToggle; + for (SItemManagerUtil.StatTypes s : SItemManagerUtil.StatTypes.values()) { + if (SItemManagerUtil.StatTypes.TOTAL != s) { + VWorkshopCatalog.SINGLETON_INSTANCE.getCardManager().getStatLabel(s).setSelected(lastToggle); + } + } + disableFiltering = false; + applyCurrentFilter(); + } + }); + + // assemble add restriction menu + final Command addRestrictionCommand = new Command() { + @Override + public void run() { + JPopupMenu popup = new JPopupMenu("RestrictionPopupMenu"); + GuiUtils.addMenuItem(popup, "Current text search", + KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), + new Runnable() { + @Override + public void run() { + addRestriction(buildSearchRestriction(), null, null); + } + }, canSearch()); + JMenu fmt = new JMenu("Format"); + for (final GameFormat f : Singletons.getModel().getFormats()) { + GuiUtils.addMenuItem(fmt, f.getName(), null, new Runnable() { + @Override + public void run() { + addRestriction(buildFormatRestriction(f.toString(), f, true), activeFormats, f); + } + }, !isActive(activeFormats, f)); + } + popup.add(fmt); + GuiUtils.addMenuItem(popup, "Sets...", null, new Runnable() { + @Override + public void run() { + final DialogChooseSets dialog = new DialogChooseSets(null, null, true); + dialog.setOkCallback(new Runnable() { + @Override + public void run() { + List setCodes = dialog.getSelectedSets(); + + if (setCodes.isEmpty()) { + return; + } + + StringBuilder label = new StringBuilder("Sets:"); + boolean truncated = false; + for (String code : setCodes) + { + // don't let the full label get too long + if (32 > label.length()) { + label.append(" ").append(code).append(";"); + } else { + truncated = true; + break; + } + } + + // chop off last semicolons + label.delete(label.length() - 1, label.length()); + + if (truncated) { + label.append("..."); + } + + addRestriction(buildSetRestriction(label.toString(), setCodes, dialog.getWantReprints()), null, null); + } + }); + } + }); + JMenu range = new JMenu("Value range"); + for (final RangeTypes t : RangeTypes.values()) { + GuiUtils.addMenuItem(range, t.toLabelString() + " restriction", null, new Runnable() { + @Override + public void run() { + addRestriction(buildRangeRestriction(t), activeRanges, t); + } + }, !isActive(activeRanges, t)); + } + popup.add(range); + JMenu world = new JMenu("Quest world"); + for (final QuestWorld w : Singletons.getModel().getWorlds()) { + GameFormatQuest format = w.getFormat(); + if (null == format) { + // assumes that no world other than the main world will have a null format + format = Singletons.getModel().getQuest().getMainFormat(); + } + final GameFormatQuest f = format; + GuiUtils.addMenuItem(world, w.getName(), null, new Runnable() { + @Override + public void run() { + addRestriction(buildFormatRestriction(w.getName(), f, true), activeWorlds, w); + } + }, !isActive(activeWorlds, w) && null != f); + } + popup.add(world); + popup.show(VWorkshopCatalog.SINGLETON_INSTANCE.getBtnAddRestriction(), 0, + VWorkshopCatalog.SINGLETON_INSTANCE.getBtnAddRestriction().getHeight()); + } + }; + FLabel btnAddRestriction = VWorkshopCatalog.SINGLETON_INSTANCE.getBtnAddRestriction(); + btnAddRestriction.setCommand(addRestrictionCommand); + btnAddRestriction.setRightClickCommand(addRestrictionCommand); //show menu on right-click too + + VWorkshopCatalog.SINGLETON_INSTANCE.getCbSearchMode().addItemListener(new ItemListener() { + @Override + public void itemStateChanged(ItemEvent arg0) { + applyCurrentFilter(); + } + }); + + Runnable addSearchRestriction = new Runnable() { + @Override + public void run() { + if (canSearch()) { + addRestriction(buildSearchRestriction(), null, null); + } + } + }; + + // add search restriction on ctrl-enter from either the textbox or combobox + VWorkshopCatalog.SINGLETON_INSTANCE.getCbSearchMode().addKeyListener(new _OnCtrlEnter(addSearchRestriction)); + VWorkshopCatalog.SINGLETON_INSTANCE.getTxfSearch().addKeyListener(new _OnCtrlEnter(addSearchRestriction) { + private boolean keypressPending; + @Override + public void keyReleased(KeyEvent e) { + if (KeyEvent.VK_ENTER == e.getKeyCode() && 0 == e.getModifiers()) { + // set focus to table when a plain enter is typed into the text filter box + VWorkshopCatalog.SINGLETON_INSTANCE.getCardManager().getTable().requestFocusInWindow(); + } else if (keypressPending) { + // do this in keyReleased instead of keyTyped since the textbox text isn't updated until the key is released + // but depend on keypressPending since otherwise we pick up hotkeys and other unwanted stuff + applyCurrentFilter(); + } + } + + @Override + public void keyPressed(KeyEvent e) { + super.keyPressed(e); + keypressPending = KeyEvent.VK_ENTER != e.getKeyCode(); + } + }); + + VWorkshopCatalog.SINGLETON_INSTANCE.getLblName().setCommand(updateFilterCommand); + VWorkshopCatalog.SINGLETON_INSTANCE.getLblType().setCommand(updateFilterCommand); + VWorkshopCatalog.SINGLETON_INSTANCE.getLblText().setCommand(updateFilterCommand); + + // ensure mins can's exceed maxes and maxes can't fall below mins + for (Pair sPair : VWorkshopCatalog.SINGLETON_INSTANCE.getSpinners().values()) { + final FSpinner min = sPair.getLeft(); + final FSpinner max = sPair.getRight(); + + min.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent arg0) { + if (Integer.parseInt(max.getValue().toString()) < + Integer.parseInt(min.getValue().toString())) + { + max.setValue(min.getValue()); + } + applyCurrentFilter(); + } + }); + + max.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent arg0) { + if (Integer.parseInt(min.getValue().toString()) > + Integer.parseInt(max.getValue().toString())) + { + min.setValue(max.getValue()); + } + applyCurrentFilter(); + } + }); + } + } + + private class _OnCtrlEnter extends KeyAdapter { + private final Runnable action; + _OnCtrlEnter(Runnable action) { + this.action = action; + } + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == 10) { + if (e.isControlDown() || e.isMetaDown()) { + action.run(); + } + } + } + } + + /* (non-Javadoc) + * @see forge.gui.framework.ICDoc#update() + */ + @Override + public void update() { + final List> lstCatalogCols = SColumnUtil.getCatalogDefaultColumns(); + lstCatalogCols.remove(SColumnUtil.getColumn(ColumnName.CAT_QUANTITY)); + VWorkshopCatalog.SINGLETON_INSTANCE.getCardManager().getTable().setup(lstCatalogCols); + } + + public void applyCurrentFilter() { + // The main trick here is to apply a CardPrinted predicate + // to the table. CardRules will lead to difficulties. + + List> cardPredicates = new ArrayList>(); + cardPredicates.add(Predicates.instanceOf(PaperCard.class)); + cardPredicates.add(SFilterUtil.buildColorAndTypeFilter(VWorkshopCatalog.SINGLETON_INSTANCE.getStatLabels())); + cardPredicates.addAll(activePredicates); + + // apply current values in the range filters + for (RangeTypes t : RangeTypes.values()) { + if (activeRanges.contains(t)) { + cardPredicates.add(SFilterUtil.buildIntervalFilter(VWorkshopCatalog.SINGLETON_INSTANCE.getSpinners(), t)); + } + } + + // get the current contents of the search box + cardPredicates.add(SFilterUtil.buildTextFilter( + VWorkshopCatalog.SINGLETON_INSTANCE.getTxfSearch().getText(), + 0 != VWorkshopCatalog.SINGLETON_INSTANCE.getCbSearchMode().getSelectedIndex(), + VWorkshopCatalog.SINGLETON_INSTANCE.getLblName().getSelected(), + VWorkshopCatalog.SINGLETON_INSTANCE.getLblType().getSelected(), + VWorkshopCatalog.SINGLETON_INSTANCE.getLblText().getSelected())); + + Predicate cardFilter = Predicates.and(cardPredicates); + + // show packs and decks in the card shop according to the toggle setting + // this is special-cased apart from the buildColorAndTypeFilter() above + if (VWorkshopCatalog.SINGLETON_INSTANCE.getCardManager().getStatLabel(StatTypes.PACK).getSelected()) { + List> itemPredicates = new ArrayList>(); + itemPredicates.add(cardFilter); + itemPredicates.add(ItemPredicate.Presets.IS_PACK); + itemPredicates.add(ItemPredicate.Presets.IS_DECK); + cardFilter = Predicates.or(itemPredicates); + } + + // Apply to table + VWorkshopCatalog.SINGLETON_INSTANCE.getCardManager().setFilterPredicate(cardFilter); + } + + private boolean canSearch() { + return !VWorkshopCatalog.SINGLETON_INSTANCE.getTxfSearch().getText().isEmpty() && + (VWorkshopCatalog.SINGLETON_INSTANCE.getLblName().getSelected() || + VWorkshopCatalog.SINGLETON_INSTANCE.getLblType().getSelected() || + VWorkshopCatalog.SINGLETON_INSTANCE.getLblText().getSelected()); + } + + private boolean isActive(Set activeSet, T key) { + return activeSet.contains(key); + } + + @SuppressWarnings("serial") + private void addRestriction(Pair> restriction, final Set activeSet, final T key) { + final Predicate predicate = restriction.getRight(); + + if (null != predicate && activePredicates.contains(predicate)) { + return; + } + + VWorkshopCatalog.SINGLETON_INSTANCE.addRestrictionWidget(restriction.getLeft(), new Command() { + @Override + public void run() { + if (null != key) { + activeSet.remove(key); + } + if (null != predicate) { + activePredicates.remove(predicate); + } + applyCurrentFilter(); + } + }); + + if (null != key) { + activeSet.add(key); + } + if (null != predicate) { + activePredicates.add(predicate); + } + + applyCurrentFilter(); + } + + private Pair> buildRangeRestriction(RangeTypes t) { + final Pair s = VWorkshopCatalog.SINGLETON_INSTANCE.getSpinners().get(t); + s.getLeft().setValue(0); + s.getRight().setValue(10); + + // set focus to lower bound widget + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + ((JSpinner.DefaultEditor)s.getLeft().getEditor()).getTextField().requestFocusInWindow(); + } + }); + + return Pair.of(VWorkshopCatalog.SINGLETON_INSTANCE.buildRangeRestrictionWidget(t), null); + } + + private String buildSearchRestrictionText(String text, boolean isInverse, boolean wantName, boolean wantType, boolean wantText) { + StringBuilder sb = new StringBuilder(); + sb.append(isInverse ? "Without" : "Contains"); + sb.append(": '").append(text).append("' in:"); + if (wantName) { sb.append(" name,"); } + if (wantType) { sb.append(" type,"); } + if (wantText) { sb.append(" text,"); } + sb.delete(sb.length() - 1, sb.length()); // chop off last comma + + return sb.toString(); + } + + private Pair> buildSearchRestriction() { + boolean isInverse = + VWorkshopCatalog.SEARCH_MODE_INVERSE_INDEX == VWorkshopCatalog.SINGLETON_INSTANCE.getCbSearchMode().getSelectedIndex(); + String text = VWorkshopCatalog.SINGLETON_INSTANCE.getTxfSearch().getText(); + boolean wantName = VWorkshopCatalog.SINGLETON_INSTANCE.getLblName().getSelected(); + boolean wantType = VWorkshopCatalog.SINGLETON_INSTANCE.getLblType().getSelected(); + boolean wantText = VWorkshopCatalog.SINGLETON_INSTANCE.getLblText().getSelected(); + + String shortText = buildSearchRestrictionText(text, isInverse, wantName, wantType, wantText); + String fullText = null; + if (25 < text.length()) { + fullText = shortText; + shortText = buildSearchRestrictionText(text.substring(0, 22) + "...", + isInverse, wantName, wantType, wantText); + } + + VWorkshopCatalog.SINGLETON_INSTANCE.getTxfSearch().setText(""); + + return Pair.of( + VWorkshopCatalog.SINGLETON_INSTANCE.buildPlainRestrictionWidget(shortText, fullText), + SFilterUtil.buildTextFilter(text, isInverse, wantName, wantType, wantText)); + } + + private Pair> buildFormatRestriction(String displayName, GameFormat format, boolean allowReprints) { + EditionCollection editions = Singletons.getModel().getEditions(); + StringBuilder tooltip = new StringBuilder("Sets:"); + + int lastLen = 0; + int lineLen = 0; + + // use HTML tooltips so we can insert line breaks + List sets = format.getAllowedSetCodes(); + if (null == sets || sets.isEmpty()) { + tooltip.append(" All"); + } else { + for (String code : sets) { + // don't let a single line get too long + if (50 < lineLen) { + tooltip.append("
"); + lastLen += lineLen; + lineLen = 0; + } + + CardEdition edition = editions.get(code); + tooltip.append(" ").append(edition.getName()).append(" (").append(code).append("),"); + lineLen = tooltip.length() - lastLen; + } + + // chop off last comma + tooltip.delete(tooltip.length() - 1, tooltip.length()); + + if (allowReprints) { + tooltip.append("

Allowing identical cards from other sets"); + } + } + + List bannedCards = format.getBannedCardNames(); + if (null != bannedCards && !bannedCards.isEmpty()) { + tooltip.append("

Banned:"); + lastLen += lineLen; + lineLen = 0; + + for (String cardName : bannedCards) { + // don't let a single line get too long + if (50 < lineLen) { + tooltip.append("
"); + lastLen += lineLen; + lineLen = 0; + } + + tooltip.append(" ").append(cardName).append(";"); + lineLen = tooltip.length() - lastLen; + } + + // chop off last semicolon + tooltip.delete(tooltip.length() - 1, tooltip.length()); + } + tooltip.append(""); + + return Pair.of( + VWorkshopCatalog.SINGLETON_INSTANCE.buildPlainRestrictionWidget(displayName, tooltip.toString()), + allowReprints ? format.getFilterRules() : format.getFilterPrinted()); + } + + private Pair> buildSetRestriction(String displayName, List setCodes, boolean allowReprints) { + return buildFormatRestriction(displayName, new GameFormat(null, setCodes, null), allowReprints); + } +} diff --git a/forge-gui/src/main/java/forge/gui/workshop/views/VCardDesigner.java b/forge-gui/src/main/java/forge/gui/workshop/views/VCardDesigner.java new file mode 100644 index 00000000000..0c5f8b25b5e --- /dev/null +++ b/forge-gui/src/main/java/forge/gui/workshop/views/VCardDesigner.java @@ -0,0 +1,102 @@ +package forge.gui.workshop.views; + +import java.awt.Dimension; + +import javax.swing.JPanel; +import javax.swing.SpringLayout; + +import forge.gui.framework.DragCell; +import forge.gui.framework.DragTab; +import forge.gui.framework.EDocID; +import forge.gui.framework.IVDoc; +import forge.gui.toolbox.FLabel; +import forge.gui.toolbox.FSkin; +import forge.gui.workshop.controllers.CCardDesigner; + +/** + * Assembles Swing components of workshop card designer tab. + * + *

(V at beginning of class name denotes a view class.) + */ +public enum VCardDesigner implements IVDoc { + /** */ + SINGLETON_INSTANCE; + + // Fields used with interface IVDoc + private DragCell parentCell; + private final DragTab tab = new DragTab("Card Designer"); + + private FLabel btnSaveCard = new FLabel.Builder() + .opaque(true).hoverable(true) + .text("Save and Apply Card Changes") + .icon(FSkin.getIcon(FSkin.InterfaceIcons.ICO_SAVE)) + .enabled(false) //disabled by default until card changes made + .build(); + + //========== Constructor + private VCardDesigner() { + } + + public FLabel getBtnSaveCard() { + return btnSaveCard; + } + + //========== Overridden methods + + /* (non-Javadoc) + * @see forge.gui.framework.IVDoc#getDocumentID() + */ + @Override + public EDocID getDocumentID() { + return EDocID.WORKSHOP_CARDDESIGNER; + } + + /* (non-Javadoc) + * @see forge.gui.framework.IVDoc#getTabLabel() + */ + @Override + public DragTab getTabLabel() { + return tab; + } + + /* (non-Javadoc) + * @see forge.gui.framework.IVDoc#getLayoutControl() + */ + @Override + public CCardDesigner getLayoutControl() { + return CCardDesigner.SINGLETON_INSTANCE; + } + + /* (non-Javadoc) + * @see forge.gui.framework.IVDoc#setParentCell(forge.gui.framework.DragCell) + */ + @Override + public void setParentCell(final DragCell cell0) { + this.parentCell = cell0; + } + + /* (non-Javadoc) + * @see forge.gui.framework.IVDoc#getParentCell() + */ + @Override + public DragCell getParentCell() { + return this.parentCell; + } + + /* (non-Javadoc) + * @see forge.gui.framework.IVDoc#populate() + */ + @Override + public void populate() { + JPanel body = parentCell.getBody(); + SpringLayout layout = new SpringLayout(); + body.setLayout(layout); + layout.putConstraint(SpringLayout.SOUTH, btnSaveCard, -6, SpringLayout.SOUTH, body); + layout.putConstraint(SpringLayout.WEST, btnSaveCard, 6, SpringLayout.WEST, body); + layout.putConstraint(SpringLayout.EAST, btnSaveCard, -6, SpringLayout.EAST, body); + btnSaveCard.setPreferredSize(new Dimension(60, 30)); + body.add(btnSaveCard); + //body.setLayout(new MigLayout("insets 1, gap 0, wrap")); + //body.add(btnSaveCard, "w 100% - 12, h 30px!, ay bottom, gap 6"); + } +} diff --git a/forge-gui/src/main/java/forge/gui/workshop/views/VCardScript.java b/forge-gui/src/main/java/forge/gui/workshop/views/VCardScript.java new file mode 100644 index 00000000000..fb42e74a99a --- /dev/null +++ b/forge-gui/src/main/java/forge/gui/workshop/views/VCardScript.java @@ -0,0 +1,101 @@ +package forge.gui.workshop.views; + +import java.awt.Insets; + +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import net.miginfocom.swing.MigLayout; +import forge.gui.framework.DragCell; +import forge.gui.framework.DragTab; +import forge.gui.framework.EDocID; +import forge.gui.framework.IVDoc; +import forge.gui.toolbox.FSkin; +import forge.gui.workshop.controllers.CCardScript; + +/** + * Assembles Swing components of workshop card script tab. + * + *

(V at beginning of class name denotes a view class.) + */ +public enum VCardScript implements IVDoc { + /** */ + SINGLETON_INSTANCE; + + // Fields used with interface IVDoc + private DragCell parentCell; + private final DragTab tab = new DragTab("Card Script"); + + private final JTextArea tarScript = new JTextArea(); + private final JScrollPane scroller; + + //========== Constructor + private VCardScript() { + FSkin.JTextComponentSkin txtScriptSkin = FSkin.get(tarScript); + txtScriptSkin.setFont(FSkin.getFixedFont(16)); + txtScriptSkin.setForeground(FSkin.getColor(FSkin.Colors.CLR_TEXT)); + txtScriptSkin.setBackground(FSkin.getColor(FSkin.Colors.CLR_THEME2)); + txtScriptSkin.setCaretColor(FSkin.getColor(FSkin.Colors.CLR_TEXT)); + tarScript.setMargin(new Insets(3, 3, 3, 3)); + + scroller = new JScrollPane(tarScript); + scroller.setBorder(null); + scroller.setOpaque(false); + } + + public JTextArea getTarScript() { + return tarScript; + } + + //========== Overridden methods + + /* (non-Javadoc) + * @see forge.gui.framework.IVDoc#getDocumentID() + */ + @Override + public EDocID getDocumentID() { + return EDocID.WORKSHOP_CARDSCRIPT; + } + + /* (non-Javadoc) + * @see forge.gui.framework.IVDoc#getTabLabel() + */ + @Override + public DragTab getTabLabel() { + return tab; + } + + /* (non-Javadoc) + * @see forge.gui.framework.IVDoc#getLayoutControl() + */ + @Override + public CCardScript getLayoutControl() { + return CCardScript.SINGLETON_INSTANCE; + } + + /* (non-Javadoc) + * @see forge.gui.framework.IVDoc#setParentCell(forge.gui.framework.DragCell) + */ + @Override + public void setParentCell(final DragCell cell0) { + this.parentCell = cell0; + } + + /* (non-Javadoc) + * @see forge.gui.framework.IVDoc#getParentCell() + */ + @Override + public DragCell getParentCell() { + return this.parentCell; + } + + /* (non-Javadoc) + * @see forge.gui.framework.IVDoc#populate() + */ + @Override + public void populate() { + JPanel body = parentCell.getBody(); + body.setLayout(new MigLayout("insets 1, gap 0, wrap")); + body.add(scroller, "w 100%, h 100%"); + } +} diff --git a/forge-gui/src/main/java/forge/gui/workshop/views/VWorkshopCatalog.java b/forge-gui/src/main/java/forge/gui/workshop/views/VWorkshopCatalog.java new file mode 100644 index 00000000000..a98bb94443e --- /dev/null +++ b/forge-gui/src/main/java/forge/gui/workshop/views/VWorkshopCatalog.java @@ -0,0 +1,270 @@ +package forge.gui.workshop.views; + +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JSpinner; +import javax.swing.JTextField; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import net.miginfocom.swing.MigLayout; + +import org.apache.commons.lang3.tuple.Pair; + +import forge.Command; +import forge.card.CardDb; +import forge.gui.WrapLayout; +import forge.gui.deckeditor.views.VCardCatalog.RangeTypes; +import forge.gui.framework.DragCell; +import forge.gui.framework.DragTab; +import forge.gui.framework.EDocID; +import forge.gui.framework.IVDoc; +import forge.gui.match.controllers.CDetail; +import forge.gui.match.controllers.CPicture; +import forge.gui.toolbox.FComboBoxWrapper; +import forge.gui.toolbox.FLabel; +import forge.gui.toolbox.FSkin; +import forge.gui.toolbox.FSpinner; +import forge.gui.toolbox.FTextField; +import forge.gui.toolbox.itemmanager.CardManager; +import forge.gui.toolbox.itemmanager.ItemManagerContainer; +import forge.gui.toolbox.itemmanager.SItemManagerUtil; +import forge.gui.workshop.controllers.CCardScript; +import forge.gui.workshop.controllers.CWorkshopCatalog; +import forge.item.ItemPool; +import forge.item.PaperCard; + +/** + * Assembles Swing components of card catalog in workshop. + * + *

(V at beginning of class name denotes a view class.) + * + */ +public enum VWorkshopCatalog implements IVDoc { + /** */ + SINGLETON_INSTANCE; + + public static final int SEARCH_MODE_INVERSE_INDEX = 1; + + // Fields used with interface IVDoc + private DragCell parentCell; + private final DragTab tab = new DragTab("Card Catalog"); + + // Total and color count labels/filter toggles + private final Dimension labelSize = new Dimension(60, 24); + private final JPanel pnlStats = new JPanel(new WrapLayout(FlowLayout.LEFT)); + private final Map statLabels = + new HashMap(); + + // restriction button and search widgets + private final JPanel pnlSearch = new JPanel(new MigLayout("insets 0, gap 5px, center")); + private final FLabel btnAddRestriction = new FLabel.ButtonBuilder() + .text("Add filter") + .tooltip("Click to add custom filters to the card list") + .reactOnMouseDown().build(); + private final FComboBoxWrapper cbSearchMode = new FComboBoxWrapper(); + private final JTextField txfSearch = new FTextField.Builder().ghostText("Search").build(); + private final FLabel lblName = new FLabel.Builder().text("Name").hoverable().selectable().selected().build(); + private final FLabel lblType = new FLabel.Builder().text("Type").hoverable().selectable().selected().build(); + private final FLabel lblText = new FLabel.Builder().text("Text").hoverable().selectable().selected().build(); + private final JPanel pnlRestrictions = new JPanel(new WrapLayout(FlowLayout.LEFT, 10, 5)); + + private final ItemManagerContainer cardManagerContainer = new ItemManagerContainer(); + private final CardManager cardManager; + + private final Map> spinners = new HashMap>(); + + //========== Constructor + /** */ + private VWorkshopCatalog() { + pnlStats.setOpaque(false); + + for (SItemManagerUtil.StatTypes s : SItemManagerUtil.StatTypes.values()) { + FLabel label = buildToggleLabel(s, SItemManagerUtil.StatTypes.TOTAL != s); + statLabels.put(s, label); + JComponent component = label; + if (SItemManagerUtil.StatTypes.TOTAL == s) { + label.setToolTipText("Total cards (click to toggle all filters)"); + } else if (SItemManagerUtil.StatTypes.PACK == s) { + // wrap in a constant-size panel so we can change its visibility without affecting layout + component = new JPanel(new MigLayout("insets 0, gap 0")); + component.setPreferredSize(labelSize); + component.setMinimumSize(labelSize); + component.setOpaque(false); + label.setVisible(false); + component.add(label); + } + pnlStats.add(component); + } + + pnlSearch.setOpaque(false); + pnlSearch.add(btnAddRestriction, "center, w pref+8, h pref+8"); + pnlSearch.add(txfSearch, "pushx, growx"); + cbSearchMode.addItem("in"); + cbSearchMode.addItem("not in"); + cbSearchMode.addTo(pnlSearch, "center"); + pnlSearch.add(lblName, "w pref+8, h pref+8"); + pnlSearch.add(lblType, "w pref+8, h pref+8"); + pnlSearch.add(lblText, "w pref+8, h pref+8"); + + pnlRestrictions.setOpaque(false); + + // fill spinner map + for (RangeTypes t : RangeTypes.values()) { + FSpinner lowerBound = new FSpinner.Builder().maxValue(10).build(); + FSpinner upperBound = new FSpinner.Builder().maxValue(10).build(); + _setupSpinner(lowerBound); + _setupSpinner(upperBound); + spinners.put(t, Pair.of(lowerBound, upperBound)); + } + + this.cardManager = new CardManager(this.statLabels, true); + this.cardManager.setPool(ItemPool.createFrom(CardDb.instance().getAllCards(), PaperCard.class), true); + this.cardManagerContainer.setItemManager(this.cardManager); + + this.cardManager.addSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + PaperCard card = cardManager.getSelectedItem(); + CDetail.SINGLETON_INSTANCE.showCard(card); + CPicture.SINGLETON_INSTANCE.showImage(card); + CCardScript.SINGLETON_INSTANCE.showCard(card); + } + }); + } + + private void _setupSpinner (JSpinner spinner) { + spinner.setFocusable(false); // only the spinner text field should be focusable, not the up/down widget + } + + //========== Overridden from IVDoc + + @Override + public EDocID getDocumentID() { + return EDocID.WORKSHOP_CATALOG; + } + + @Override + public DragTab getTabLabel() { + return tab; + } + + @Override + public CWorkshopCatalog getLayoutControl() { + return CWorkshopCatalog.SINGLETON_INSTANCE; + } + + @Override + public void setParentCell(DragCell cell0) { + this.parentCell = cell0; + } + + @Override + public DragCell getParentCell() { + return this.parentCell; + } + + @Override + public void populate() { + JPanel parentBody = parentCell.getBody(); + parentBody.setLayout(new MigLayout("insets 0, gap 0, wrap, hidemode 3")); + parentBody.add(pnlStats, "w 100:520:520, center"); + parentBody.add(pnlSearch, "w 96%, gap 1% 1%"); + parentBody.add(pnlRestrictions, "w 96%, gapleft 1%, gapright push"); + parentBody.add(cardManagerContainer, "w 98%!, h 100% - 35, gap 1% 0 0 1%"); + } + + //========== Accessor/mutator methods + public FLabel getLblName() { return lblName; } + public FLabel getLblType() { return lblType; } + public FLabel getLblText() { return lblText; } + + public FLabel getBtnAddRestriction() { return btnAddRestriction; } + public FComboBoxWrapper getCbSearchMode() { return cbSearchMode; } + public JTextField getTxfSearch() { return txfSearch; } + + public CardManager getCardManager() { + return cardManager; + } + public Map getStatLabels() { + return statLabels; + } + public Map> getSpinners() { + return spinners; + } + + //========== Other methods + private FLabel buildToggleLabel(SItemManagerUtil.StatTypes s, boolean selectable) { + String tooltip; + if (selectable) { //construct tooltip for selectable toggle labels, indicating click and right-click behavior + String labelString = s.toLabelString(); + tooltip = labelString + " (click to toggle the filter, right-click to show only " + labelString.toLowerCase() + ")"; + } + else { tooltip = ""; } + + FLabel label = new FLabel.Builder() + .icon(s.img).iconScaleAuto(false) + .fontSize(11) + .tooltip(tooltip) + .hoverable().selectable(selectable).selected(selectable) + .build(); + + label.setPreferredSize(labelSize); + label.setMinimumSize(labelSize); + + return label; + } + + @SuppressWarnings("serial") + public void addRestrictionWidget(JComponent component, final Command onRemove) { + final JPanel pnl = new JPanel(new MigLayout("insets 2, gap 2, h 30!")); + + pnl.setOpaque(false); + FSkin.get(pnl).setMatteBorder(1, 2, 1, 2, FSkin.getColor(FSkin.Colors.CLR_TEXT)); + + pnl.add(component, "h 30!, center"); + pnl.add(new FLabel.Builder().text("X").fontSize(10).hoverable(true) + .tooltip("Remove filter").cmdClick(new Command() { + @Override + public void run() { + pnlRestrictions.remove(pnl); + refreshRestrictionWidgets(); + onRemove.run(); + } + }).build(), "top"); + + pnlRestrictions.add(pnl, "h 30!"); + refreshRestrictionWidgets(); + } + + public void refreshRestrictionWidgets() { + Container parent = pnlRestrictions.getParent(); + pnlRestrictions.validate(); + parent.validate(); + parent.repaint(); + } + + public JPanel buildRangeRestrictionWidget(RangeTypes t) { + JPanel pnl = new JPanel(new MigLayout("insets 0, gap 2")); + pnl.setOpaque(false); + + Pair s = spinners.get(t); + pnl.add(s.getLeft(), "w 45!, h 26!, center"); + pnl.add(new FLabel.Builder().text("<=").fontSize(11).build(), "h 26!, center"); + pnl.add(new FLabel.Builder().text(t.toLabelString()).fontSize(11).build(), "h 26!, center"); + pnl.add(new FLabel.Builder().text("<=").fontSize(11).build(), "h 26!, center"); + pnl.add(s.getRight(), "w 45!, h 26!, center"); + + return pnl; + } + + public FLabel buildPlainRestrictionWidget(String label, String tooltip) { + return new FLabel.Builder().text(label).tooltip(tooltip).fontSize(11).build(); + } +} diff --git a/forge-gui/src/main/java/forge/properties/NewConstants.java b/forge-gui/src/main/java/forge/properties/NewConstants.java index 99ca31dc0c1..5782cb043fc 100644 --- a/forge-gui/src/main/java/forge/properties/NewConstants.java +++ b/forge-gui/src/main/java/forge/properties/NewConstants.java @@ -88,6 +88,7 @@ public final class NewConstants { public static final FileLocation EDITOR_PREFERENCES_FILE = new FileLocation(_DEFAULTS_DIR, USER_PREFS_DIR, "editor.preferences"); public static final FileLocation WINDOW_LAYOUT_FILE = new FileLocation(_DEFAULTS_DIR, USER_PREFS_DIR, "window.xml"); public static final FileLocation MATCH_LAYOUT_FILE = new FileLocation(_DEFAULTS_DIR, USER_PREFS_DIR, "match.xml"); + public static final FileLocation WORKSHOP_LAYOUT_FILE = new FileLocation(_DEFAULTS_DIR, USER_PREFS_DIR, "workshop.xml"); public static final FileLocation EDITOR_LAYOUT_FILE = new FileLocation(_DEFAULTS_DIR, USER_PREFS_DIR, "editor.xml"); public static final FileLocation GAUNTLET_DIR = new FileLocation(_DEFAULTS_DIR, USER_DIR, "gauntlet/"); diff --git a/forge-gui/src/main/java/forge/util/FileUtil.java b/forge-gui/src/main/java/forge/util/FileUtil.java index 9d906f154c4..c5cfc260bb0 100644 --- a/forge-gui/src/main/java/forge/util/FileUtil.java +++ b/forge-gui/src/main/java/forge/util/FileUtil.java @@ -115,15 +115,22 @@ public final class FileUtil { } // writeAllDecks() public static String readFileToString(String filename) { + return readFileToString(new File(filename)); + } + + public static String readFileToString(File file) { StringBuilder s = new StringBuilder(); - for (String line : readFile(filename)) { - s.append(line).append('\n'); + for (String line : readFile(file)) { + if (s.length() > 0) { + s.append('\n'); + } + s.append(line); } return s.toString(); } public static List readFile(final String filename) { - return FileUtil.readFile(new File(filename)); + return readFile(new File(filename)); } // reads line by line and adds each line to the ArrayList diff --git a/forge-gui/src/main/java/forge/view/FNavigationBar.java b/forge-gui/src/main/java/forge/view/FNavigationBar.java index 1e328f34787..cd87063610a 100644 --- a/forge-gui/src/main/java/forge/view/FNavigationBar.java +++ b/forge-gui/src/main/java/forge/view/FNavigationBar.java @@ -89,6 +89,7 @@ public class FNavigationBar extends FTitleBarBase { addNavigationTab(FScreen.HOME_SCREEN); addNavigationTab(FScreen.DECK_EDITOR_CONSTRUCTED); + addNavigationTab(FScreen.WORKSHOP_SCREEN); super.addControls();