diff --git a/.gitattributes b/.gitattributes index 7f9009ef7fe..a8bdee7987c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -9805,6 +9805,7 @@ res/images/deckeditor/filter_sorcery_n.png -text svneol=unset#image/png res/images/deckeditor/filter_sorcery_y.png -text svneol=unset#image/png res/images/deckeditor/filter_white_n.png -text svneol=unset#image/png res/images/deckeditor/filter_white_y.png -text svneol=unset#image/png +res/images/skins/default/bg_match.jpg -text res/images/skins/default/bg_splash.jpg -text res/images/skins/default/btnLdown.png -text res/images/skins/default/btnLover.png -text @@ -9818,9 +9819,9 @@ res/images/skins/default/btnRup.png -text res/images/skins/default/font1.ttf -text res/images/skins/default/font2.ttf -text res/images/skins/default/palette.png -text +res/images/skins/default/sprite.png -text res/images/skins/default/texture1.jpg -text -res/images/skins/default/texture2.jpg -text -res/images/skins/default/texture3.jpg -text +res/images/skins/rebel/bg_match.jpg -text res/images/skins/rebel/bg_splash.jpg -text res/images/skins/rebel/btnLdown.png -text res/images/skins/rebel/btnLover.png -text @@ -9834,9 +9835,8 @@ res/images/skins/rebel/btnRup.png -text res/images/skins/rebel/font1.ttf -text res/images/skins/rebel/font2.ttf -text res/images/skins/rebel/palette.png -text +res/images/skins/rebel/sprite.png -text res/images/skins/rebel/texture1.jpg -text -res/images/skins/rebel/texture2.jpg -text -res/images/skins/rebel/texture3.jpg -text res/images/symbols-13/0.png -text svneol=unset#image/png res/images/symbols-13/1.png -text svneol=unset#image/png res/images/symbols-13/10.png -text svneol=unset#image/png @@ -9891,6 +9891,12 @@ res/images/symbols-13/counters2.png -text res/images/symbols-13/counters3.png -text res/images/symbols-13/countersMulti.png -text res/images/symbols-13/defend.png -text svneol=unset#image/png +res/images/symbols-13/detail_exile.png -text +res/images/symbols-13/detail_flashback.png -text +res/images/symbols-13/detail_grave.png -text +res/images/symbols-13/detail_hand.png -text +res/images/symbols-13/detail_library.png -text +res/images/symbols-13/detail_poison.png -text res/images/symbols-13/foil01.png -text svneol=unset#image/png res/images/symbols-13/foil02.png -text svneol=unset#image/png res/images/symbols-13/foil03.png -text svneol=unset#image/png @@ -10692,6 +10698,18 @@ src/main/java/forge/card/trigger/TriggerTurnFaceUp.java svneol=native#text/plain src/main/java/forge/card/trigger/TriggerUnequip.java svneol=native#text/plain src/main/java/forge/card/trigger/TriggerUntaps.java svneol=native#text/plain src/main/java/forge/card/trigger/package-info.java svneol=native#text/plain +src/main/java/forge/control/ControlAllUI.java -text +src/main/java/forge/control/ControlEditorUI.java -text +src/main/java/forge/control/ControlHomeUI.java -text +src/main/java/forge/control/ControlMatchUI.java -text +src/main/java/forge/control/match/ControlCardviewer.java -text +src/main/java/forge/control/match/ControlDock.java -text +src/main/java/forge/control/match/ControlField.java -text +src/main/java/forge/control/match/ControlHand.java -text +src/main/java/forge/control/match/ControlInput.java -text +src/main/java/forge/control/match/ControlTabber.java -text +src/main/java/forge/control/match/package-info.java -text +src/main/java/forge/control/package-info.java -text src/main/java/forge/deck/Deck.java svneol=native#text/plain src/main/java/forge/deck/DeckGeneration.java -text src/main/java/forge/deck/DeckManager.java svneol=native#text/plain @@ -10848,6 +10866,18 @@ src/main/java/forge/quest/gui/main/package-info.java svneol=native#text/plain src/main/java/forge/quest/gui/package-info.java svneol=native#text/plain src/main/java/forge/quest/package-info.java svneol=native#text/plain src/main/java/forge/view/FView.java svneol=native#text/plain +src/main/java/forge/view/GuiTopLevel.java -text +src/main/java/forge/view/home/HomeTopLevel.java -text +src/main/java/forge/view/match/ViewAreaBattlefield.java -text +src/main/java/forge/view/match/ViewAreaSidebar.java -text +src/main/java/forge/view/match/ViewAreaUser.java -text +src/main/java/forge/view/match/ViewCardviewer.java -text +src/main/java/forge/view/match/ViewDock.java -text +src/main/java/forge/view/match/ViewField.java -text +src/main/java/forge/view/match/ViewHand.java -text +src/main/java/forge/view/match/ViewInput.java -text +src/main/java/forge/view/match/ViewTabber.java -text +src/main/java/forge/view/match/ViewTopLevel.java -text src/main/java/forge/view/package-info.java svneol=native#text/plain src/main/java/forge/view/swing/ApplicationView.java svneol=native#text/plain src/main/java/forge/view/swing/GuiHomeScreen.java svneol=native#text/plain @@ -10859,6 +10889,10 @@ src/main/java/forge/view/swing/SplashProgressModel.java -text src/main/java/forge/view/swing/WinLoseFrame.java -text src/main/java/forge/view/swing/WinLoseModeHandler.java -text src/main/java/forge/view/swing/package-info.java svneol=native#text/plain +src/main/java/forge/view/toolbox/CardDetailPanel.java -text +src/main/java/forge/view/toolbox/CardViewer.java -text +src/main/java/forge/view/toolbox/FOverlay.java -text +src/main/java/forge/view/toolbox/FVerticalTabPanel.java -text src/main/java/net/slightlymagic/braids/LICENSE.txt svneol=native#text/plain src/main/java/net/slightlymagic/braids/util/ClumsyRunnable.java svneol=native#text/plain src/main/java/net/slightlymagic/braids/util/ImmutableIterableFrom.java svneol=native#text/plain diff --git a/.gitignore b/.gitignore index a9ec3ba43b7..144d82156da 100644 --- a/.gitignore +++ b/.gitignore @@ -17,8 +17,22 @@ res/quest/questData.dat res/quest/questData.dat.xml res/reprintSetInfo.log res/setInfoScript.log +src/main/java/forge/control +src/main/java/forge/gui/control +src/main/java/forge/gui/ds +src/main/java/forge/gui/editor +src/main/java/forge/gui/home src/main/java/forge/gui/match +src/main/java/forge/gui/matchOLD src/main/java/forge/gui/toolbox src/main/java/forge/gui/unfinished +src/main/java/forge/interfaces +src/main/java/forge/view/components +src/main/java/forge/view/controllers +src/main/java/forge/view/editor +src/main/java/forge/view/home +src/main/java/forge/view/interfaces +src/main/java/forge/view/match +src/main/java/forge/view/toolbox /target /test-output diff --git a/res/images/skins/default/bg_match.jpg b/res/images/skins/default/bg_match.jpg new file mode 100644 index 00000000000..51a480836b8 Binary files /dev/null and b/res/images/skins/default/bg_match.jpg differ diff --git a/res/images/skins/default/sprite.png b/res/images/skins/default/sprite.png new file mode 100644 index 00000000000..0492e8ce5cf Binary files /dev/null and b/res/images/skins/default/sprite.png differ diff --git a/res/images/skins/default/texture2.jpg b/res/images/skins/default/texture2.jpg deleted file mode 100644 index 6fc0e6f890f..00000000000 Binary files a/res/images/skins/default/texture2.jpg and /dev/null differ diff --git a/res/images/skins/default/texture3.jpg b/res/images/skins/default/texture3.jpg deleted file mode 100644 index 023287f74d9..00000000000 Binary files a/res/images/skins/default/texture3.jpg and /dev/null differ diff --git a/res/images/skins/rebel/bg_match.jpg b/res/images/skins/rebel/bg_match.jpg new file mode 100644 index 00000000000..f84722e6b0b Binary files /dev/null and b/res/images/skins/rebel/bg_match.jpg differ diff --git a/res/images/skins/rebel/sprite.png b/res/images/skins/rebel/sprite.png new file mode 100644 index 00000000000..6b8f9a8567a Binary files /dev/null and b/res/images/skins/rebel/sprite.png differ diff --git a/res/images/skins/rebel/texture2.jpg b/res/images/skins/rebel/texture2.jpg deleted file mode 100644 index 75a621f202d..00000000000 Binary files a/res/images/skins/rebel/texture2.jpg and /dev/null differ diff --git a/res/images/skins/rebel/texture3.jpg b/res/images/skins/rebel/texture3.jpg deleted file mode 100644 index c01bfb91604..00000000000 Binary files a/res/images/skins/rebel/texture3.jpg and /dev/null differ diff --git a/res/images/symbols-13/detail_exile.png b/res/images/symbols-13/detail_exile.png new file mode 100644 index 00000000000..4fc20ba1d23 Binary files /dev/null and b/res/images/symbols-13/detail_exile.png differ diff --git a/res/images/symbols-13/detail_flashback.png b/res/images/symbols-13/detail_flashback.png new file mode 100644 index 00000000000..a71a1b4473e Binary files /dev/null and b/res/images/symbols-13/detail_flashback.png differ diff --git a/res/images/symbols-13/detail_grave.png b/res/images/symbols-13/detail_grave.png new file mode 100644 index 00000000000..1b04cf022bd Binary files /dev/null and b/res/images/symbols-13/detail_grave.png differ diff --git a/res/images/symbols-13/detail_hand.png b/res/images/symbols-13/detail_hand.png new file mode 100644 index 00000000000..0b1487be445 Binary files /dev/null and b/res/images/symbols-13/detail_hand.png differ diff --git a/res/images/symbols-13/detail_library.png b/res/images/symbols-13/detail_library.png new file mode 100644 index 00000000000..91d5698ea14 Binary files /dev/null and b/res/images/symbols-13/detail_library.png differ diff --git a/res/images/symbols-13/detail_poison.png b/res/images/symbols-13/detail_poison.png new file mode 100644 index 00000000000..de6721ad7ad Binary files /dev/null and b/res/images/symbols-13/detail_poison.png differ diff --git a/res/lang/de.properties b/res/lang/de.properties index ea2122654f3..acf48eb9f94 100644 --- a/res/lang/de.properties +++ b/res/lang/de.properties @@ -134,7 +134,7 @@ NewGame/booster_text=Booster Draft (Schwierig) - W NewGame/yourdeck=Dein Deck NewGame/opponent=Gegner NewGame/deckeditor=Deck Editor -NewGame/newgui=New Gui +NewGame/oldgui=Old Gui NewGame/ailand=Stack AI Land NewGame/devmode=Developer Mode NewGame/questmode=Quest Mode @@ -142,7 +142,7 @@ NewGame/startgame=Spiel starten NewGame/savesealed_msg=Please name this sealed deck NewGame/savesealed_ttl=Save Sealed Deck -WinLose/won=Siege: +WinLose/won=Siege: WinLose/lost=, Niederlagen: WinLose/win=Gewonnen WinLose/lose=Verloren diff --git a/res/lang/en.properties b/res/lang/en.properties index 725391cda54..6e9a15ce40f 100644 --- a/res/lang/en.properties +++ b/res/lang/en.properties @@ -172,7 +172,7 @@ NewGame/booster_text=Booster Draft (Hard) - Pick cards 1 at a time to create you NewGame/yourdeck=Your Deck NewGame/opponent=Opponent NewGame/deckeditor=Deck Editor -NewGame/newgui=New Gui +NewGame/oldgui=Old Gui NewGame/ailand=Stack AI Land NewGame/devmode=Developer Mode NewGame/questmode=Quest Mode diff --git a/src/main/java/forge/AllZone.java b/src/main/java/forge/AllZone.java index 43d324f1316..01cdff41e51 100644 --- a/src/main/java/forge/AllZone.java +++ b/src/main/java/forge/AllZone.java @@ -11,6 +11,7 @@ import forge.card.trigger.TriggerHandler; import forge.deck.DeckManager; import forge.game.GameSummary; import forge.gui.input.InputControl; +import forge.view.toolbox.FOverlay; import forge.gui.skin.FSkin; import forge.model.FGameState; import forge.properties.ForgeProps; @@ -78,6 +79,9 @@ public final class AllZone { /** Global display. */ private static Display display; + /** Global overlay. */ + private static FOverlay overlay; + /** Constant DECK_MGR. */ private static DeckManager deckManager; @@ -629,4 +633,18 @@ public final class AllZone { public static void setSkin(final FSkin fs) { skin = fs; } + + /** + * @return overlay + */ + public static FOverlay getOverlay() { + return overlay; + } + + /** + * @param overlay0   Overlay panel + */ + public static void setOverlay(FOverlay overlay0) { + overlay = overlay0; + } } // AllZone diff --git a/src/main/java/forge/Constant.java b/src/main/java/forge/Constant.java index 2168f96ee66..d5c829c72dc 100644 --- a/src/main/java/forge/Constant.java +++ b/src/main/java/forge/Constant.java @@ -40,10 +40,17 @@ public final class Constant { public static final boolean[] MILL = new boolean[1]; /** The Constant DevMode. */ - public static final boolean[] DEV_MODE = new boolean[1]; // one for - // normal mode - // one for quest - // mode + // one for normal mode, one for quest mode + public static final boolean[] DEV_MODE = new boolean[1]; + + /** The Constant HANDVIEW. */ + public static final boolean[] HANDVIEW = new boolean[1]; + + /** The Constant LIBRARYVIEW. */ + public static final boolean[] LIBRARYVIEW = new boolean[1]; + + /** The Constant OLDGUI. */ + public static final boolean[] OLDGUI = new boolean[1]; /** The Constant NetConn. */ public static final boolean[] NET_CONN = new boolean[1]; diff --git a/src/main/java/forge/GameAction.java b/src/main/java/forge/GameAction.java index ba15dbe7eb0..cc21b310079 100644 --- a/src/main/java/forge/GameAction.java +++ b/src/main/java/forge/GameAction.java @@ -42,6 +42,7 @@ import forge.properties.ForgeProps; import forge.properties.NewConstants.Lang.GameAction.GameActionText; import forge.quest.gui.QuestWinLoseHandler; import forge.quest.gui.main.QuestEvent; +import forge.view.match.ViewTopLevel; import forge.view.swing.WinLoseFrame; /** @@ -776,14 +777,22 @@ public class GameAction { final boolean refreeze = AllZone.getStack().isFrozen(); AllZone.getStack().setFrozen(true); - final JFrame frame = (JFrame) AllZone.getDisplay(); - if (!frame.isDisplayable()) { - return; + if (Constant.Runtime.OLDGUI[0]) { + final JFrame frame = (JFrame) AllZone.getDisplay(); + if (!frame.isDisplayable()) { + return; + } + } + else { + final ViewTopLevel frame = (ViewTopLevel) AllZone.getDisplay(); + if (!frame.isDisplayable()) { + return; + } } if (this.canShowWinLose && this.checkEndGameSate()) { AllZone.getDisplay().savePrefs(); - frame.setEnabled(false); + //frame.setEnabled(false); // frame.dispose(); // Gui_WinLose gwl = new Gui_WinLose(AllZone.getMatchState(), diff --git a/src/main/java/forge/Phase.java b/src/main/java/forge/Phase.java index e423f2e5ace..44cdd8c1687 100644 --- a/src/main/java/forge/Phase.java +++ b/src/main/java/forge/Phase.java @@ -516,6 +516,9 @@ public class Phase extends MyObservable implements java.io.Serializable { this.turn++; } + // Visual indicators + PhaseUtil.visuallyActivatePhase(this.getPhase()); + // When consecutively skipping phases (like in combat) this section // pushes through that block this.updateObservers(); diff --git a/src/main/java/forge/PhaseUtil.java b/src/main/java/forge/PhaseUtil.java index 1bfa4db521d..fefc0adb898 100644 --- a/src/main/java/forge/PhaseUtil.java +++ b/src/main/java/forge/PhaseUtil.java @@ -6,6 +6,8 @@ import java.util.HashMap; import forge.Constant.Zone; import forge.card.cardfactory.CardFactoryUtil; import forge.gui.input.Input; +import forge.view.match.ViewField.PhaseLabel; +import forge.view.match.ViewTopLevel; /** *

@@ -423,6 +425,7 @@ public class PhaseUtil { */ public static void handleUpkeep() { Player turn = AllZone.getPhase().getPlayerTurn(); + if (skipUpkeep()) { // Slowtrips all say "on the next turn's upkeep" if there is no // upkeep next turn, the trigger will never occur. @@ -623,4 +626,46 @@ public class PhaseUtil { || phase.equals(Constant.Phase.DRAW) || phase.equals(Constant.Phase.MAIN1) || phase.equals(Constant.Phase.COMBAT_BEGIN); } + + /** + * Retrieves and visually activates phase label for appropriate + * phase and player. + * + * @param s   Phase state + */ + public static void visuallyActivatePhase(String s) { + PhaseLabel lbl = null; + Player p = AllZone.getPhase().getPlayerTurn(); + ViewTopLevel t = (ViewTopLevel) AllZone.getDisplay(); + + int i; // Index of field; computer is 0, human is 1 + if (p.isComputer()) { + i = 0; + } + else { + i = 1; + } + + if (s.equals(Constant.Phase.UPKEEP)) { + lbl = t.getFieldControllers().get(i).getView().getLblUpkeep(); + } + else if (s.equals(Constant.Phase.DRAW)) { + lbl = t.getFieldControllers().get(i).getView().getLblDraw(); + } + else if (s.equals(Constant.Phase.COMBAT_BEGIN)) { + lbl = t.getFieldControllers().get(i).getView().getLblBeginCombat(); + } + else if (s.equals(Constant.Phase.COMBAT_END)) { + lbl = t.getFieldControllers().get(i).getView().getLblEndCombat(); + } + else if (s.equals(Constant.Phase.END_OF_TURN)) { + lbl = t.getFieldControllers().get(i).getView().getLblEndTurn(); + } + else { + return; + } + + t.getController().resetAllPhaseButtons(); + lbl.setActive(true); + } } diff --git a/src/main/java/forge/control/ControlAllUI.java b/src/main/java/forge/control/ControlAllUI.java new file mode 100644 index 00000000000..17ad7e7763a --- /dev/null +++ b/src/main/java/forge/control/ControlAllUI.java @@ -0,0 +1,98 @@ +package forge.control; + +import java.awt.Component; + +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; + +import javax.swing.JLayeredPane; + +import forge.view.GuiTopLevel; +import forge.view.editor.EditorTopLevel; +import forge.view.home.HomeTopLevel; +import forge.view.match.ViewTopLevel; + +/** + *

ControlAllUI.

+ * Controls all Forge UI functionality inside one JFrame. + * This class switches between various display states in that JFrame. + * Controllers are instantiated separately by each state's top level view class. + */ +public class ControlAllUI { + private JLayeredPane display; + private GuiTopLevel view; + private HomeTopLevel home = null; + private ViewTopLevel match = null; + private EditorTopLevel editor = null; + + /** + *

ControlAllUI.

+ * Controls all Forge UI functionality inside one JFrame. + * This class switches between various display states in that JFrame. + * Controllers are instantiated separately by each state's top level view class. + * + */ + public ControlAllUI() { + view = new GuiTopLevel(); + + view.addComponentListener(new ComponentAdapter() { + public void componentResized(ComponentEvent e) { + Component[] children; + children = display.getComponentsInLayer(JLayeredPane.DEFAULT_LAYER); + children[0].setSize(display.getSize()); + + children = display.getComponentsInLayer(JLayeredPane.MODAL_LAYER); + children[0].setSize(display.getSize()); + } + }); + + display = (JLayeredPane) view.getContentPane(); + + changeState(1); + } + + /** + *

changeState.

+ * Switches between display states in top level JFrame. + * + * @param i   State index: 0 for home, 1 for match, etc. + */ + public void changeState(int i) { + home = null; + match = null; + editor = null; + + display.removeAll(); + view.addOverlay(); + + // Fire up new state + switch (i) { + case 0: // Home screen + home = new HomeTopLevel(); + display.add(home); + break; + + case 1: // Match screen + match = new ViewTopLevel(); + display.add(match, JLayeredPane.DEFAULT_LAYER); + break; + + case 2: // Deck editor screen + editor = new EditorTopLevel(); + display.add(editor); + break; + + default: break; + } + } + + /** @return ViewTopLevel */ + public ViewTopLevel getMatchView() { + return match; + } + + /** @return ControlMatchUI */ + public ControlMatchUI getMatchController() { + return match.getController(); + } +} diff --git a/src/main/java/forge/control/ControlEditorUI.java b/src/main/java/forge/control/ControlEditorUI.java new file mode 100644 index 00000000000..536dac28967 --- /dev/null +++ b/src/main/java/forge/control/ControlEditorUI.java @@ -0,0 +1,19 @@ +package forge.control; + +import javax.swing.JPanel; + +/** + *

ControlEditorUI

+ * Top-level controller for deck editor. + * + */ +@SuppressWarnings("serial") +public class ControlEditorUI extends JPanel { + /** + *

ControlEditorUI

+ * Top-level controller for deck editor. + * + */ + public ControlEditorUI() { + } +} diff --git a/src/main/java/forge/control/ControlHomeUI.java b/src/main/java/forge/control/ControlHomeUI.java new file mode 100644 index 00000000000..2c667320168 --- /dev/null +++ b/src/main/java/forge/control/ControlHomeUI.java @@ -0,0 +1,9 @@ +package forge.control; + +/** + * TODO: Write javadoc for this type. + * + */ +public class ControlHomeUI { + +} diff --git a/src/main/java/forge/control/ControlMatchUI.java b/src/main/java/forge/control/ControlMatchUI.java new file mode 100644 index 00000000000..a0524174671 --- /dev/null +++ b/src/main/java/forge/control/ControlMatchUI.java @@ -0,0 +1,240 @@ +package forge.control; + +import java.awt.event.ActionEvent; +import java.awt.event.KeyEvent; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.InputMap; +import javax.swing.JComponent; + +import forge.AllZone; +import forge.Singletons; +import forge.Constant.Zone; +import forge.control.match.ControlField; +import javax.swing.KeyStroke; + +import org.apache.commons.lang3.StringUtils; + +import forge.properties.ForgePreferences; +import forge.view.match.ViewTopLevel; + +/** + *

ControlMatchUI

+ * Top-level controller for matches. Implements Display. + * + */ +public class ControlMatchUI { + private ViewTopLevel view; + + /** + *

ControlMatchUI

+ * Constructs instance of match UI controller, used as a single + * point of top-level control for child UIs - in other words, this class + * controls the controllers. Tasks targeting the view of individual + * components are found in a separate controller for that component + * and should not be included here. + * + * This constructor is called after child components have been instantiated. + * When children are instantiated, they also instantiate their controller. + * So, this class must be called after everything is already in place. + * + * @param v   A ViewTopLevel object + */ + public ControlMatchUI(ViewTopLevel v) { + view = v; + } + + /** + * Fires up controllers for each component of UI. + * + */ + public void initMatch() { + // All child components have been assembled; observers and listeners can + // be added safely. + view.getAreaSidebar().getTabber().getController().addObservers(); + view.getAreaSidebar().getTabber().getController().addListeners(); + + view.getAreaUser().getPnlInput().getController().addListeners(); + + view.getAreaUser().getPnlHand().getController().addObservers(); + view.getAreaUser().getPnlHand().getController().addListeners(); + + // Update all observers with values for start of match. + List fieldControllers = view.getFieldControllers(); + for (ControlField f : fieldControllers) { + f.addObservers(); + f.addListeners(); + f.getPlayer().updateObservers(); + } + + AllZone.getHumanPlayer().getZone(Zone.Hand).updateObservers(); + AllZone.getComputerPlayer().getZone(Zone.Hand).updateObservers(); + AllZone.getStack().updateObservers(); + AllZone.getHumanPlayer().getZone(Zone.Battlefield).updateObservers(); + AllZone.getInputControl().updateObservers(); + view.getAreaSidebar().getTabber().getController().updateObservers(); + this.mapKeyboardShortcuts(); + } + + /** + * Resets all phase buttons in all fields to "inactive", so highlight won't be + * drawn on them. "Enabled" state remains the same. + */ + // This method is in the top-level controller because it affects ALL fields (not just one). + public void resetAllPhaseButtons() { + List fieldControllers = view.getFieldControllers(); + + for (ControlField c : fieldControllers) { + c.resetPhaseButtons(); + } + } + + /** + * Maps actions to shortcuts, and attaches each shortcut to the + * InputMap of the top level view. + * + */ + @SuppressWarnings("serial") + private void mapKeyboardShortcuts() { + InputMap im = ((ViewTopLevel) AllZone.getDisplay()).getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); + ForgePreferences fp = Singletons.getModel().getPreferences(); + String str; + KeyStroke key; + + // Actions which correspond to key presses + Action actShowStack = new AbstractAction() { + public void actionPerformed(ActionEvent e) { view.getTabberController().showPnlStack(); } + }; + + Action actShowCombat = new AbstractAction() { + public void actionPerformed(ActionEvent e) { view.getTabberController().showPnlCombat(); } + }; + + Action actShowConsole = new AbstractAction() { + public void actionPerformed(ActionEvent e) { view.getTabberController().showPnlConsole(); } + }; + + Action actShowPlayers = new AbstractAction() { + public void actionPerformed(ActionEvent e) { view.getTabberController().showPnlPlayers(); } + }; + + Action actShowDev = new AbstractAction() { + public void actionPerformed(ActionEvent e) { view.getTabberController().showPnlDev(); } + }; + + Action actConcede = new AbstractAction() { + public void actionPerformed(ActionEvent e) { view.getDockController().concede(); } + }; + + Action actShowPicture = new AbstractAction() { + public void actionPerformed(ActionEvent e) { view.getCardviewerController().showPnlCardPic(); } + }; + + Action actShowDetail = new AbstractAction() { + public void actionPerformed(ActionEvent e) { view.getCardviewerController().showPnlCardDetail(); } + }; + + // Show stack + // (Get keycode string, convert to char, convert to keystroke, put on input map.) + str = fp.getShowStackShortcut(); + key = KeyStroke.getKeyStroke(codes2Chars(str)); + + im.put(key, str); + ((ViewTopLevel) AllZone.getDisplay()).getActionMap().put(im.get(key), actShowStack); + + // Show combat + str = fp.getShowCombatShortcut(); + key = KeyStroke.getKeyStroke(codes2Chars(str)); + + im.put(key, str); + ((ViewTopLevel) AllZone.getDisplay()).getActionMap().put(im.get(key), actShowCombat); + + // Show console + str = fp.getShowConsoleShortcut(); + key = KeyStroke.getKeyStroke(codes2Chars(str)); + + im.put(key, str); + ((ViewTopLevel) AllZone.getDisplay()).getActionMap().put(im.get(key), actShowConsole); + + // Show players + str = fp.getShowPlayersShortcut(); + key = KeyStroke.getKeyStroke(codes2Chars(str)); + + im.put(key, str); + ((ViewTopLevel) AllZone.getDisplay()).getActionMap().put(im.get(key), actShowPlayers); + + // Show devmode + str = fp.getShowDevShortcut(); + key = KeyStroke.getKeyStroke(codes2Chars(str)); + + im.put(key, str); + ((ViewTopLevel) AllZone.getDisplay()).getActionMap().put(im.get(key), actShowDev); + + // Concede game + str = fp.getConcedeShortcut(); + key = KeyStroke.getKeyStroke(codes2Chars(str)); + + im.put(key, str); + ((ViewTopLevel) AllZone.getDisplay()).getActionMap().put(im.get(key), actConcede); + + // Show card picture + str = fp.getShowPictureShortcut(); + key = KeyStroke.getKeyStroke(codes2Chars(str)); + + im.put(key, str); + ((ViewTopLevel) AllZone.getDisplay()).getActionMap().put(im.get(key), actShowPicture); + + // Show card detail + str = fp.getShowDetailShortcut(); + key = KeyStroke.getKeyStroke(codes2Chars(str)); + + im.put(key, str); + ((ViewTopLevel) AllZone.getDisplay()).getActionMap().put(im.get(key), actShowDetail); + } + + /** + * Converts a string of key codes (space delimited) into their respective + * key texts. This helps juggling between input maps, display text, save + * values, and input data. + * + * @param s0   A string of keycodes + * @return String + */ + private String codes2Chars(String s0) { + List codes = new ArrayList(Arrays.asList(s0.split(" "))); + List displayText = new ArrayList(); + String temp; + + for (String s : codes) { + temp = KeyEvent.getKeyText(Integer.valueOf(s)); + + if (!s.isEmpty()) { + // Probably a better way to do this; but I couldn't find it + // after a decent look around. The main problem is that + // KeyEvent.getKeyText() will return "Ctrl", but the input + // map expects "control". Similar case problems with Shift and Alt. + // Doublestrike 21-11-11 + if (temp.equalsIgnoreCase("ctrl")) { + temp = "control"; + } + else if (temp.equalsIgnoreCase("shift")) { + temp = "shift"; + } + else if (temp.equalsIgnoreCase("alt")) { + temp = "alt"; + } + else if (temp.equalsIgnoreCase("escape")) { + temp = "escape"; + } + + displayText.add(temp); + } + } + + return StringUtils.join(displayText, ' '); + } +} diff --git a/src/main/java/forge/control/match/ControlCardviewer.java b/src/main/java/forge/control/match/ControlCardviewer.java new file mode 100644 index 00000000000..f269dcc3d3e --- /dev/null +++ b/src/main/java/forge/control/match/ControlCardviewer.java @@ -0,0 +1,64 @@ +package forge.control.match; + +import java.awt.Image; + +import forge.Card; +import forge.ImageCache; +import forge.view.match.ViewCardviewer; + +/** + * + * Controls the vertical tabber in sidebar used for + * viewing card details and picture. + * + */ +public class ControlCardviewer { + private ViewCardviewer view; + private Card currentCard = null; + + /** + * Controls the vertical tabber in sidebar used for + * viewing card details and picture. + * @param v   The CardViewer Swing component. + */ + public ControlCardviewer(ViewCardviewer v) { + view = v; + } + + /** + * Shows card details and/or picture in sidebar cardview tabber. + * @param c   Card object + */ + public void showCard(Card c) { + Image img = ImageCache.getImage(c); + this.currentCard = c; + view.getPnlCardPic().setCard(c); + view.getPnlCardDetail().setCard(c); + + if (img != null) { + showPnlCardPic(); + } + else { + showPnlCardDetail(); + } + } + + /** @return Card */ + public Card getCurrentCard() { + return currentCard; + } + + /** + * Programatically forces card layout of sidebar tabber to show "CardDetail" panel. + */ + public void showPnlCardDetail() { + view.getVtpCardviewer().showTab(1); + } + + /** + * Programatically forces card layout of sidebar tabber to show card picture panel. + */ + public void showPnlCardPic() { + view.getVtpCardviewer().showTab(0); + } +} diff --git a/src/main/java/forge/control/match/ControlDock.java b/src/main/java/forge/control/match/ControlDock.java new file mode 100644 index 00000000000..fbd9b4eb358 --- /dev/null +++ b/src/main/java/forge/control/match/ControlDock.java @@ -0,0 +1,106 @@ +package forge.control.match; + +import java.awt.event.KeyEvent; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; + +import forge.AllZone; +import forge.Singletons; +import forge.properties.ForgePreferences; +import forge.view.match.ViewDock; +import forge.view.match.ViewDock.KeyboardShortcutField; + +/** + * Child controller, handles dock button operations. + * + */ +public class ControlDock { + private ViewDock view; + + /** + * Child controller, handles dock button operations. + * @param v   ViewDock obj + */ + public ControlDock(ViewDock v) { + view = v; + } + + /** Concede game, bring up WinLose UI. */ + public void concede() { + AllZone.getHumanPlayer().concede(); + AllZone.getGameAction().checkStateEffects(); + } + + /** @return ViewDock */ + public ViewDock getView() { + return view; + } + + /** Updates and saves ForgePreferences with current shortcuts. */ + public void saveKeyboardShortcuts() { + ForgePreferences fp = Singletons.getModel().getPreferences(); + Map shortcuts = view.getKeyboardShortcutFields(); + + fp.setShowStackShortcut(shortcuts.get("showstack").getCodeString()); + fp.setShowCombatShortcut(shortcuts.get("showcombat").getCodeString()); + fp.setShowPlayersShortcut(shortcuts.get("showplayers").getCodeString()); + fp.setShowConsoleShortcut(shortcuts.get("showconsole").getCodeString()); + fp.setShowDevShortcut(shortcuts.get("showdev").getCodeString()); + fp.setConcedeShortcut(shortcuts.get("concede").getCodeString()); + fp.setShowPictureShortcut(shortcuts.get("showpicture").getCodeString()); + fp.setShowDetailShortcut(shortcuts.get("showdetail").getCodeString()); + + try { + fp.save(); + } catch (Exception e) { + e.printStackTrace(); + } + + AllZone.getOverlay().hideOverlay(); + } + + /** + * - Adds keycode to list stored in name of a text field. + * - Code is not added if already in list. + * - Backspace removes last code in list. + * - Sets text of text field with character equivalent of keycodes. + * + * @param e   KeyEvent + */ + public void addKeyCode(KeyEvent e) { + KeyboardShortcutField ksf = (KeyboardShortcutField) e.getSource(); + String newCode = Integer.toString(e.getKeyCode()); + String codestring = ksf.getCodeString(); + List existingCodes; + + if (codestring != null) { + existingCodes = new ArrayList(Arrays.asList(codestring.split(" "))); + } + else { + existingCodes = new ArrayList(); + } + + // Backspace (8) will remove last code from list. + if (e.getKeyCode() == 8) { + existingCodes.remove(existingCodes.size() - 1); + } + else if (!existingCodes.contains(newCode)) { + existingCodes.add(newCode); + } + + ksf.setCodeString(StringUtils.join(existingCodes, ' ')); + } + + /** */ + public void endTurn() { + // Big thanks to you, Gameplay Guru, since I was too lazy to figure this out + // before release. Doublestrike 24-11-11 + System.err.println("forge.control.match > ControlDock > endTurn()"); + System.out.println("Should skip to the end of turn, or entire turn."); + System.err.println("If some gameplay guru could implement this, that would be great..."); + } +} diff --git a/src/main/java/forge/control/match/ControlField.java b/src/main/java/forge/control/match/ControlField.java new file mode 100644 index 00000000000..6b3981827b6 --- /dev/null +++ b/src/main/java/forge/control/match/ControlField.java @@ -0,0 +1,459 @@ +package forge.control.match; + +import java.awt.Toolkit; +import java.awt.datatransfer.StringSelection; +import java.awt.event.ActionEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map.Entry; +import java.util.Observable; +import java.util.Observer; + +import javax.swing.JOptionPane; + +import net.slightlymagic.braids.util.ImmutableIterableFrom; + +import com.google.code.jyield.Generator; +import com.google.code.jyield.YieldUtils; + +import forge.AllZone; +import forge.Card; +import forge.CardList; +import forge.Constant; +import forge.Constant.Zone; +import forge.GuiDisplayUtil; +import forge.Player; +import forge.PlayerZone; +import forge.card.cardfactory.CardFactoryUtil; +import forge.deck.Deck; +import forge.gui.ForgeAction; +import forge.gui.GuiUtils; +import forge.gui.input.Input; +import forge.gui.input.InputAttack; +import forge.gui.input.InputBlock; +import forge.gui.input.InputPayManaCost; +import forge.gui.input.InputPayManaCostAbility; +import forge.item.CardPrinted; +import forge.properties.ForgeProps; +import forge.properties.NewConstants; +import forge.properties.NewConstants.Lang.GuiDisplay.ComputerHand; +import forge.properties.NewConstants.Lang.GuiDisplay.ComputerLibrary; +import forge.view.match.ViewField; +import forge.view.match.ViewTopLevel; + +/** + * Child controller, applied to single field in battlefield. + * Manages player view functions such as card observers, + * life total changes, graveyard button click, etc. + * + */ +public class ControlField { + private Player player; + private ViewField view; + + /** + * Child controller, applied to single field in battlefield. + * Manages player view functions such as card observers, + * life total changes, graveyard button click, etc. + * + * @param p   The Player this field applies to + * @param v   The Swing component for this field + */ + public ControlField(Player p, ViewField v) { + player = p; + view = v; + } + + /** @return Player */ + public Player getPlayer() { + return player; + } + + /** @return ViewField */ + public ViewField getView() { + return view; + } + + /** + * Adds observers to field components where required: card stats, player stats, etc. + */ + public void addObservers() { + // Hand, Graveyard, Library, Flashback, Exile totals, attached to respective Zones. + Observer o1 = new Observer() { + @Override + public void update(final Observable a, final Object b) { + view.updateZones(player); + } + }; + + player.getZone(Zone.Hand).addObserver(o1); + + // Life total, poison total, and keywords, attached directly to Player. + Observer o2 = new Observer() { + @Override + public void update(final Observable a, final Object b) { + view.updateDetails(player); + } + }; + player.addObserver(o2); + + // Card play area, attached to battlefield zone. + Observer o3 = new Observer() { + public void update(final Observable a, final Object b) { + PlayerZone pZone = (PlayerZone) a; + Card[] c = pZone.getCards(false); + GuiDisplayUtil.setupPlayZone(view.getTabletop(), c); + } + }; + + player.getZone(Zone.Battlefield).addObserver(o3); + } + + /** + * Listeners for user actions on the battlefield. + * + */ + public void addListeners() { + // When/if zone action properties become less specific, the conditional + // tests for computer/human players can be removed. If that's not ever + // going to happen, this comment can be removed. :) Doublestrike 29-10-11. + + this.addZoneListeners(); + + // Battlefield card clicks + view.getTabletop().addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(final MouseEvent e) { + ViewTopLevel t = (ViewTopLevel) AllZone.getDisplay(); + Card c = t.getCardviewerController().getCurrentCard(); + Input input = t.getInputController().getInputControl().getInput(); + + if (c != null) { + if (c.isTapped() + && (input instanceof InputPayManaCost + || input instanceof InputPayManaCostAbility)) { + arcane.ui.CardPanel cardPanel = view.getTabletop().getCardPanel(c.getUniqueNumber()); + for (arcane.ui.CardPanel cp : cardPanel.getAttachedPanels()) { + if (cp.getCard().isUntapped()) { + break; + } + } + } + + CardList att = new CardList(AllZone.getCombat().getAttackers()); + if ((c.isTapped() || c.hasSickness() || ((c.hasKeyword("Vigilance")) && att.contains(c))) + && (input instanceof InputAttack)) { + arcane.ui.CardPanel cardPanel = view.getTabletop().getCardPanel(c.getUniqueNumber()); + for (arcane.ui.CardPanel cp : cardPanel.getAttachedPanels()) { + if (cp.getCard().isUntapped() && !cp.getCard().hasSickness()) { + break; + } + } + } + + if (e.isMetaDown()) { + if (att.contains(c) && (input instanceof InputAttack) + && !c.hasKeyword("CARDNAME attacks each turn if able.")) { + c.untap(); + AllZone.getCombat().removeFromCombat(c); + } else if (input instanceof InputBlock) { + if (c.getController().isHuman()) { + AllZone.getCombat().removeFromCombat(c); + } + ((InputBlock) input).removeFromAllBlocking(c); + } + } + else { + t.getInputController().getInputControl().selectCard(c, AllZone.getHumanPlayer().getZone(Zone.Battlefield)); + } + } + } + }); + + // Battlefield card mouseover + view.getTabletop().addMouseMotionListener(new MouseMotionAdapter() { + @Override + public void mouseMoved(final MouseEvent me) { + ViewTopLevel t = (ViewTopLevel) AllZone.getDisplay(); + Card c = view.getTabletop().getCardFromMouseOverPanel(); + if (c != null) { + t.getCardviewerController().showCard(c); + } + } + }); + + // Player select + view.getLblLife().addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(final MouseEvent e) { + ViewTopLevel t = (ViewTopLevel) AllZone.getDisplay(); + if (player.isComputer()) { + t.getInputController().getInputControl().selectPlayer(AllZone.getComputerPlayer()); + } + else { + t.getInputController().getInputControl().selectPlayer(AllZone.getHumanPlayer()); + } + } + }); + } // End addListeners() + + /** Adds listeners to "zone" labels: flashback, graveyard, etc. + * This method only exists to avoid the 150-line limit in the checkstyle rules. + */ + private void addZoneListeners() { + // Graveyard card list button + view.getLblGraveyard().enableHover(); + view.getLblGraveyard().addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + if (player.isComputer()) { + new ZoneAction(player.getZone(Zone.Graveyard), + NewConstants.Lang.GuiDisplay.COMPUTER_GRAVEYARD).actionPerformed(null); + } + else { + new ZoneAction(player.getZone(Zone.Graveyard), + NewConstants.Lang.GuiDisplay.HUMAN_GRAVEYARD).actionPerformed(null); + } + } + }); + // Exile card list button + view.getLblExile().enableHover(); + view.getLblExile().addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + if (player.isComputer()) { + new ZoneAction(player.getZone(Zone.Exile), + NewConstants.Lang.GuiDisplay.COMPUTER_REMOVED).actionPerformed(null); + } + else { + new ZoneAction(player.getZone(Zone.Exile), + NewConstants.Lang.GuiDisplay.HUMAN_REMOVED).actionPerformed(null); + } + } + }); + // Library card list button + view.getLblLibrary().enableHover(); + view.getLblLibrary().addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + if (!player.isComputer()) { + new DeckListAction(NewConstants.Lang.GuiDisplay.HUMAN_DECKLIST).actionPerformed(null); + } + else { + // TODO DeckListAction has been rewritten to accept either human or computer + // decklists. However, NewConstants.Lang.GuiDisplay does not have a computer + // decklist available. That needs to be added for the below line to work + // properly. The current solution will work in the meantime. Doublestrike 15-11-11. + + //new DeckListAction(NewConstants.Lang.GuiDisplay).actionPerformed(null); + + new ZoneAction(player.getZone(Zone.Library), ComputerLibrary.BASE).actionPerformed(null); + } + } + }); + // Flashback card list button + view.getLblFlashback().enableHover(); + view.getLblFlashback().addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + if (!player.isComputer()) { + new ZoneAction(player.getZone(Zone.Graveyard), NewConstants.Lang.GuiDisplay.HUMAN_FLASHBACK) { + + private static final long serialVersionUID = 8120331222693706164L; + + @Override + protected Iterable getCardsAsIterable() { + return new ImmutableIterableFrom(CardFactoryUtil.getExternalZoneActivationCards(AllZone + .getHumanPlayer())); + } + + @Override + protected void doAction(final Card c) { + AllZone.getGameAction().playCard(c); + } + }; + } + } + }); + // Hand button + if (player.isComputer()) { + view.getLblHand().enableHover(); + + view.getLblHand().addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + new ZoneAction(player.getZone(Zone.Hand), ComputerHand.BASE).actionPerformed(null); + } + }); + } + } + + /** + * Resets all phase buttons to "inactive", so highlight won't be + * drawn on them. "Enabled" state remains the same. + */ + public void resetPhaseButtons() { + view.getLblUpkeep().setActive(false); + view.getLblDraw().setActive(false); + view.getLblBeginCombat().setActive(false); + view.getLblEndCombat().setActive(false); + view.getLblEndTurn().setActive(false); + } + + /** + * Receives click and programmatic requests for viewing data stacks + * in the "zones" of a player field: hand, library, etc. + * + */ + private class ZoneAction extends ForgeAction { + private static final long serialVersionUID = -5822976087772388839L; + private PlayerZone zone; + private String title; + + /** + * Receives click and programmatic requests for viewing data stacks + * in the "zones" of a player field: hand, graveyard, etc. The library + * "zone" is an exception to the rule; it's handled in DeckListAction. + * + * @param zone   PlayerZone obj + * @param property   String obj + */ + public ZoneAction(final PlayerZone zone, final String property) { + super(property); + title = ForgeProps.getLocalized(property + "/title"); + this.zone = zone; + } + + /** @param e   ActionEvent obj */ + public void actionPerformed(final ActionEvent e) { + Generator c = YieldUtils.toGenerator(getCardsAsIterable()); + + if (AllZone.getNameChanger().shouldChangeCardName()) { + c = AllZone.getNameChanger().changeCard(c); + } + + Iterable myIterable = YieldUtils.toIterable(c); + ArrayList choices = YieldUtils.toArrayList(myIterable); + //System.out.println("immediately after: "+choices); + //Iterator iter = myIterable.iterator(); + + ArrayList choices2 = new ArrayList(); + + if (choices.isEmpty()) { + GuiUtils.getChoiceOptional(title, new String[]{"no cards"}); + } + else { + for (int i = 0; i < choices.size(); i++) { + Card crd = choices.get(i); + //System.out.println(crd+": "+crd.isFaceDown()); + if (crd.isFaceDown()) { + Card faceDown = new Card(); + faceDown.setName("Face Down"); + choices2.add(faceDown); + //System.out.println("Added: "+faceDown); + } + else { + choices2.add(crd); + } + } + //System.out.println("Face down cards replaced: "+choices2); + Card choice = (Card) GuiUtils.getChoiceOptional(title, choices2.toArray()); + if (choice != null) { + doAction(choice); + /* + Card choice = GuiUtils.getChoiceOptional(title, iter); + if (choice != null) doAction(choice); + */ + } + } + } + + protected Iterable getCardsAsIterable() { + return new ImmutableIterableFrom(Arrays.asList(zone.getCards())); + } + + protected void doAction(final Card c) { + } + } // End ZoneAction + + /** + * Receives click and programmatic requests for viewing a player's + * library (typically used in dev mode). Allows copy of the + * cardlist to clipboard. + * + */ + private class DeckListAction extends ForgeAction { + public DeckListAction(final String property) { + super(property); + } + + private static final long serialVersionUID = 9874492387239847L; + + public void actionPerformed(final ActionEvent e) { + Deck targetDeck; + + if (Constant.Runtime.HUMAN_DECK[0].countMain() > 1) { + targetDeck = Constant.Runtime.HUMAN_DECK[0]; + } + else if (Constant.Runtime.COMPUTER_DECK[0].countMain() > 1) { + targetDeck = Constant.Runtime.COMPUTER_DECK[0]; + } + else { + return; + } + + HashMap deckMap = new HashMap(); + + for (Entry s : targetDeck.getMain()) { + deckMap.put(s.getKey().getName(), s.getValue()); + } + + String nl = System.getProperty("line.separator"); + StringBuilder deckList = new StringBuilder(); + String dName = targetDeck.getName(); + + if (dName == null) { + dName = ""; + } else { + deckList.append(dName + nl); + } + + ArrayList dmKeys = new ArrayList(); + for (String s : deckMap.keySet()) { + dmKeys.add(s); + } + + Collections.sort(dmKeys); + + for (String s : dmKeys) { + deckList.append(deckMap.get(s) + " x " + s + nl); + } + + int rcMsg = -1138; + String ttl = "Human's Decklist"; + if (!dName.equals("")) { + ttl += " - " + dName; + } + + StringBuilder msg = new StringBuilder(); + if (deckMap.keySet().size() <= 32) { + msg.append(deckList.toString() + nl); + } else { + msg.append("Decklist too long for dialog." + nl + nl); + } + + msg.append("Copy Decklist to Clipboard?"); + + rcMsg = JOptionPane.showConfirmDialog(null, msg, ttl, JOptionPane.OK_CANCEL_OPTION); + + if (rcMsg == JOptionPane.OK_OPTION) { + StringSelection ss = new StringSelection(deckList.toString()); + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, null); + } + } + } // End DeckListAction +} diff --git a/src/main/java/forge/control/match/ControlHand.java b/src/main/java/forge/control/match/ControlHand.java new file mode 100644 index 00000000000..20407b874d7 --- /dev/null +++ b/src/main/java/forge/control/match/ControlHand.java @@ -0,0 +1,128 @@ +package forge.control.match; + +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Observable; +import java.util.Observer; + +import forge.AllZone; +import forge.Card; +import forge.Constant.Zone; +import forge.PlayerZone; +import forge.view.match.ViewHand; +import forge.view.match.ViewHand.CardPanel; +import forge.view.match.ViewTopLevel; + +/** + * Child controller - handles operations related to cards in user's hand + * and their Swing components, which are assembled in ViewHand. + * + */ +public class ControlHand { + private List cardsInPanel; + private ViewHand view; + + /** + * Child controller - handles operations related to cards in user's hand + * and their Swing components, which are assembled in ViewHand. + * @param v   The Swing component for user hand + */ + public ControlHand(ViewHand v) { + view = v; + cardsInPanel = new ArrayList(); + } + + /** Adds observers to hand panel. */ + public void addObservers() { + Observer o1 = new Observer() { + public void update(final Observable a, final Object b) { + resetCards(Arrays.asList(((PlayerZone) a).getCards())); + } + }; + AllZone.getHumanPlayer().getZone(Zone.Hand).addObserver(o1); + } + + /** Adds listeners to hand panel: window resize, etc. */ + public void addListeners() { + view.addComponentListener(new ComponentAdapter() { + public void componentResized(ComponentEvent e) { + // Ensures cards in hand scale properly with parent. + view.refreshLayout(); + } + }); + } + + /** + * Adds various listeners for cards in hand. Uses CardPanel + * instance from ViewHand. + * + * @param c   CardPanel object + */ + public void addCardPanelListeners(final CardPanel c) { + // Grab top level controller to facilitate interaction between children + final ViewTopLevel display = (ViewTopLevel) (AllZone.getDisplay()); + final Card cardobj = c.getCard(); + + // Sidebar pic/detail on card hover + c.addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(final MouseEvent e) { + display.getCardviewerController().showCard(cardobj); + } + }); + + // Mouse press + c.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(final MouseEvent e) { + + if (e.getButton() != MouseEvent.BUTTON1) { + return; + } + + display.getInputController().getInputControl().selectCard( + cardobj, AllZone.getHumanPlayer().getZone(Zone.Hand)); + } + }); + } + + /** @param c   Card object */ + public void addCard(Card c) { + cardsInPanel.add(c); + view.refreshLayout(); + } + + /** @param c   List of Card objects */ + public void addCards(List c) { + cardsInPanel.addAll(c); + view.refreshLayout(); + } + + /** @return List */ + public List getCards() { + return cardsInPanel; + } + + /** @param c   Card object */ + public void removeCard(Card c) { + cardsInPanel.remove(c); + view.refreshLayout(); + } + + /** @param c   List of Card objects */ + public void removeCards(List c) { + cardsInPanel.removeAll(c); + view.refreshLayout(); + } + + /** @param c   List of Card objects */ + public void resetCards(List c) { + cardsInPanel.clear(); + addCards(c); + } +} diff --git a/src/main/java/forge/control/match/ControlInput.java b/src/main/java/forge/control/match/ControlInput.java new file mode 100644 index 00000000000..7e288ba7584 --- /dev/null +++ b/src/main/java/forge/control/match/ControlInput.java @@ -0,0 +1,88 @@ +package forge.control.match; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; + +import forge.AllZone; +import forge.GuiInput; +import forge.view.match.ViewInput; + +/** + * Child controller - handles operations related to input panel. + * + */ +public class ControlInput { + private ViewInput view; + + private GuiInput inputControl; + + /** + * Child controller - handles operations related to input panel. + * @param v   The Swing component for the input area + */ + public ControlInput(ViewInput v) { + view = v; + inputControl = new GuiInput(); + } + + /** Adds listeners to input area. */ + public void addListeners() { + view.getBtnCancel().addActionListener(new ActionListener() { + public void actionPerformed(final ActionEvent evt) { + btnCancelActionPerformed(evt); + view.getBtnOK().requestFocusInWindow(); + } + }); + // + view.getBtnOK().addActionListener(new ActionListener() { + public void actionPerformed(final ActionEvent evt) { + btnOKActionPerformed(evt); + + if (AllZone.getPhase().isNeedToNextPhase()) { + // moves to next turn + AllZone.getPhase().setNeedToNextPhase(false); + AllZone.getPhase().nextPhase(); + } + view.getBtnOK().requestFocusInWindow(); + } + }); + // + view.getBtnOK().addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(final KeyEvent arg0) { + // TODO make triggers on escape + int code = arg0.getKeyCode(); + if (code == KeyEvent.VK_ESCAPE) { + view.getBtnOK().doClick(); + } + } + }); + } + + /** + *

btnCancelActionPerformed.

+ * Triggers current cancel action from whichever input controller is being used. + * + * @param evt a {@link java.awt.event.ActionEvent} object. + */ + private void btnCancelActionPerformed(final ActionEvent evt) { + inputControl.selectButtonCancel(); + } + + /** + *

btnOKActionPerformed.

+ * Triggers current OK action from whichever input controller is being used. + * + * @param evt a {@link java.awt.event.ActionEvent} object. + */ + private void btnOKActionPerformed(final ActionEvent evt) { + inputControl.selectButtonOK(); + } + + /** @return GuiInput */ + public GuiInput getInputControl() { + return inputControl; + } +} diff --git a/src/main/java/forge/control/match/ControlTabber.java b/src/main/java/forge/control/match/ControlTabber.java new file mode 100644 index 00000000000..5246a94aec9 --- /dev/null +++ b/src/main/java/forge/control/match/ControlTabber.java @@ -0,0 +1,198 @@ +package forge.control.match; + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Observable; +import java.util.Observer; + +import forge.AllZone; +import forge.GuiDisplayUtil; +import forge.MyObservable; +import forge.Singletons; +import forge.view.match.ViewTabber; + +/** + * Controls the vertical tabber in sidebar used for + * viewing gameplay data: stack, combat, etc. + * + */ +public class ControlTabber extends MyObservable { + private ViewTabber view; + + /** + * Controls the vertical tabber in sidebar used for + * viewing gameplay data: stack, combat, etc. + * + * @param v   The tabber Swing component + */ + public ControlTabber(ViewTabber v) { + view = v; + if (Singletons.getModel().getPreferences().isMillingLossCondition()) { + view.getLblMilling().setEnabled(true); + } + else { + view.getLblMilling().setEnabled(false); + } + + if (Singletons.getModel().getPreferences().getHandView()) { + view.getLblHandView().setEnabled(true); + } + else { + view.getLblHandView().setEnabled(false); + } + + if (Singletons.getModel().getPreferences().getLibraryView()) { + view.getLblLibraryView().setEnabled(true); + } + else { + view.getLblLibraryView().setEnabled(false); + } + } + + /** Adds observers to tabber. */ + public void addObservers() { + // Stack + Observer o1 = new Observer() { + public void update(final Observable a, final Object b) { + view.updateStack(); + } + }; + + AllZone.getStack().addObserver(o1); + } + + /** Adds listeners to various components in tabber. */ + public void addListeners() { + // Milling enable toggle + view.getLblMilling().addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + view.getLblMilling().toggleEnabled(); + } + }); + + // View any hand toggle + view.getLblHandView().addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + view.getLblHandView().toggleEnabled(); + } + }); + + // DevMode: View any library toggle + view.getLblLibraryView().addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + view.getLblLibraryView().toggleEnabled(); + } + }); + + // DevMode: Play unlimited land this turn toggle + view.getLblUnlimitedLands().addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + GuiDisplayUtil.devModeUnlimitedLand(); + + // TODO: Enable toggle for this (e.g. Unlimited land each turn: enabled) + // Also must change enabled/disabled text in ViewTabber to reflect this. + //view.getLblUnlimitedLands().toggleEnabled(); + } + }); + + // DevMode: Generate mana + view.getLblGenerateMana().addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + GuiDisplayUtil.devModeGenerateMana(); + } + }); + + // DevMode: Battlefield setup + view.getLblSetupGame().addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + GuiDisplayUtil.devSetupGameState(); + } + }); + + // DevMode: Tutor for card + view.getLblTutor().addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + GuiDisplayUtil.devModeTutor(); + } + }); + + // DevMode: Add counter to permanent + view.getLblCounterPermanent().addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + GuiDisplayUtil.devModeAddCounter(); + } + }); + + // DevMode: Tap permanent + view.getLblTapPermanent().addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + GuiDisplayUtil.devModeTapPerm(); + } + }); + + // DevMode: Untap permanent + view.getLblUntapPermanent().addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + GuiDisplayUtil.devModeUntapPerm(); + } + }); + + // DevMode: Set human life + view.getLblHumanLife().addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + GuiDisplayUtil.devModeSetLife(); + } + }); + } + + /** @return ViewTabber */ + public ViewTabber getView() { + return view; + } + + /** + * Programatically forces card layout of sidebar tabber to show "Dev" panel. + */ + public void showPnlDev() { + view.getVtpTabber().showTab(4); + } + + /** + * Programatically forces card layout of sidebar tabber to show "Players" panel. + */ + public void showPnlPlayers() { + view.getVtpTabber().showTab(3); + } + + /** + * Programatically forces card layout of sidebar tabber to show "Console" panel. + */ + public void showPnlConsole() { + view.getVtpTabber().showTab(2); + } + + /** + * Programatically forces card layout of sidebar tabber to show "Combat" panel. + */ + public void showPnlCombat() { + view.getVtpTabber().showTab(1); + } + + /** + * Programatically forces card layout of sidebar tabber to show "Stack" panel. + */ + public void showPnlStack() { + view.getVtpTabber().showTab(0); + } +} diff --git a/src/main/java/forge/control/match/package-info.java b/src/main/java/forge/control/match/package-info.java new file mode 100644 index 00000000000..ba6acc3b070 --- /dev/null +++ b/src/main/java/forge/control/match/package-info.java @@ -0,0 +1,3 @@ +/** Child controllers used in match UI. */ +package forge.control.match; + diff --git a/src/main/java/forge/control/package-info.java b/src/main/java/forge/control/package-info.java new file mode 100644 index 00000000000..8276b98567c --- /dev/null +++ b/src/main/java/forge/control/package-info.java @@ -0,0 +1,3 @@ +/** Controller (as in model-view-controller) for Forge. */ +package forge.control; + diff --git a/src/main/java/forge/gui/skin/FButton.java b/src/main/java/forge/gui/skin/FButton.java index 7287cf1014d..00c04bf4340 100644 --- a/src/main/java/forge/gui/skin/FButton.java +++ b/src/main/java/forge/gui/skin/FButton.java @@ -31,7 +31,14 @@ public class FButton extends JButton { private final AlphaComposite disabledComposite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.25f); /** - * Instantiates a new f button. + * Instantiates a new FButton. + */ + public FButton() { + this(""); + } + + /** + * Instantiates a new FButton. * * @param msg * the msg diff --git a/src/main/java/forge/gui/skin/FPanel.java b/src/main/java/forge/gui/skin/FPanel.java index 076270fdfea..9fa4f7d4a0d 100644 --- a/src/main/java/forge/gui/skin/FPanel.java +++ b/src/main/java/forge/gui/skin/FPanel.java @@ -2,6 +2,7 @@ package forge.gui.skin; import java.awt.Dimension; import java.awt.Graphics; +import java.awt.Image; import java.awt.LayoutManager; import javax.swing.ImageIcon; @@ -11,14 +12,15 @@ import javax.swing.JPanel; *

* FPanel. *

- * The core JPanel used throughout the Forge project. Allows tiled images and - * ... + * The core JPanel used throughout the Forge project. Allows tiled texture images + * and single background images, using setBGTexture() and setBGImage() respectively. * */ @SuppressWarnings("serial") public class FPanel extends JPanel { - private ImageIcon bgImg = null; - private int w, h, iw, ih, x, y = 0; + private Image bgTexture, bgImg = null; + // Panel Width, panel Height, Image Width, Image Height, Image Aspect Ratio + private double w, h, iw, ih, iar, x, y = 0; /** * Instantiates a new f panel. @@ -45,40 +47,84 @@ public class FPanel extends JPanel { */ @Override protected void paintComponent(final Graphics g) { - // System.out.print("\nRepainting. "); - if (this.bgImg != null) { - this.w = this.getWidth(); - this.h = this.getHeight(); - this.iw = this.bgImg.getIconWidth(); - this.ih = this.bgImg.getIconHeight(); + w = this.getWidth(); + h = this.getHeight(); - while (this.x < this.w) { - while (this.y < this.h) { - g.drawImage(this.bgImg.getImage(), this.x, this.y, null); - this.y += this.ih; + // Draw background texture + if (bgTexture != null) { + iw = bgTexture.getWidth(null); + ih = bgTexture.getHeight(null); + x = 0; + y = 0; + + while (x < w) { + while (y < h) { + g.drawImage(bgTexture, (int) x, (int) y, null); + y += ih; } - this.x += this.iw; - this.y = 0; + x += iw; + y = 0; + } + x = 0; + } + + // Draw background image + if (bgImg != null) { + iw = bgImg.getWidth(null); // Image Width + ih = bgImg.getHeight(null); // Image Height + iar = iw / ih; // Image Aspect Ratio + + // Image is smaller than panel: + if (w > iw && h > ih) { + g.drawImage(bgImg, (int) (w - iw) / 2, (int) (h - ih) / 2, (int) iw, (int) ih, null); + } + // Image is larger than panel, and tall: + else if (iar < 1) { + g.drawImage(bgImg, + (int) ((w - h * iar) / 2), 0, + (int) ((w + h * iar) / 2), (int) h, + 0, 0, (int) iw, (int) ih, null); + } + // Image is larger than panel, and wide: + else if (iar > 1) { + g.drawImage(bgImg, + 0, (int) ((h - w / iar) / 2), + (int) w, (int) ((h + w / iar) / 2), + 0, 0, (int) iw, (int) ih, null); } - this.x = 0; } super.paintComponent(g); } /** - * Sets the bG img. - * - * @param icon - * the new bG img + * An FPanel can have a tiled texture and an image. The texture will be drawn + * first. If a background image has been set, it will be drawn on top of the + * texture, centered and scaled proportional to its aspect ratio. + * + * @param i0   ImageIcon */ - public void setBGImg(final ImageIcon icon) { - this.bgImg = icon; + public void setBGImg(final ImageIcon i0) { + this.bgImg = i0.getImage(); if (this.bgImg != null) { this.setOpaque(false); } } + /** + * An FPanel can have a tiled texture and an image. The texture will be drawn + * first. If a background image has been set, it will be drawn on top of the + * texture, centered and scaled proportional to its aspect ratio. + * + * @param i0   ImageIcon + */ + public void setBGTexture(final ImageIcon i0) { + this.bgTexture = i0.getImage(); + if (this.bgTexture != null) { + this.setOpaque(false); + } + } + /** * Sets the preferred size. * diff --git a/src/main/java/forge/gui/skin/FRoundedPanel.java b/src/main/java/forge/gui/skin/FRoundedPanel.java index 0a2d3fe9ebf..29c1a85d61c 100644 --- a/src/main/java/forge/gui/skin/FRoundedPanel.java +++ b/src/main/java/forge/gui/skin/FRoundedPanel.java @@ -22,6 +22,7 @@ import forge.AllZone; */ @SuppressWarnings("serial") public class FRoundedPanel extends JPanel { + private boolean[] borders = { true, true, true, true }; private boolean[] corners = { true, true, true, true }; // NW, SW, SE, NE private Color shadowColor = new Color(150, 150, 150, 150); private Color borderColor = AllZone.getSkin().getClrBorders(); @@ -146,39 +147,56 @@ public class FRoundedPanel extends JPanel { // Mid, left, right rectangles: border g2d.setColor(this.borderColor); - g2d.drawLine(r, 0, w - r - so, 0); - g2d.drawLine(r, h - so - 1, w - r - so, h - so - 1); - g2d.drawLine(0, r, 0, h - r - so); - g2d.drawLine(w - so - 1, r, w - so - 1, h - r - so); + + if (this.borders[0]) { g2d.drawLine(r, 0, w - r - so, 0); } + if (this.borders[1]) { g2d.drawLine(0, r, 0, h - r - so); } + if (this.borders[2]) { g2d.drawLine(r, h - so - 1, w - r - so, h - so - 1); } + if (this.borders[3]) { g2d.drawLine(w - so - 1, r, w - so - 1, h - r - so); } // Corners: border // NW if (this.corners[0]) { g2d.drawArc(0, 0, 2 * r, 2 * r, 90, 90); } else { - g2d.drawLine(0, 0, r, 0); - g2d.drawLine(0, 0, 0, r); + if (this.borders[0]) { + g2d.drawLine(0, 0, r, 0); + } + if (this.borders[1]) { + g2d.drawLine(0, 0, 0, r); + } } // SW if (this.corners[1]) { g2d.drawArc(0, h - (2 * r) - so, 2 * r, (2 * r) - 1, 180, 90); } else { - g2d.drawLine(0, h - so - 1, 0, h - r - so - 1); - g2d.drawLine(0, h - so - 1, r, h - so - 1); + if (this.borders[1]) { + g2d.drawLine(0, h - so - 1, 0, h - r - so - 1); + } + if (this.borders[2]) { + g2d.drawLine(0, h - so - 1, r, h - so - 1); + } } // SE if (this.corners[2]) { g2d.drawArc(w - (2 * r) - so, h - (2 * r) - so, (2 * r) - 1, (2 * r) - 1, 270, 90); } else { - g2d.drawLine(w - so - 1, h - so - 1, w - so - 1, h - r - so); - g2d.drawLine(w - so - 1, h - so - 1, w - r - so, h - so - 1); + if (this.borders[2]) { + g2d.drawLine(w - so - 1, h - so - 1, w - r - so, h - so - 1); + } + if (this.borders[3]) { + g2d.drawLine(w - so - 1, h - so - 1, w - so - 1, h - r - so); + } } // NE if (this.corners[3]) { g2d.drawArc(w - (2 * r) - so, 0, (2 * r) - 1, (2 * r) - 1, 0, 90); } else { - g2d.drawLine(w - so - 1, 0, w - so - r, 0); - g2d.drawLine(w - so - 1, 0, w - so - 1, r); + if (this.borders[0]) { + g2d.drawLine(w - so - 1, 0, w - so - r, 0); + } + if (this.borders[3]) { + g2d.drawLine(w - so - 1, 0, w - so - 1, r); + } } } @@ -230,15 +248,14 @@ public class FRoundedPanel extends JPanel { *

* Sets radius of each corner on rounded panel. * - * @param r - * the new corner radius + * @param r0   int */ - public void setCornerRadius(int r) { - if (r < 0) { - r = 0; + public void setCornerRadius(int r0) { + if (r0 < 0) { + r0 = 0; } - this.cornerRadius = r; + this.cornerRadius = r0; } /** @@ -248,8 +265,7 @@ public class FRoundedPanel extends JPanel { * Sets if corners should be rounded or not in the following order: NW, SW, * SE, NE * - * @param vals - * the new corners + * @param vals   boolean[4] */ public void setCorners(final boolean[] vals) { if (vals.length != 4) { @@ -259,6 +275,23 @@ public class FRoundedPanel extends JPanel { this.corners = vals; } + /** + *

+ * setBorders. + *

+ * Sets if borders should be displayed or not following order: N, W, S, E. + * Only works for square corners, rounded corners will be shown with borders. + * + * @param vals   boolean[4] + */ + public void setBorders(final boolean[] vals) { + if (vals.length != 4) { + throw new IllegalArgumentException("FRoundedPanel > setBorders requires an array of booleans of length 4."); + } + + this.borders = vals; + } + /** * Sets the show shadow. * diff --git a/src/main/java/forge/gui/skin/FSkin.java b/src/main/java/forge/gui/skin/FSkin.java index f28c74b7a28..530adfd9cf4 100644 --- a/src/main/java/forge/gui/skin/FSkin.java +++ b/src/main/java/forge/gui/skin/FSkin.java @@ -28,6 +28,9 @@ public class FSkin { /** Primary texture used in skin. */ private ImageIcon texture1 = null; + /** Primary background image used during match play. */ + private ImageIcon matchBG = null; + /** Left side of button, up state. */ private ImageIcon btnLup = null; @@ -58,6 +61,17 @@ public class FSkin { /** Splash screen image. */ private ImageIcon splash = null; + /** Splash screen image. */ + private ImageIcon icoEnabled = null; + private ImageIcon icoDisabled = null; + private ImageIcon icoTap = null; + private ImageIcon icoUntap = null; + private ImageIcon icoPlus = null; + private ImageIcon icoShortcuts = null; + private ImageIcon icoSettings = null; + private ImageIcon icoConcede = null; + private ImageIcon icoEndTurn = null; + /** Base color used in skin. */ private Color clrTheme = Color.red; @@ -95,7 +109,7 @@ public class FSkin { private String name = "default"; // ===== Private fields - private final String paletteFile = "palette.png"; + private final String spriteFile = "sprite.png"; private final String font1file = "font1.ttf"; private final String font2file = "font2.ttf"; private final String texture1file = "texture1.jpg"; @@ -109,11 +123,11 @@ public class FSkin { private final String btnMdownfile = "btnMdown.png"; private final String btnRdownfile = "btnRdown.png"; private final String splashfile = "bg_splash.jpg"; + private final String matchfile = "bg_match.jpg"; private ImageIcon tempImg; private Font tempFont; - private String skin; - private final String notfound = "FSkin.java: \"" + this.skin + "\" skin can't find "; + private final String notfound = "FSkin.java: Can't find "; /** * FSkin constructor. No arguments, will generate default skin settings, @@ -153,6 +167,7 @@ public class FSkin { // Images this.setTexture1(this.retrieveImage(dirName + this.texture1file)); + this.setMatchBG(this.retrieveImage(dirName + this.matchfile)); this.setBtnLup(this.retrieveImage(dirName + this.btnLupfile)); this.setBtnMup(this.retrieveImage(dirName + this.btnMupfile)); this.setBtnRup(this.retrieveImage(dirName + this.btnRupfile)); @@ -162,26 +177,35 @@ public class FSkin { this.setBtnLdown(this.retrieveImage(dirName + this.btnLdownfile)); this.setBtnMdown(this.retrieveImage(dirName + this.btnMdownfile)); this.setBtnRdown(this.retrieveImage(dirName + this.btnRdownfile)); - this.setSplash(this.retrieveImage(dirName + this.splashfile)); + this.setSplashBG(this.retrieveImage(dirName + this.splashfile)); // Color palette - final File file = new File(dirName + this.paletteFile); + final File file = new File(dirName + this.spriteFile); BufferedImage image; try { image = ImageIO.read(file); - this.setClrTheme(this.getColorFromPixel(image.getRGB(60, 10))); - this.setClrBorders(this.getColorFromPixel(image.getRGB(60, 30))); - this.setClrZebra(this.getColorFromPixel(image.getRGB(60, 50))); - this.setClrHover(this.getColorFromPixel(image.getRGB(60, 70))); - this.setClrActive(this.getColorFromPixel(image.getRGB(60, 90))); - this.setClrInactive(this.getColorFromPixel(image.getRGB(60, 110))); - this.setClrText(this.getColorFromPixel(image.getRGB(60, 130))); - this.setClrProgress1(this.getColorFromPixel(image.getRGB(55, 145))); - this.setClrProgress2(this.getColorFromPixel(image.getRGB(65, 145))); - this.setClrProgress3(this.getColorFromPixel(image.getRGB(55, 155))); - this.setClrProgress4(this.getColorFromPixel(image.getRGB(65, 155))); + this.setClrTheme(this.getColorFromPixel(image.getRGB(70, 10))); + this.setClrBorders(this.getColorFromPixel(image.getRGB(70, 30))); + this.setClrZebra(this.getColorFromPixel(image.getRGB(70, 50))); + this.setClrHover(this.getColorFromPixel(image.getRGB(70, 70))); + this.setClrActive(this.getColorFromPixel(image.getRGB(70, 90))); + this.setClrInactive(this.getColorFromPixel(image.getRGB(70, 110))); + this.setClrText(this.getColorFromPixel(image.getRGB(70, 130))); + this.setClrProgress1(this.getColorFromPixel(image.getRGB(65, 145))); + this.setClrProgress2(this.getColorFromPixel(image.getRGB(75, 145))); + this.setClrProgress3(this.getColorFromPixel(image.getRGB(65, 155))); + this.setClrProgress4(this.getColorFromPixel(image.getRGB(75, 155))); + this.setIconEnabled(image.getSubimage(80, 0, 40, 40)); + this.setIconDisabled(image.getSubimage(120, 0, 40, 40)); + this.setIconTap(image.getSubimage(80, 40, 40, 40)); + this.setIconUntap(image.getSubimage(120, 40, 40, 40)); + this.setIconPlus(image.getSubimage(80, 80, 40, 40)); + this.setIconShortcuts(image.getSubimage(160, 0, 80, 80)); + this.setIconEndTurn(image.getSubimage(160, 80, 80, 80)); + this.setIconSettings(image.getSubimage(80, 0, 80, 80)); + this.setIconConcede(image.getSubimage(80, 80, 80, 80)); } catch (final IOException e) { - System.err.println(this.notfound + this.paletteFile); + System.err.println(this.notfound + this.spriteFile); } } @@ -273,7 +297,7 @@ public class FSkin { * Splash screen image. * @return {@link javax.swing.ImageIcon} splash */ - public ImageIcon getSplash() { + public ImageIcon getSplashBG() { return splash; } @@ -281,7 +305,7 @@ public class FSkin { * Splash screen image. * @param splash0   an image icon */ - public void setSplash(ImageIcon splash0) { + public void setSplashBG(ImageIcon splash0) { this.splash = splash0; } @@ -333,6 +357,22 @@ public class FSkin { this.texture1 = texture10; } + /** + * Primary background image used during match play. + * @return ImageIcon + */ + public ImageIcon getMatchBG() { + return matchBG; + } + + /** + * Primary background image used during match play. + * @param img0   an image icon + */ + public void setMatchBG(ImageIcon img0) { + this.matchBG = img0; + } + /** * Color of zebra striping in grid displays. * @return {@link java.awt.Color} clrZebra @@ -626,4 +666,94 @@ public class FSkin { public String getName() { return name; } + + /** @param bi0   BufferedImage */ + public void setIconEnabled(BufferedImage bi0) { + this.icoEnabled = new ImageIcon(bi0); + } + + /** @return ImageIcon */ + public ImageIcon getIconEnabled() { + return icoEnabled; + } + + /** @param bi0   BufferedImage */ + public void setIconDisabled(BufferedImage bi0) { + this.icoDisabled = new ImageIcon(bi0); + } + + /** @return ImageIcon */ + public ImageIcon getIconDisabled() { + return icoDisabled; + } + + /** @param bi0   BufferedImage */ + public void setIconTap(BufferedImage bi0) { + this.icoTap = new ImageIcon(bi0); + } + + /** @return ImageIcon */ + public ImageIcon getIconTap() { + return icoTap; + } + + /** @param bi0   BufferedImage */ + public void setIconUntap(BufferedImage bi0) { + this.icoUntap = new ImageIcon(bi0); + } + + /** @return ImageIcon */ + public ImageIcon getIconUntap() { + return icoUntap; + } + + /** @param bi0   BufferedImage */ + public void setIconPlus(BufferedImage bi0) { + this.icoPlus = new ImageIcon(bi0); + } + + /** @return ImageIcon */ + public ImageIcon getIconPlus() { + return icoPlus; + } + + /** @param bi0   BufferedImage */ + public void setIconShortcuts(BufferedImage bi0) { + this.icoShortcuts = new ImageIcon(bi0); + } + + /** @return ImageIcon */ + public ImageIcon getIconShortcuts() { + return icoShortcuts; + } + + /** @param bi0   BufferedImage */ + public void setIconSettings(BufferedImage bi0) { + this.icoSettings = new ImageIcon(bi0); + } + + /** @return ImageIcon */ + public ImageIcon getIconSettings() { + return icoSettings; + } + + /** @param bi0   BufferedImage */ + public void setIconConcede(BufferedImage bi0) { + this.icoConcede = new ImageIcon(bi0); + } + + /** @return ImageIcon */ + public ImageIcon getIconConcede() { + return icoConcede; + } + + /** @param bi0   BufferedImage */ + public void setIconEndTurn(BufferedImage bi0) { + this.icoEndTurn = new ImageIcon(bi0); + } + + /** @return ImageIcon */ + public ImageIcon getIconEndTurn() { + return icoEndTurn; + } } diff --git a/src/main/java/forge/model/FGameState.java b/src/main/java/forge/model/FGameState.java index 31014097701..64901aa1f4d 100644 --- a/src/main/java/forge/model/FGameState.java +++ b/src/main/java/forge/model/FGameState.java @@ -172,6 +172,7 @@ public class FGameState { * the phase to set */ protected final void setPhase(final Phase phase0) { + System.out.println("asdf:"+phase0); this.phase = phase0; } diff --git a/src/main/java/forge/properties/ForgePreferences.java b/src/main/java/forge/properties/ForgePreferences.java index b2a86719f24..5fc92012d22 100644 --- a/src/main/java/forge/properties/ForgePreferences.java +++ b/src/main/java/forge/properties/ForgePreferences.java @@ -18,8 +18,8 @@ import java.util.List; */ public class ForgePreferences extends Preferences { - /** The new gui. */ - private final boolean newGui; + /** Old gui checkbox toggle. */ + private boolean oldGui; /** The stack ai land. */ private boolean stackAiLand; @@ -27,7 +27,13 @@ public class ForgePreferences extends Preferences { /** The milling loss condition. */ private boolean millingLossCondition; - /** The developer mode. */ + /** Hand view toggle. */ + private boolean handView; + + /** Library view toggle. */ + private boolean libraryView; + + /** Developer mode. */ private boolean developerMode; /** The upload draft ai. */ @@ -106,6 +112,17 @@ public class ForgePreferences extends Preferences { /** The b human end combat. */ private boolean bHumanEndCombat; + // Keyboard shortcuts + private String showStackShortcut; + private String showCombatShortcut; + private String showConsoleShortcut; + private String showPlayersShortcut; + private String showDevShortcut; + private String concedeShortcut; + private String showPictureShortcut; + private String showDetailShortcut; + + // private final List saveListeners = new ArrayList(); private final String fileName; @@ -135,9 +152,11 @@ public class ForgePreferences extends Preferences { throw new Exception("Error reading \"" + fileName + "\".", ex); } - this.newGui = this.getBoolean("gui.new", true); + this.oldGui = this.getBoolean("gui.old", true); this.setStackAiLand(this.getBoolean("AI.stack.land", false)); this.setMillingLossCondition(this.getBoolean("loss.condition.milling", true)); + this.setHandView(this.getBoolean("developer.handview", true)); + this.setLibraryView(this.getBoolean("developer.libraryview", true)); this.setDeveloperMode(this.getBoolean("developer.mode", false)); this.setUploadDraftAI(this.getBoolean("upload.Draft.AI", true)); @@ -172,6 +191,16 @@ public class ForgePreferences extends Preferences { this.setbHumanEOT(this.getBoolean("phase.human.eot", true)); this.setbHumanBeginCombat(this.getBoolean("phase.human.beginCombat", true)); this.setbHumanEndCombat(this.getBoolean("phase.human.endCombat", true)); + + // Keyboard shortcuts + this.setShowStackShortcut(this.get("shortcut.showstack", "83")); + this.setShowCombatShortcut(this.get("shortcut.showcombat", "67")); + this.setShowConsoleShortcut(this.get("shortcut.showconsole", "76")); + this.setShowPlayersShortcut(this.get("shortcut.showplayers", "80")); + this.setShowDevShortcut(this.get("shortcut.showdev", "68")); + this.setConcedeShortcut(this.get("shortcut.concede", "27")); + this.setShowPictureShortcut(this.get("shortcut.showpicture", "17 80")); + this.setShowDetailShortcut(this.get("shortcut.showdetail", "17 68")); } /** @@ -184,10 +213,12 @@ public class ForgePreferences extends Preferences { */ public final void save() throws Exception { - this.set("gui.new", this.newGui); + this.set("gui.old", this.oldGui); this.set("AI.stack.land", this.isStackAiLand()); this.set("loss.condition.milling", this.isMillingLossCondition()); + this.set("developer.handview", this.getHandView()); + this.set("developer.libraryview", this.getLibraryView()); this.set("developer.mode", this.isDeveloperMode()); this.set("upload.Draft.AI", this.isUploadDraftAI()); @@ -224,6 +255,16 @@ public class ForgePreferences extends Preferences { this.set("phase.human.beginCombat", this.isbHumanBeginCombat()); this.set("phase.human.endCombat", this.isbHumanEndCombat()); + // Keyboard shortcuts + this.set("shortcut.showstack", this.getShowStackShortcut()); + this.set("shortcut.showcombat", this.getShowCombatShortcut()); + this.set("shortcut.showconsole", this.getShowConsoleShortcut()); + this.set("shortcut.showplayers", this.getShowPlayersShortcut()); + this.set("shortcut.showdev", this.getShowDevShortcut()); + this.set("shortcut.concede", this.getConcedeShortcut()); + this.set("shortcut.showpicture", this.getShowPictureShortcut()); + this.set("shortcut.showdetail", this.getShowDetailShortcut()); + try { final FileOutputStream stream = new FileOutputStream(this.fileName); this.store(stream, "Forge"); @@ -248,7 +289,7 @@ public class ForgePreferences extends Preferences { /** * Checks if is stack ai land. * - * @return the stackAiLand + * @return boolean */ public boolean isStackAiLand() { return this.stackAiLand; @@ -257,29 +298,78 @@ public class ForgePreferences extends Preferences { /** * Sets the stack ai land. * - * @param stackAiLand the stackAiLand to set + * @param b0   boolean */ - public void setStackAiLand(final boolean stackAiLand) { - this.stackAiLand = stackAiLand; // TODO: Add 0 to parameter's name. + public void setStackAiLand(final boolean b0) { + this.stackAiLand = b0; } /** - * Checks if is milling loss condition. + * Checks if old gui is to be used. * - * @return the millingLossCondition + * @return boolean + */ + public boolean isOldGui() { + return this.oldGui; + } + + /** + * Sets if old gui is to be used. + * + * @param b0   boolean + */ + public void setOldGui(final boolean b0) { + this.oldGui = b0; + } + + /** + * Checks if loss by milling is enabled. + * + * @return boolean */ public boolean isMillingLossCondition() { return this.millingLossCondition; } /** - * Sets the milling loss condition. + * Sets if loss by milling is enabled. * - * @param millingLossCondition the millingLossCondition to set + * @param millingLossCondition0 the millingLossCondition to set */ - public void setMillingLossCondition(final boolean millingLossCondition) { - this.millingLossCondition = millingLossCondition; // TODO: Add 0 to - // parameter's name. + public void setMillingLossCondition(final boolean millingLossCondition0) { + this.millingLossCondition = millingLossCondition0; + } + + /** + * Determines if "view any hand" option in dev mode is enabled or not. + * @return boolean + */ + public boolean getHandView() { + return this.handView; + } + + /** + * Determines if "view any hand" option in dev mode is enabled or not. + * @param b0   boolean + */ + public void setHandView(final boolean b0) { + this.handView = b0; + } + + /** + * Determines if "view any library" option in dev mode is enabled or not. + * @return boolean + */ + public boolean getLibraryView() { + return this.libraryView; + } + + /** + * Determines if "view any library" option in dev mode is enabled or not. + * @param b0   boolean + */ + public void setLibraryView(final boolean b0) { + this.libraryView = b0; } /** @@ -791,4 +881,85 @@ public class ForgePreferences extends Preferences { /** The large. */ large } + + // Keyboard shortcuts + /** @return String   String of keycodes set for this shortcut, delimited with spaces. */ + public String getShowStackShortcut() { + return this.showStackShortcut; + } + + /** @param keycodes   String of keycodes to set for this shortcut, delimited with spaces. */ + public void setShowStackShortcut(String keycodes) { + this.showStackShortcut = keycodes; + } + + /** @return String   String of keycodes set for this shortcut, delimited with spaces. */ + public String getShowCombatShortcut() { + return this.showCombatShortcut; + } + + /** @param keycodes   String of keycodes to set for this shortcut, delimited with spaces. */ + public void setShowCombatShortcut(String keycodes) { + this.showCombatShortcut = keycodes; + } + + /** @return String   String of keycodes set for this shortcut, delimited with spaces. */ + public String getShowConsoleShortcut() { + return this.showConsoleShortcut; + } + + /** @param keycodes   String of keycodes to set for this shortcut, delimited with spaces. */ + public void setShowConsoleShortcut(String keycodes) { + this.showConsoleShortcut = keycodes; + } + + /** @return String   String of keycodes set for this shortcut, delimited with spaces. */ + public String getShowPlayersShortcut() { + return this.showPlayersShortcut; + } + + /** @param keycodes   String of keycodes to set for this shortcut, delimited with spaces. */ + public void setShowPlayersShortcut(String keycodes) { + this.showPlayersShortcut = keycodes; + } + + /** @return String   String of keycodes set for this shortcut, delimited with spaces. */ + public String getShowDevShortcut() { + return this.showDevShortcut; + } + + /** @param keycodes   String of keycodes to set for this shortcut, delimited with spaces. */ + public void setShowDevShortcut(String keycodes) { + this.showDevShortcut = keycodes; + } + + /** @return String   String of keycodes set for this shortcut, delimited with spaces. */ + public String getConcedeShortcut() { + return this.concedeShortcut; + } + + /** @param keycodes   String of keycodes to set for this shortcut, delimited with spaces. */ + public void setConcedeShortcut(String keycodes) { + this.concedeShortcut = keycodes; + } + + /** @return String   String of keycodes set for this shortcut, delimited with spaces. */ + public String getShowPictureShortcut() { + return this.showPictureShortcut; + } + + /** @param keycodes   String of keycodes to set for this shortcut, delimited with spaces. */ + public void setShowPictureShortcut(String keycodes) { + this.showPictureShortcut = keycodes; + } + + /** @return String   String of keycodes set for this shortcut, delimited with spaces. */ + public String getShowDetailShortcut() { + return this.showDetailShortcut; + } + + /** @param keycodes   String of keycodes to set for this shortcut, delimited with spaces. */ + public void setShowDetailShortcut(String keycodes) { + this.showDetailShortcut = keycodes; + } } diff --git a/src/main/java/forge/properties/NewConstants.java b/src/main/java/forge/properties/NewConstants.java index dece320b55a..37cd7990a6d 100644 --- a/src/main/java/forge/properties/NewConstants.java +++ b/src/main/java/forge/properties/NewConstants.java @@ -760,7 +760,7 @@ public final class NewConstants { public static final String DECK_EDITOR = "%s/NewGame/deckeditor"; /** The NE w_ gui. */ - public static final String NEW_GUI = "%s/NewGame/newgui"; + public static final String OLD_GUI = "%s/NewGame/oldgui"; /** The A i_ land. */ public static final String AI_LAND = "%s/NewGame/ailand"; diff --git a/src/main/java/forge/quest/gui/QuestMainPanel.java b/src/main/java/forge/quest/gui/QuestMainPanel.java index b1f31570ff1..f2b504cf1ec 100644 --- a/src/main/java/forge/quest/gui/QuestMainPanel.java +++ b/src/main/java/forge/quest/gui/QuestMainPanel.java @@ -31,6 +31,7 @@ import forge.Command; import forge.Constant; import forge.GuiDisplay; import forge.ImageCache; +import forge.control.ControlAllUI; import forge.deck.Deck; import forge.gui.GuiUtils; import forge.gui.deckeditor.DeckEditorQuest; @@ -755,13 +756,14 @@ public class QuestMainPanel extends QuestAbstractPanel { // Dev Mode occurs before Display Constant.Runtime.DEV_MODE[0] = this.devModeCheckBox.isSelected(); - // DO NOT CHANGE THIS ORDER, GuiDisplay needs to be created before cards - // are added - // if (newGUICheckbox.isSelected()) { - AllZone.setDisplay(new GuiDisplay()); - // } else { - // AllZone.setDisplay(new GuiDisplay3()); - // } + if (Constant.Runtime.OLDGUI[0]) { + AllZone.setDisplay(new GuiDisplay()); + } + else { + ControlAllUI ui = new ControlAllUI(); + AllZone.setDisplay(ui.getMatchView()); + ui.getMatchController().initMatch(); + } Constant.Runtime.SMOOTH[0] = this.smoothLandCheckBox.isSelected(); diff --git a/src/main/java/forge/view/GuiTopLevel.java b/src/main/java/forge/view/GuiTopLevel.java new file mode 100644 index 00000000000..003d0144acd --- /dev/null +++ b/src/main/java/forge/view/GuiTopLevel.java @@ -0,0 +1,49 @@ +package forge.view; + +import java.awt.Dimension; + +import javax.swing.JFrame; +import javax.swing.JLayeredPane; + +import forge.AllZone; +import forge.view.toolbox.FOverlay; + +/** + * Parent JFrame for Forge UI. + * + */ +@SuppressWarnings("serial") +public class GuiTopLevel extends JFrame { + private JLayeredPane lpnContent; + + /** + * Parent JFrame for Forge UI. + */ + public GuiTopLevel() { + super(); + setMinimumSize(new Dimension(800, 600)); + setDefaultCloseOperation(EXIT_ON_CLOSE); + setLocationRelativeTo(null); + setExtendedState(getExtendedState() | JFrame.MAXIMIZED_BOTH); + + lpnContent = new JLayeredPane(); + lpnContent.setOpaque(true); + setContentPane(lpnContent); + addOverlay(); + + setVisible(true); + } + + /** + * Adds overlay panel to modal layer. Used when removeAll() + * has been called on the JLayeredPane parent. + */ + public void addOverlay() { + final FOverlay pnlOverlay = new FOverlay(); + AllZone.setOverlay(pnlOverlay); + pnlOverlay.setOpaque(false); + pnlOverlay.setVisible(false); + pnlOverlay.setBounds(0, 0, getWidth(), getHeight()); + lpnContent.add(pnlOverlay, JLayeredPane.MODAL_LAYER); + } +} diff --git a/src/main/java/forge/view/home/HomeTopLevel.java b/src/main/java/forge/view/home/HomeTopLevel.java new file mode 100644 index 00000000000..a8157bfec8a --- /dev/null +++ b/src/main/java/forge/view/home/HomeTopLevel.java @@ -0,0 +1,16 @@ +package forge.view.home; + +import forge.gui.skin.FPanel; + +/** + * Lays out battle, sidebar, user areas in locked % vals and repaints + * as necessary. + * + */ + +@SuppressWarnings("serial") +public class HomeTopLevel extends FPanel { + public HomeTopLevel() { + super(); + } +} diff --git a/src/main/java/forge/view/match/ViewAreaBattlefield.java b/src/main/java/forge/view/match/ViewAreaBattlefield.java new file mode 100644 index 00000000000..8244527ecc8 --- /dev/null +++ b/src/main/java/forge/view/match/ViewAreaBattlefield.java @@ -0,0 +1,52 @@ +package forge.view.match; + +import java.util.ArrayList; +import java.util.List; + +import net.miginfocom.swing.MigLayout; +import forge.AllZone; +import forge.gui.skin.FPanel; + +/** + * Battlefield, assembles and contains instances of MatchPlayer. + * SHOULD PROBABLY COLLAPSE INTO TOP LEVEL. + * + */ +@SuppressWarnings("serial") +public class ViewAreaBattlefield extends FPanel { + private List fields; + + /** + * An FPanel that adds instances of ViewField fields + * from player name list. + * + */ + public ViewAreaBattlefield() { + super(); + setOpaque(false); + setLayout(new MigLayout("wrap, insets 1% 0.5% 0 0, gap 1%, nocache")); + + // When future codebase upgrades allow, as many fields as + // necessary can be instantiated here. Doublestrike 29-10-11 + + fields = new ArrayList(); + + ViewField temp; + + temp = new ViewField(AllZone.getComputerPlayer()); + this.add(temp, "h 48.5%!, w 99.5%!"); + fields.add(temp); + + temp = new ViewField(AllZone.getHumanPlayer()); + this.add(temp, "h 48.5%!, w 99.5%!"); + fields.add(temp); + } + + /** + * Returns a list of field components in battlefield. + * @return List + */ + public List getFields() { + return fields; + } +} diff --git a/src/main/java/forge/view/match/ViewAreaSidebar.java b/src/main/java/forge/view/match/ViewAreaSidebar.java new file mode 100644 index 00000000000..dd9ddbbeba0 --- /dev/null +++ b/src/main/java/forge/view/match/ViewAreaSidebar.java @@ -0,0 +1,62 @@ +package forge.view.match; + +import net.miginfocom.swing.MigLayout; +import forge.AllZone; +import forge.gui.skin.FPanel; +import forge.gui.skin.FRoundedPanel; + +/** + * Handles display of child components of sidebar area in match UI. + * SHOULD PROBABLY COLLAPSE INTO TOP LEVEL. + * + */ +@SuppressWarnings("serial") +public class ViewAreaSidebar extends FPanel { + private ViewCardviewer cardviewer; + private ViewTabber tabber; + private FRoundedPanel sidebar; + + /** + * Handles display of all components of sidebar area in match UI. + * + */ + public ViewAreaSidebar() { + super(); + setOpaque(false); + setLayout(new MigLayout("insets 0")); + sidebar = new FRoundedPanel(); + sidebar.setLayout(new MigLayout("wrap, gap 0, insets 0")); + sidebar.setBackground(AllZone.getSkin().getClrTheme()); + sidebar.setCorners(new boolean[] {true, true, false, false}); + + // Add tabber, cardview, and finally sidebar. Unfortunately, + // tabber and cardviewer cannot extend FVerticalTabPanel, since that + // requires child panels to be prepared before it's instantiated. + // Therefore, their vertical tab panels must be accessed indirectly via + // an instance of this class. + cardviewer = new ViewCardviewer(); + tabber = new ViewTabber(); + + sidebar.add(cardviewer.getVtpCardviewer(), "w 97%!, h 40%!, gapleft 1%, gapright 2%"); + sidebar.add(tabber.getVtpTabber(), "w 97%!, h 60%!, gapright 2%"); + add(sidebar, "h 98%!, w 98%!, gapleft 2%, gaptop 1%"); + } + + /** + * Retrieves vertical tab panel used for card picture and detail. + * + * @return ViewCardviewer vertical tab panel + */ + public ViewCardviewer getCardviewer() { + return cardviewer; + } + + /** + * Retrieves vertical tab panel used for data presentation. + * + * @return ViewTabber vertical tab panel + */ + public ViewTabber getTabber() { + return tabber; + } +} diff --git a/src/main/java/forge/view/match/ViewAreaUser.java b/src/main/java/forge/view/match/ViewAreaUser.java new file mode 100644 index 00000000000..87fc14d15e0 --- /dev/null +++ b/src/main/java/forge/view/match/ViewAreaUser.java @@ -0,0 +1,63 @@ +package forge.view.match; + +import javax.swing.JPanel; + +import net.miginfocom.swing.MigLayout; +import forge.gui.skin.FPanel; + +/** + * Parent panel for display of input, hand, and dock. + * SHOULD PROBABLY COLLAPSE INTO TOP LEVEL. + * + */ +@SuppressWarnings("serial") +public class ViewAreaUser extends FPanel { + private ViewDock pnlDock; + private ViewHand pnlHand; + + private JPanel pnlMessage; + private ViewInput pnlInput; + + /** + * Assembles user area of match UI. + */ + public ViewAreaUser() { + super(); + setOpaque(false); + setLayout(new MigLayout("fill, insets 0, gap 0")); + + // Input panel + pnlInput = new ViewInput(); + + // Hand panel + pnlHand = new ViewHand(); + + // Dock panel + pnlDock = new ViewDock(); + + // A.D.D. + add(pnlInput, "h 100%!, west, w 200px!"); + add(pnlHand, "grow, gapleft 5"); + add(pnlDock, "growx, h 50px!, south, gaptop 5, gapleft 5"); + } + + /** @return ViewDock */ + public ViewDock getPnlDock() { + return pnlDock; + } + + /** @return ViewHand */ + public ViewHand getPnlHand() { + return pnlHand; + } + + /** @return JPanel */ + public JPanel getPnlMessage() { + return pnlMessage; + } + + /** @return ViewInput */ + public ViewInput getPnlInput() { + return pnlInput; + } +} diff --git a/src/main/java/forge/view/match/ViewCardviewer.java b/src/main/java/forge/view/match/ViewCardviewer.java new file mode 100644 index 00000000000..fb94b94a0f5 --- /dev/null +++ b/src/main/java/forge/view/match/ViewCardviewer.java @@ -0,0 +1,107 @@ +package forge.view.match; + +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JPanel; + +import forge.Card; +import forge.ImageCache; +import forge.control.match.ControlCardviewer; +import forge.gui.skin.FPanel; +import forge.view.toolbox.CardDetailPanel; +import forge.view.toolbox.FVerticalTabPanel; + +/** + * Vertical tab panel for viewing card picture and/or details. + * + */ +public class ViewCardviewer { + private List panelList; + private ControlCardviewer control; + + private CardPicPanel pnlCardPic; + private CardDetailPanel pnlCardDetail; + private FVerticalTabPanel vtpCardviewer; + + private int w, h; + + /** + * + */ + public ViewCardviewer() { + // Assemble card pic viewer + panelList = new ArrayList(); + + pnlCardPic = new CardPicPanel(); + pnlCardPic.setOpaque(false); + pnlCardPic.setName("Picture"); + pnlCardPic.setToolTipText("Card Picture"); + panelList.add(pnlCardPic); + + pnlCardDetail = new CardDetailPanel(); + pnlCardDetail.setOpaque(false); + pnlCardDetail.setName("Detail"); + pnlCardDetail.setToolTipText("Card Text"); + panelList.add(pnlCardDetail); + + vtpCardviewer = new FVerticalTabPanel(panelList); + + // After all components are in place, instantiate controller. + control = new ControlCardviewer(this); + } + + /** @return ControlCardviewer */ + public ControlCardviewer getController() { + return control; + } + + /** + * Card picture handling in side panel of match. + * + */ + @SuppressWarnings("serial") + public class CardPicPanel extends FPanel { + private Card card = null; + + /** @param c   Card object */ + public void setCard(Card c) { + this.card = c; + repaint(); + } + + /** @return Card */ + public Card getCard() { + return this.card; + } + + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + + if (card != null) { + w = getWidth(); + h = (int) (w / 0.7); + BufferedImage img = ImageCache.getImage(card, w, h); + g.drawImage(img, 0, (int) ((getHeight() - h) / 2), null); + } + } + } + + /** @return CardPicPanel */ + public CardPicPanel getPnlCardPic() { + return pnlCardPic; + } + + /** @return CardDetailPanel */ + public CardDetailPanel getPnlCardDetail() { + return pnlCardDetail; + } + + /** @return FVerticalTabPanel */ + public FVerticalTabPanel getVtpCardviewer() { + return vtpCardviewer; + } +} diff --git a/src/main/java/forge/view/match/ViewDock.java b/src/main/java/forge/view/match/ViewDock.java new file mode 100644 index 00000000000..50ca97a8df2 --- /dev/null +++ b/src/main/java/forge/view/match/ViewDock.java @@ -0,0 +1,388 @@ +package forge.view.match; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Image; +import java.awt.event.ActionEvent; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.border.LineBorder; + +import org.apache.commons.lang3.StringUtils; + +import net.miginfocom.swing.MigLayout; +import forge.AllZone; +import forge.Singletons; +import forge.control.match.ControlDock; +import forge.gui.skin.FButton; +import forge.gui.skin.FPanel; +import forge.gui.skin.FRoundedPanel; +import forge.gui.skin.FSkin; +import forge.properties.ForgePreferences; +import forge.view.toolbox.FOverlay; + +/** + * Swing component for button dock. + * + */ +@SuppressWarnings("serial") +public class ViewDock extends FRoundedPanel { + private FSkin skin; + private ControlDock control; + private Map keyboardShortcutFields; + private Action actClose, actSaveKeyboardShortcuts; + + /** + * Swing component for button dock. + * + */ + public ViewDock() { + super(); + setCorners(new boolean[] {false, false, false, false}); + setBorders(new boolean[] {true, true, false, true}); + setToolTipText("Shortcut Button Dock"); + setBackground(AllZone.getSkin().getClrTheme()); + setLayout(new MigLayout("insets 0, gap 0, ay center, ax center")); + String constraints = "w 30px!, h 30px!, gap 0 10px"; + skin = AllZone.getSkin(); + keyboardShortcutFields = new HashMap(); + + actClose = new AbstractAction() { + public void actionPerformed(ActionEvent e) { + AllZone.getOverlay().hideOverlay(); + } + }; + + actSaveKeyboardShortcuts = new AbstractAction() { + public void actionPerformed(ActionEvent e) { + control.saveKeyboardShortcuts(); + } + }; + + JLabel btnConcede = new DockButton(skin.getIconConcede(), "Concede Game"); + btnConcede.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + control.concede(); + } + }); + + JLabel btnShortcuts = new DockButton(skin.getIconShortcuts(), "Keyboard Shortcuts"); + btnShortcuts.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + overlayKeyboard(); + } + }); + + JLabel btnSettings = new DockButton(skin.getIconSettings(), "Game Settings"); + btnSettings.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + overlaySettings(); + } + }); + + JLabel btnEndTurn = new DockButton(skin.getIconEndTurn(), "Game Settings"); + btnEndTurn.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + control.endTurn(); + } + }); + + add(btnConcede, constraints); + add(btnShortcuts, constraints); + add(btnSettings, constraints); + add(btnEndTurn, constraints); + + // After all components are in place, instantiate controller. + control = new ControlDock(this); + } + + /** @return ControlDock */ + public ControlDock getController() { + return control; + } + + /** + * Buttons in Dock. JLabels are used to allow hover effects. + */ + public class DockButton extends JLabel { + private Image img; + private Color hoverBG = skin.getClrHover(); + private Color defaultBG = new Color(0, 0, 0, 0); + private Color clrBorders = new Color(0, 0, 0, 0); + private int w, h; + + /** + * Buttons in Dock. JLabels are used to allow hover effects. + * + * @param i0   ImageIcon to show in button + * @param s0   Tooltip string + */ + public DockButton(ImageIcon i0, String s0) { + super(); + setToolTipText(s0); + setOpaque(false); + setBackground(defaultBG); + img = i0.getImage(); + + addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + clrBorders = skin.getClrBorders(); + setBackground(hoverBG); + } + + @Override + public void mouseExited(MouseEvent e) { + clrBorders = new Color(0, 0, 0, 0); + setBackground(defaultBG); + } + }); + } + + @Override + public void paintComponent(Graphics g) { + w = getWidth(); + h = getHeight(); + g.setColor(getBackground()); + g.fillRect(0, 0, w, h); + g.setColor(clrBorders); + g.drawRect(0, 0, w - 1, h - 1); + g.drawImage(img, 0, 0, w, h, null); + super.paintComponent(g); + } + } + + /** */ + private void overlayKeyboard() { + FOverlay overlay = AllZone.getOverlay(); + overlay.removeAll(); + overlay.setLayout(new MigLayout("insets 0")); + overlay.showOverlay(); + + FPanel parent = new FPanel(); + parent.setBGImg(skin.getTexture1()); + parent.setBorder(new LineBorder(skin.getClrBorders(), 1)); + parent.setLayout(new MigLayout("insets 0, wrap 2, ax center, ay center")); + overlay.add(parent, "w 80%!, h 80%!, gaptop 10%, gapleft 10%, span 2 1"); + + FButton btnOK = new FButton(); + FButton btnCancel = new FButton(); + + overlay.add(btnOK, "width 30%, newline, gapright 10%, gapleft 15%, gaptop 10px"); + overlay.add(btnCancel, "width 30%!"); + + btnOK.setAction(actSaveKeyboardShortcuts); + btnOK.setText("Save and Exit"); + + btnCancel.setAction(actClose); + btnCancel.setText("Exit Without Save"); + + KeyboardShortcutLabel lblBlurb = new KeyboardShortcutLabel(); + lblBlurb.setText("
Focus in a box and press a key.
" + + "Backspace will remove the last keypress.
" + + "Restart Forge to map any new changes.
"); + parent.add(lblBlurb, "span 2 1, gapbottom 30px"); + + ForgePreferences fp = Singletons.getModel().getPreferences(); + + // Keyboard shortcuts are a bit tricky to make, since they involve three + // different parts of the codebase: Preferences, main match control, and + // the customize button in the dock. To make a keyboard shortcut: + // + // 1. Go to ForgePreferences and set a variable, default value, setter, and getter + // 2. Go to ControlMatchUI and map an action using mapKeyboardShortcuts + // 3. (Optional) Come back to this file and add a new KeyboardShortcutField so + // the user can customize this shortcut. Update ControlDock to ensure any + // changes will be saved. + + // Keyboard shortcuts must be created, then added to the HashMap to help saving. + // Their actions must also be declared, using mapKeyboardShortcuts in ControlMatchUI. + final KeyboardShortcutField showStack = new KeyboardShortcutField(fp.getShowStackShortcut()); + this.keyboardShortcutFields.put("showstack", showStack); + + final KeyboardShortcutField showCombat = new KeyboardShortcutField(fp.getShowCombatShortcut()); + this.keyboardShortcutFields.put("showcombat", showCombat); + + final KeyboardShortcutField showConsole = new KeyboardShortcutField(fp.getShowConsoleShortcut()); + this.keyboardShortcutFields.put("showconsole", showConsole); + + final KeyboardShortcutField showPlayers = new KeyboardShortcutField(fp.getShowPlayersShortcut()); + this.keyboardShortcutFields.put("showplayers", showPlayers); + + final KeyboardShortcutField showDev = new KeyboardShortcutField(fp.getShowDevShortcut()); + this.keyboardShortcutFields.put("showdev", showDev); + + final KeyboardShortcutField concedeGame = new KeyboardShortcutField(fp.getConcedeShortcut()); + this.keyboardShortcutFields.put("concede", concedeGame); + + final KeyboardShortcutField showPicture = new KeyboardShortcutField(fp.getShowPictureShortcut()); + this.keyboardShortcutFields.put("showpicture", showPicture); + + final KeyboardShortcutField showDetail = new KeyboardShortcutField(fp.getShowDetailShortcut()); + this.keyboardShortcutFields.put("showdetail", showDetail); + + // + parent.add(new KeyboardShortcutLabel("Show stack tab: "), "w 200px!"); + parent.add(showStack, "w 100px!"); + parent.add(new KeyboardShortcutLabel("Show combat tab: "), "w 200px!"); + parent.add(showCombat, "w 100px!"); + parent.add(new KeyboardShortcutLabel("Show console tab: "), "w 200px!"); + parent.add(showConsole, "w 100px!"); + parent.add(new KeyboardShortcutLabel("Show players tab: "), "w 200px!"); + parent.add(showPlayers, "w 100px!"); + parent.add(new KeyboardShortcutLabel("Show devmode tab: "), "w 200px!"); + parent.add(showDev, "w 100px!"); + parent.add(new KeyboardShortcutLabel("Concede game: "), "w 200px!"); + parent.add(concedeGame, "w 100px!"); + parent.add(new KeyboardShortcutLabel("Show card picture: "), "w 200px!"); + parent.add(showPicture, "w 100px!"); + parent.add(new KeyboardShortcutLabel("Show card detail: "), "w 200px!"); + parent.add(showDetail, "w 100px!"); + } + + /** */ + private void overlaySettings() { + FOverlay overlay = AllZone.getOverlay(); + overlay.setLayout(new MigLayout("insets 0")); + overlay.showOverlay(); + + JPanel parent = new JPanel(); + parent.setBackground(Color.red.darker()); + overlay.add(parent, "w 80%!, h 80%!, gaptop 10%, gapleft 10%, span 2 1"); + + FButton btnOK = new FButton("Save and Exit"); + FButton btnCancel = new FButton("Exit Without Save"); + + overlay.add(btnOK, "width 30%, newline, gapright 10%, gapleft 15%, gaptop 10px"); + overlay.add(btnCancel, "width 30%!"); + + btnOK.setAction(actClose); + btnOK.setText("Save and Exit"); + + btnCancel.setAction(actClose); + btnCancel.setText("Exit Without Save"); + + JLabel test = new JLabel(); + test.setForeground(Color.white); + test.setText("
'Settings' does not do anything yet.
" + + "This button is just here to demonstrate the dock feature.
" + + "'Settings' can be removed or developed further.
"); + + parent.add(test); + } + + /** Small private class to centralize label styling. */ + private class KeyboardShortcutLabel extends JLabel { + public KeyboardShortcutLabel() { + this(""); + } + + public KeyboardShortcutLabel(String s0) { + super(s0); + + this.setForeground(skin.getClrText()); + this.setFont(skin.getFont1().deriveFont(Font.PLAIN, 16)); + } + } + + /** A JTextField plus a "codeString" property, that stores keycodes for the shortcut. + * Also, an action listener that handles translation of keycodes into characters and + * (dis)assembly of keycode stack. + */ + public class KeyboardShortcutField extends JTextField { + private String codeString; + + /** A JTextField plus a "codeString" property, that stores keycodes for the shortcut. + * Also, an action listener that handles translation of keycodes into characters and + * (dis)assembly of keycode stack. + * + * This constructor sets the keycode string for this shortcut. + * Important: this parameter is keyCODEs not keyCHARs. + * + * @param s0   The string of keycodes for this shortcut + */ + public KeyboardShortcutField(String s0) { + this(); + this.setCodeString(s0); + } + + /** A JTextField plus a "codeString" property, that stores keycodes for the shortcut. + * Also, an action listener that handles translation of keycodes into characters and + * (dis)assembly of keycode stack. + */ + public KeyboardShortcutField() { + super(); + this.setEditable(false); + this.setFont(skin.getFont1().deriveFont(Font.PLAIN, 14)); + + this.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + control.addKeyCode(e); + } + }); + + this.addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) { + setBackground(skin.getClrActive()); + } + @Override + public void focusLost(FocusEvent e) { + setBackground(Color.white); + } + }); + } + + /** @return String */ + public String getCodeString() { + return codeString; + } + + /** @param s0   The new code string (space delimited) */ + public void setCodeString(String s0) { + if (s0.equals("null")) { + return; + } + + codeString = s0.trim(); + + List codes = new ArrayList(Arrays.asList(codeString.split(" "))); + List displayText = new ArrayList(); + + for (String s : codes) { + if (!s.isEmpty()) { + displayText.add(KeyEvent.getKeyText(Integer.valueOf(s))); + } + } + + this.setText(StringUtils.join(displayText, ' ')); + } + } + + /** @return Map */ + public Map getKeyboardShortcutFields() { + return keyboardShortcutFields; + } +} diff --git a/src/main/java/forge/view/match/ViewField.java b/src/main/java/forge/view/match/ViewField.java new file mode 100644 index 00000000000..18ad52bd011 --- /dev/null +++ b/src/main/java/forge/view/match/ViewField.java @@ -0,0 +1,628 @@ +package forge.view.match; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.GridBagConstraints; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.ScrollPaneConstants; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import javax.swing.border.LineBorder; +import javax.swing.border.MatteBorder; + +import arcane.ui.PlayArea; + +import net.miginfocom.swing.MigLayout; +import forge.AllZone; +import forge.Player; +import forge.Constant.Zone; +import forge.card.cardfactory.CardFactoryUtil; +import forge.control.match.ControlField; +import forge.gui.skin.FPanel; +import forge.gui.skin.FRoundedPanel; +import forge.gui.skin.FSkin; + +/** + * Assembles Swing components of player field instance. + * + */ +@SuppressWarnings("serial") +public class ViewField extends FRoundedPanel { + private FSkin skin; + private int counter; + + private ControlField control; + private PlayArea tabletop; + + private Border hoverBorder, inactiveBorder; + + private DetailLabel + lblHand, lblGraveyard, lblLibrary, + lblExile, lblFlashback, lblPoison, + lblBlack, lblBlue, lblGreen, + lblRed, lblWhite, lblColorless; + + private PhaseLabel + lblUpkeep, lblDraw, lblBeginCombat, lblEndCombat, lblEndTurn; + + private JLabel lblLife; + private Map keywordLabels; + private Color transparent = new Color(0, 0, 0, 0); + + /** + * Assembles Swing components of player field instance. + * + * @param player   a Player object. + */ + public ViewField(Player player) { + super(); + setOpaque(false); + setLayout(new MigLayout("insets 1% 0.5%, gap 0.5%")); + setCornerRadius(5); + setToolTipText(player.getName() + " Gameboard"); + setBackground(AllZone.getSkin().getClrTheme()); + + skin = AllZone.getSkin(); + inactiveBorder = new LineBorder(new Color(0, 0, 0, 0), 1); + hoverBorder = new LineBorder(skin.getClrBorders(), 1); + counter = -1; + + JScrollPane scroller = new JScrollPane(); + tabletop = new PlayArea(scroller, true); + + scroller.setViewportView(tabletop); + scroller.setOpaque(false); + scroller.getViewport().setOpaque(false); + scroller.setBorder(null); + tabletop.setBorder(new MatteBorder(0, 1, 0, 0, skin.getClrBorders())); + tabletop.setOpaque(false); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.BOTH; + + // + Avatar pic = new Avatar("res/pics/icons/unknown.jpg"); + Details pool = new Details(); + + add(pic, "h 98%!, w 10%!"); + add(pool, "h 98%!, w 11%!"); + add(scroller, "h 98%!, w 77%!"); + + // After all components are in place, instantiate controller. + control = new ControlField(player, this); + } + + /** @return ControlField */ + public ControlField getController() { + return control; + } + + /** Handles observer update of player Zones - hand, graveyard, etc. + * + * @param p0   Player obj + */ + public void updateZones(Player p0) { + getLblHand().setText("" + + p0.getZone(Zone.Hand).size()); + getLblGraveyard().setText("" + + p0.getZone(Zone.Graveyard).size()); + getLblLibrary().setText("" + + p0.getZone(Zone.Library).size()); + getLblFlashback().setText("" + + CardFactoryUtil.getExternalZoneActivationCards(p0).size()); + getLblExile().setText("" + + p0.getZone(Zone.Exile).size()); + } + + /** Handles observer update of non-Zone details - life, poison, etc. + * Also updates "players" panel in tabber for this player. + * + * @param p0   Player obj + */ + public void updateDetails(Player p0) { + // "Players" panel update + ViewTopLevel t = (ViewTopLevel) AllZone.getDisplay(); + t.getTabberController().getView().updatePlayerLabels(p0); + + // Poison/life + getLblLife().setText("" + p0.getLife()); + getLblPoison().setText("" + p0.getPoisonCounters()); + + // Hide all keyword labels, then show the appropriate ones. + for (JLabel lbl : keywordLabels.values()) { + lbl.setVisible(false); + } + + for (String s : p0.getKeywords()) { + keywordLabels.get(s).setVisible(true); + } + } + + //========= Retrieval methods + /** @return PlayArea where cards for this field are in play */ + public PlayArea getTabletop() { + return tabletop; + } + + /** @return DetailLabel */ + public JLabel getLblLife() { + return lblLife; + } + + /** @return DetailLabel for hand cards */ + public DetailLabel getLblHand() { + return lblHand; + } + + /** @return DetailLabel for library cards */ + public DetailLabel getLblLibrary() { + return lblLibrary; + } + + /** @return DetailLabel for graveyard cards */ + public DetailLabel getLblGraveyard() { + return lblGraveyard; + } + + /** @return DetailLabel for exiled cards */ + public DetailLabel getLblExile() { + return lblExile; + } + + /** @return DetailLabel for flashback cards */ + public DetailLabel getLblFlashback() { + return lblFlashback; + } + + /** @return DetailLabel for poison counters */ + public DetailLabel getLblPoison() { + return lblPoison; + } + + /** @return DetailLabel for colorless mana count */ + public DetailLabel getLblColorless() { + return lblColorless; + } + + /** @return DetailLabel for black mana count */ + public DetailLabel getLblBlack() { + return lblBlack; + } + + /** @return DetailLabel for blue mana count */ + public DetailLabel getLblBlue() { + return lblBlue; + } + + /** @return DetailLabel for green mana count */ + public DetailLabel getLblGreen() { + return lblGreen; + } + + /** @return DetailLabel for red mana count */ + public DetailLabel getLblRed() { + return lblRed; + } + + /** @return DetailLabel for white mana count */ + public DetailLabel getLblWhite() { + return lblWhite; + } + + // Phases + /** @return PhaseLabel for upkeep */ + public PhaseLabel getLblUpkeep() { + return lblUpkeep; + } + + /** @return PhaseLabel for draw */ + public PhaseLabel getLblDraw() { + return lblDraw; + } + + /** @return PhaseLabel for beginning of combat*/ + public PhaseLabel getLblBeginCombat() { + return lblBeginCombat; + } + + /** @return PhaseLabel for end of combat */ + public PhaseLabel getLblEndCombat() { + return lblEndCombat; + } + + /** @return PhaseLabel for end of turn */ + public PhaseLabel getLblEndTurn() { + return lblEndTurn; + } + + /** @return Map */ + public Map getKeywordLabels() { + return keywordLabels; + } + + //========== Custom classes + + /** + * Shows user icon, keywords, and phase for this field. + */ + private class Avatar extends FPanel { + private ImageIcon icon; + private Color transparent = new Color(0, 0, 0, 0); + + public Avatar(String filename) { + // Panel and background image icon init + super(); + setOpaque(false); + setLayout(new MigLayout("fill, wrap, insets 0, gap 0")); + icon = new ImageIcon(filename); + + // Life label + lblLife = new JLabel("--"); + lblLife.setBorder(inactiveBorder); + lblLife.setToolTipText("Player life.
Click to select player."); + lblLife.setFont(skin.getFont1().deriveFont(Font.BOLD, 18)); + lblLife.setForeground(skin.getClrText()); + lblLife.setBackground(skin.getClrTheme().darker()); + lblLife.setOpaque(true); + lblLife.setHorizontalAlignment(JLabel.CENTER); + lblLife.setAlignmentX(Component.CENTER_ALIGNMENT); + lblLife.addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + lblLife.setBackground(AllZone.getSkin().getClrHover()); + lblLife.setBorder(hoverBorder); + } + + @Override + public void mouseExited(MouseEvent e) { + lblLife.setBackground(skin.getClrTheme()); + lblLife.setBorder(inactiveBorder); + } + }); + add(lblLife, "w 100%!, dock north"); + + keywordLabels = new HashMap(); + // TODO link these map keys to correct keyword constant + keywordLabels.put("shroud", new KeywordLabel("Shroud")); + keywordLabels.put("extraturn", new KeywordLabel("+1 turn")); + keywordLabels.put("skipturn", new KeywordLabel("Skip turn")); + keywordLabels.put("problack", new KeywordLabel("Pro: Black")); + keywordLabels.put("problue", new KeywordLabel("Pro: Blue")); + keywordLabels.put("progreen", new KeywordLabel("Pro: Green")); + keywordLabels.put("prored", new KeywordLabel("Pro: Red")); + keywordLabels.put("prowhite", new KeywordLabel("Pro: White")); + + JPanel pnlKeywords = new JPanel(new MigLayout("insets 0, wrap, hidemode 2")); + pnlKeywords.setOpaque(false); + for (JLabel lbl : keywordLabels.values()) { + pnlKeywords.add(lbl); + } + + JScrollPane scrKeywords = new JScrollPane(pnlKeywords, + ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + + scrKeywords.setBorder(new EmptyBorder(0, 0, 0, 0)); + scrKeywords.setOpaque(false); + scrKeywords.getViewport().setOpaque(false); + scrKeywords.setViewportView(pnlKeywords); + add(scrKeywords, "w 100%!, growy"); + + JPanel phase = new JPanel(); + phase.setOpaque(false); + phase.setLayout(new MigLayout("fillx, insets 0, gap 0")); + add(phase, "w 100%!, h 20px!"); + + // Constraints string must be set once, for ease and also + // since dynamic sizing is buggy. + String constraints = "w 20%!, h 100%!"; + + lblUpkeep = new PhaseLabel("UP"); + lblUpkeep.setToolTipText("Phase: Upkeep
Click to toggle."); + phase.add(lblUpkeep, constraints); + + lblDraw = new PhaseLabel("DR"); + lblDraw.setToolTipText("Phase: Draw
Click to toggle."); + phase.add(lblDraw, constraints); + + lblBeginCombat = new PhaseLabel("BC"); + lblBeginCombat.setToolTipText("Phase: Begin Combat
Click to toggle."); + phase.add(lblBeginCombat, constraints); + + lblEndCombat = new PhaseLabel("EC"); + lblEndCombat.setToolTipText("Phase: End Combat
Click to toggle."); + phase.add(lblEndCombat, constraints); + + lblEndTurn = new PhaseLabel("ET"); + lblEndTurn.setToolTipText("Phase: End Turn
Click to toggle."); + phase.add(lblEndTurn, constraints); + } + + public void paintComponent(Graphics g) { + super.paintComponent(g); + setBorder(new MatteBorder(getWidth(), 0, 0, 0, transparent)); + g.drawImage(icon.getImage(), 0, 0, getWidth(), getWidth(), + 0, 0, icon.getIconWidth(), icon.getIconHeight(), null); + lblLife.setFont(skin.getFont1().deriveFont(Font.PLAIN, (int) (getWidth() / 4))); + } + } + + /** + * The "details" section of player info: + * Hand, library, graveyard, exiled, flashback, poison, + * and mana pool (BBGRW and colorless). + * + */ + // Design note: Labels are used here since buttons have various + // difficulties in displaying the desired "flat" background and + // also strange icon/action behavior. + private class Details extends JPanel { + public Details() { + super(); + setLayout(new MigLayout("insets 0, gap 0, wrap 2, filly")); + setOpaque(false); + final String constraints = "w 50%!, h 12.5%!, growy"; + + // Hand, library, graveyard, exile, flashback, poison labels + lblGraveyard = new DetailLabel(new ImageIcon("res/images/symbols-13/detail_grave.png"), "99"); + lblGraveyard.setToolTipText("Cards in graveyard"); + add(lblGraveyard, constraints); + + lblLibrary = new DetailLabel(new ImageIcon("res/images/symbols-13/detail_library.png"), "99"); + lblLibrary.setToolTipText("Cards in library"); + add(lblLibrary, constraints); + + lblExile = new DetailLabel(new ImageIcon("res/images/symbols-13/detail_exile.png"), "99"); + lblExile.setToolTipText("Exiled cards"); + add(lblExile, constraints); + + lblFlashback = new DetailLabel(new ImageIcon("res/images/symbols-13/detail_flashback.png"), "99"); + lblFlashback.setToolTipText("Flashback cards"); + add(lblFlashback, constraints); + + lblHand = new DetailLabel(new ImageIcon("res/images/symbols-13/detail_hand.png"), "99"); + lblHand.setToolTipText("Cards in hand"); + add(lblHand, constraints); + + lblPoison = new DetailLabel(new ImageIcon("res/images/symbols-13/detail_poison.png"), "99"); + lblPoison.setToolTipText("Poison counters"); + add(lblPoison, constraints); + + // Black, Blue, Colorless, Green, Red, White mana labels + lblBlack = new DetailLabel(new ImageIcon("res/images/symbols-13/B.png"), "99"); + lblBlack.setToolTipText("Black mana"); + add(lblBlack, constraints); + + lblBlue = new DetailLabel(new ImageIcon("res/images/symbols-13/U.png"), "99"); + lblBlue.setToolTipText("Blue mana"); + add(lblBlue, constraints); + + lblGreen = new DetailLabel(new ImageIcon("res/images/symbols-13/G.png"), "99"); + lblGreen.setToolTipText("Green mana"); + add(lblGreen, constraints); + + lblRed = new DetailLabel(new ImageIcon("res/images/symbols-13/R.png"), "99"); + lblRed.setToolTipText("Red mana"); + add(lblRed, constraints); + + lblWhite = new DetailLabel(new ImageIcon("res/images/symbols-13/W.png"), "99"); + lblWhite.setToolTipText("White mana"); + add(lblWhite, constraints); + + lblColorless = new DetailLabel(new ImageIcon("res/images/symbols-13/X.png"), "99"); + lblColorless.setToolTipText("Colorless mana"); + add(lblColorless, constraints); + } + } + + /** + * Used to show various values in "details" panel. Also increments + * grid bag constraints object as it goes, and zebra-stripes the labels. + */ + public class DetailLabel extends JLabel { + private final Dimension labelSize = new Dimension(40, 25); + private Color defaultBG; + private Color hoverBG; + private Color clrBorders; + private MouseAdapter madHover; + private int w, h; + + /** + * Instance of JLabel detailing info about field: + * has icon and optional hover effect. + * + * @param icon   Label's icon + * @param txt   Label's text + */ + public DetailLabel(ImageIcon icon, String txt) { + super(); + setIcon(icon); + setText(txt); + setOpaque(false); + setForeground(skin.getClrText()); + setPreferredSize(labelSize); + setMaximumSize(labelSize); + setMinimumSize(labelSize); + setBorder(new LineBorder(new Color(0, 0, 0, 0), 1)); + setHorizontalAlignment(CENTER); + + // Increment counter and check for zebra. Set default BG + // so hover effects return to the same color. + counter++; + + if (counter % 4 == 2 || counter % 4 == 3) { + defaultBG = skin.getClrZebra(); + } + else { + defaultBG = skin.getClrTheme(); + } + setBackground(defaultBG); + + madHover = new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + setBackground(hoverBG); + clrBorders = skin.getClrBorders(); + } + + @Override + public void mouseExited(MouseEvent e) { + setBackground(defaultBG); + clrBorders = transparent; + } + }; + + hoverBG = skin.getClrHover(); + clrBorders = transparent; + } + + /** Enable hover effects for this label. */ + public void enableHover() { + addMouseListener(madHover); + } + + /** Disable hover effects for this label. */ + public void disableHover() { + removeMouseListener(madHover); + } + + @Override + protected void paintComponent(Graphics g) { + w = getWidth(); + h = getHeight(); + g.setColor(getBackground()); + g.fillRect(0, 0, w, h); + g.setColor(clrBorders); + g.drawRect(0, 0, w - 1, h - 1); + this.setFont(skin.getFont1().deriveFont(Font.PLAIN, (int) (h / 2))); + super.paintComponent(g); + } + } + + private class KeywordLabel extends JLabel { + public KeywordLabel(String s) { + super(s); + setToolTipText(s); + } + } + + /** + * Shows phase labels, handles repainting and on/off states. A PhaseLabel + * has "skip" and "active" states, meaning "this phase is (not) skipped" + * and "this is the current phase". + */ + public class PhaseLabel extends JLabel { + private boolean enabled = true; + private boolean active = false; + private boolean hover = false; + private Color hoverBG = AllZone.getSkin().getClrHover(); + + /** + * Shows phase labels, handles repainting and on/off states. A PhaseLabel + * has "skip" and "active" states, meaning "this phase is (not) skipped" + * and "this is the current phase". + * + * @param txt   Label text + */ + public PhaseLabel(String txt) { + super(txt); + this.setHorizontalTextPosition(CENTER); + this.setHorizontalAlignment(CENTER); + + addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + if (enabled) { enabled = false; } + else { enabled = true; } + } + @Override + public void mouseEntered(MouseEvent e) { + hover = true; + repaint(); + } + @Override + public void mouseExited(MouseEvent e) { + hover = false; + repaint(); + } + }); + } + + /** + * Determines whether play pauses at this phase or not. + * + * @param b   boolean, true if play pauses + */ + public void setEnabled(boolean b) { + enabled = b; + } + + /** + * Determines whether play pauses at this phase or not. + * + * @return boolean + */ + public boolean getEnabled() { + return enabled; + } + + /** + * Determines if this phase is the current phase (or not). + * + * @param b   boolean, true if phase is current + */ + public void setActive(boolean b) { + active = b; + } + + /** + * Determines if this phase is the current phase (or not). + * + * @return boolean + */ + public boolean getActive() { + return active; + } + + @Override + public void paintComponent(Graphics g) { + int w = getWidth(); + int h = getHeight(); + Color c; + + // Set color according to skip or active or hover state of label + if (hover) { + c = hoverBG; + } + else if (enabled) { + c = Color.green; + } + else { + c = Color.red; + } + + if (active && !hover) { + c = c.darker().darker(); + } + + // Center vertically and horizontally. Show border if active. + g.setColor(c); + g.fillRoundRect(1, 1, w - 2, h - 2, 5, 5); + + setFont(new Font("TAHOMA", Font.PLAIN, (int) (w / 2))); + g.setColor(Color.black); + super.paintComponent(g); + } + } +} diff --git a/src/main/java/forge/view/match/ViewHand.java b/src/main/java/forge/view/match/ViewHand.java new file mode 100644 index 00000000000..8dd4b713fda --- /dev/null +++ b/src/main/java/forge/view/match/ViewHand.java @@ -0,0 +1,171 @@ +package forge.view.match; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.ScrollPaneConstants; +import javax.swing.SwingConstants; +import javax.swing.border.LineBorder; + +import net.miginfocom.swing.MigLayout; +import forge.AllZone; +import forge.Card; +import forge.GuiDisplayUtil; +import forge.ImageCache; +import forge.control.match.ControlHand; + +import forge.gui.skin.FRoundedPanel; + +/** + * VIEW - Swing components for user hand. + * + */ +@SuppressWarnings("serial") +public class ViewHand extends JScrollPane { + private FRoundedPanel pnlContent; + private ControlHand control; + private List cardPanels = new ArrayList(); + + /** + * VIEW - Swing components for user hand. + */ + public ViewHand() { + super(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); + + setOpaque(false); + getViewport().setOpaque(false); + getHorizontalScrollBar().setUnitIncrement(16); + setBorder(null); + + // After all components are in place, instantiate controller. + control = new ControlHand(this); + } + + /** @return ControlHand */ + public ControlHand getController() { + return control; + } + + /** + * Rebuilds layout of the hand panel. Card panels are removed, + * the height and card aspect ratio are used to set layout column width, + * then card panels are added to the fresh layout. + * + */ + // This design choice was made to allow the functionality of a JPanel + // while maintaining a scale-able view. Overridden paint methods could + // do this, but require heavy coding. + public void refreshLayout() { + // Remove all panels and recalculate layout scaling based on aspect ratio. + pnlContent = new FRoundedPanel(); + pnlContent.setBackground(AllZone.getSkin().getClrTheme()); + pnlContent.setCorners(new boolean[] {true, false, false, true}); + pnlContent.setLayout(new MigLayout("insets 3 10 3 10")); + pnlContent.setSize(getViewport().getSize()); + pnlContent.validate(); + this.setViewportView(pnlContent); + + int h = getViewport().getHeight() - 6; + pnlContent.setLayout(new MigLayout("align center")); + + // Re-insert panel instances. Possible memory management problem + // from re-adding pre-existing panels. Doublestrike 22-11-11 + cardPanels = new ArrayList(); + for (Card c : control.getCards()) { + CardPanel temp = new CardPanel(c); + cardPanels.add(temp); + pnlContent.add(temp, "h " + h + "px!, w " + (int) (h * 0.7) + "px!"); + control.addCardPanelListeners(temp); + } + // Notify system of change. + } + + /** + * + */ + public class CardPanel extends JPanel { + private static final long serialVersionUID = 509877513760665415L; + private Card card = null; + private Image img; + private int w, h = 0; + + /** + *

Constructor for CardPanel.

+ * + * @param c   Card object. + */ + public CardPanel(Card c) { + super(); + this.card = c; + this.img = ImageCache.getImage(card); + + setToolTipText("" + c.getName() + "
" + GuiDisplayUtil.formatCardType(c) + ""); + + // No image? + if (img == null) { + setBorder(new LineBorder(new Color(240, 240, 240), 1)); + setLayout(new MigLayout("wrap, insets 2, gap 0")); + setOpaque(true); + setBackground(new Color(200, 200, 200)); + + JLabel lblManaCost = new JLabel(c.getManaCost()); + lblManaCost.setHorizontalAlignment(SwingConstants.RIGHT); + + JLabel lblCardName = new JLabel(c.getName()); + lblCardName.setHorizontalAlignment(SwingConstants.CENTER); + + JLabel lblPowerToughness = new JLabel(""); + lblPowerToughness.setHorizontalAlignment(SwingConstants.RIGHT); + + if (c.isFaceDown()) { + lblCardName.setText("Morph"); + lblManaCost.setText(""); + } + + if (c.isCreature()) { + lblPowerToughness.setText(c.getNetAttack() + " / " + c.getNetDefense()); + } + + add(lblManaCost, "w 90%!"); + add(lblCardName, "w 90%!"); + add(lblPowerToughness, "w 90%!, gaptop 25"); + } + else { + setBorder(new LineBorder(Color.black, 1)); + w = img.getWidth(null); + h = img.getHeight(null); + } + } + + /** @return Card */ + public Card getCard() { + return this.card; + } + + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + if (img != null) { + Graphics2D g2d = (Graphics2D) g; + g2d.drawImage(img, 0, 0, getWidth(), getHeight(), 0, 0, w, h, null); + } + else { + g.setColor(new Color(200, 200, 200)); + g.drawRect(1, 1, getWidth(), getHeight()); + } + } + } + + /** @return List */ + public List getCardPanels() { + return cardPanels; + } +} diff --git a/src/main/java/forge/view/match/ViewInput.java b/src/main/java/forge/view/match/ViewInput.java new file mode 100644 index 00000000000..c7e0686816e --- /dev/null +++ b/src/main/java/forge/view/match/ViewInput.java @@ -0,0 +1,79 @@ +package forge.view.match; + +import java.awt.Font; + +import javax.swing.JButton; +import javax.swing.JTextArea; + +import net.miginfocom.swing.MigLayout; +import forge.AllZone; +import forge.control.match.ControlInput; +import forge.gui.skin.FButton; +import forge.gui.skin.FRoundedPanel; +import forge.gui.skin.FSkin; + +/** + * Assembles Swing components of input area. + * + */ +@SuppressWarnings("serial") +public class ViewInput extends FRoundedPanel { + private ControlInput control; + private JButton btnOK, btnCancel; + private JTextArea tarMessage; + private FSkin skin; + + /** + * Assembles UI for input area (buttons and message panel). + * + */ + public ViewInput() { + super(); + skin = AllZone.getSkin(); + setToolTipText("Input Area"); + setBackground(skin.getClrTheme()); + setForeground(skin.getClrText()); + setCorners(new boolean[] {false, false, false, true}); + setBorders(new boolean[] {true, false, false, true}); + + setLayout(new MigLayout("wrap 2, fill, insets 0, gap 0")); + + // Cancel button + btnCancel = new FButton("Cancel"); + btnOK = new FButton("OK"); + + tarMessage = new JTextArea(); + tarMessage.setOpaque(false); + tarMessage.setFocusable(false); + tarMessage.setEditable(false); + tarMessage.setLineWrap(true); + tarMessage.setForeground(skin.getClrText()); + tarMessage.setFont(skin.getFont1().deriveFont(Font.PLAIN, 12)); + add(tarMessage, "span 2 1, h 80%!, w 96%!, gapleft 2%, gaptop 1%"); + add(btnOK, "w 47%!, gapright 2%, gapleft 1%"); + add(btnCancel, "w 47%!, gapright 1%"); + + // After all components are in place, instantiate controller. + control = new ControlInput(this); + } + + /** @return ControlInput */ + public ControlInput getController() { + return control; + } + + /** @return JButton */ + public JButton getBtnOK() { + return btnOK; + } + + /** @return JButton */ + public JButton getBtnCancel() { + return btnCancel; + } + + /** @return JTextArea */ + public JTextArea getTarMessage() { + return tarMessage; + } +} diff --git a/src/main/java/forge/view/match/ViewTabber.java b/src/main/java/forge/view/match/ViewTabber.java new file mode 100644 index 00000000000..92a88ab8407 --- /dev/null +++ b/src/main/java/forge/view/match/ViewTabber.java @@ -0,0 +1,501 @@ +package forge.view.match; + +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.ScrollPaneConstants; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import javax.swing.border.MatteBorder; + +import net.miginfocom.swing.MigLayout; +import forge.AllZone; +import forge.Constant; +import forge.MagicStack; +import forge.Player; +import forge.card.spellability.SpellAbilityStackInstance; +import forge.control.match.ControlTabber; +import forge.gui.MultiLineLabelUI; +import forge.gui.skin.FPanel; +import forge.gui.skin.FSkin; +import forge.view.toolbox.FVerticalTabPanel; + +/** + * Vertical tab panel for viewing stack, combat, etc. + * Unfortunately, cannot extend a Swing component, since + * vertical tabs are generated dynamically in the constructor. + * + */ +public class ViewTabber { + private List panelList; + private HashMap detailLabels; + + private ControlTabber control; + private FSkin skin; + + private FPanel pnlStack, pnlCombat, pnlConsole, pnlPlayers, pnlDev; + + private DevLabel lblMilling, lblHandView, lblLibraryView, lblGenerateMana, + lblSetupGame, lblTutor, lblCounterPermanent, lblTapPermanent, lblUntapPermanent, + lblUnlimitedLands, lblHumanLife; + + private FVerticalTabPanel vtpTabber; + + /** + * Assembles Swing components for tabber area in sidebar. + */ + public ViewTabber() { + skin = AllZone.getSkin(); + + // Assemble card pic viewer + panelList = new ArrayList(); + String constraints = "wrap, insets 0, gap 0"; + + pnlStack = new FPanel(); + pnlStack.setName("Stack"); + pnlStack.setOpaque(false); + pnlStack.setLayout(new MigLayout(constraints)); + pnlStack.setToolTipText("View Stack"); + panelList.add(pnlStack); + + pnlCombat = new FPanel(); + pnlCombat.setName("Combat"); + pnlCombat.setOpaque(false); + pnlCombat.setLayout(new MigLayout(constraints)); + pnlCombat.setToolTipText("View Combat"); + panelList.add(pnlCombat); + + pnlConsole = new FPanel(); + pnlConsole.setName("Log"); + pnlConsole.setOpaque(false); + pnlConsole.setLayout(new MigLayout(constraints)); + pnlConsole.setToolTipText("View Console"); + panelList.add(pnlConsole); + + pnlPlayers = new FPanel(); + pnlPlayers.setName("Players"); + pnlPlayers.setOpaque(false); + pnlPlayers.setLayout(new MigLayout(constraints)); + pnlPlayers.setToolTipText("Player List"); + panelList.add(pnlPlayers); + + pnlDev = new FPanel(); + pnlDev.setName("Dev"); + pnlDev.setOpaque(false); + pnlDev.setLayout(new MigLayout(constraints)); + pnlDev.setToolTipText("Developer Mode"); + + if (Constant.Runtime.DEV_MODE[0]) { + panelList.add(pnlDev); + } + + // Populate the various panels in the tabber. + populatePnlDev(); + populatePnlPlayers(); + populatePnlConsole(); + + vtpTabber = new FVerticalTabPanel(panelList); + vtpTabber.getContentPanel().setBorder(new MatteBorder(1, 0, 0, 1, skin.getClrBorders())); + + // After all components are in place, instantiate controller. + control = new ControlTabber(this); + } + + /** @return ControlTabber */ + public ControlTabber getController() { + return control; + } + + /** + * Removes and adds JTextAreas to stack panel, which briefly summarize the + * spell and allow mouseover. + * + */ + public void updateStack() { + final MagicStack stack = AllZone.getStack(); + final ViewTopLevel t = (ViewTopLevel) AllZone.getDisplay(); + + int count = 1; + JTextArea tar; + String txt, isOptional; + + pnlStack.removeAll(); + vtpTabber.showTab(0); + Font font = skin.getFont1().deriveFont(Font.PLAIN, 11); + Border border = new MatteBorder(0, 0, 1, 0, Color.black); + + for (int i = stack.size() - 1; 0 <= i; i--) { + isOptional = stack.peekAbility(i).isOptionalTrigger() && stack.peekAbility(i).getSourceCard().getController().isHuman() ? "(OPTIONAL) " : ""; + txt = (count++) + ". " + isOptional + stack.peekInstance(i).getStackDescription(); + tar = new JTextArea(txt); + tar.setToolTipText(txt); + tar.setOpaque(false); + tar.setBorder(border); + tar.setFont(font); + tar.setFocusable(false); + tar.setEditable(false); + tar.setLineWrap(true); + tar.setWrapStyleWord(true); + + final SpellAbilityStackInstance spell = stack.peekInstance(i); + + tar.addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + t.getCardviewerController().showCard(spell.getSpellAbility().getSourceCard()); + System.out.println(); + } + }); + + pnlStack.add(tar, "w 100%!"); + } + } + + /** + * Removes and adds JTextAreas to combat panel, which briefly summarize the + * current combat situation. + * + * @param s   String message + */ + + // Note: Can (should?) be easily retrofitted to fit stack-style reporting: + // multiple text areas, with mouseovers highlighting combat cards. Doublestrike 06-11-11 + public void updateCombat(String s) { + pnlCombat.removeAll(); + vtpTabber.showTab(1); + + Font font = skin.getFont1().deriveFont(Font.PLAIN, 11); + Border border = new MatteBorder(1, 0, 0, 0, Color.black); + + JTextArea tar = new JTextArea(s); + tar.setOpaque(false); + tar.setBorder(border); + tar.setFont(font); + tar.setFocusable(false); + tar.setLineWrap(true); + pnlCombat.add(tar, "h 100%!, w 100%!"); + } + + /** Updates labels in the "player" panel, which display non-critical details about + * each player in the game. + * + * @param p0   Player obj + */ + public void updatePlayerLabels(Player p0) { + JLabel[] temp = detailLabels.get(p0); + temp[0].setText("Life: " + String.valueOf(p0.getLife())); + temp[1].setText("Max hand: " + String.valueOf(p0.getMaxHandSize())); + temp[2].setText("Draw per turn: " + String.valueOf(p0.getNumDrawnThisTurn())); + } + + /** @return FVerticalTabPanel */ + public FVerticalTabPanel getVtpTabber() { + return vtpTabber; + } + + /** @return FPanel */ + public FPanel getPnlStack() { + return pnlStack; + } + + /** @return FPanel */ + public FPanel getPnlCombat() { + return pnlCombat; + } + + /** @return FPanel */ + public FPanel getPnlPlayers() { + return pnlPlayers; + } + + /** @return FPanel */ + public FPanel getPnlDev() { + return pnlDev; + } + + /** @return DevLabel */ + public DevLabel getLblMilling() { + return lblMilling; + } + + /** @return DevLabel */ + public DevLabel getLblHandView() { + return lblHandView; + } + + /** @return DevLabel */ + public DevLabel getLblLibraryView() { + return lblLibraryView; + } + + /** @return DevLabel */ + public DevLabel getLblGenerateMana() { + return lblGenerateMana; + } + + /** @return DevLabel */ + public DevLabel getLblSetupGame() { + return lblSetupGame; + } + + /** @return DevLabel */ + public DevLabel getLblTutor() { + return lblTutor; + } + + /** @return DevLabel */ + public DevLabel getLblCounterPermanent() { + return lblCounterPermanent; + } + + /** @return DevLabel */ + public DevLabel getLblTapPermanent() { + return lblTapPermanent; + } + + /** @return DevLabel */ + public DevLabel getLblUntapPermanent() { + return lblUntapPermanent; + } + + /** @return DevLabel */ + public DevLabel getLblUnlimitedLands() { + return lblUnlimitedLands; + } + + /** @return DevLabel */ + public DevLabel getLblHumanLife() { + return lblHumanLife; + } + + /** @return HashMap */ + public HashMap getDetailLabels() { + return detailLabels; + } + + + /** Assembles Swing components for "players" panel. */ + private void populatePnlPlayers() { + List players = AllZone.getPlayersInGame(); + detailLabels = new HashMap(); + + for (Player p : players) { + // Create and store labels detailing various non-critical player info. + InfoLabel name = new InfoLabel(); + InfoLabel life = new InfoLabel(); + InfoLabel hand = new InfoLabel(); + InfoLabel draw = new InfoLabel(); + detailLabels.put(p, new JLabel[] {life, hand, draw}); + + // Set border on bottom label, and larger font on player name + draw.setBorder(new MatteBorder(0, 0, 1, 0, skin.getClrBorders())); + name.setText(p.getName()); + name.setFont(skin.getFont1().deriveFont(Font.PLAIN, 14)); + + // Add to "players" tab panel + String constraints = "w 97%!, gapleft 2%, gapbottom 1%"; + pnlPlayers.add(name, constraints); + pnlPlayers.add(life, constraints); + pnlPlayers.add(hand, constraints); + pnlPlayers.add(draw, constraints); + } + } + + /** Assembles Swing components for "dev mode" panel. */ + private void populatePnlDev() { + JPanel viewport = new JPanel(); + viewport.setLayout(new MigLayout("wrap, insets 0")); + viewport.setOpaque(false); + + JScrollPane jsp = new JScrollPane(viewport, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + jsp.setOpaque(false); + jsp.getViewport().setOpaque(false); + + pnlDev.add(jsp, "w 100%!, h 100%!"); + + lblMilling = new DevLabel("Loss by Milling: Enabled", "Loss by Milling: Disabled"); + lblHandView = new DevLabel("View Any Hand: Enabled", "View Any Hand: Disabled"); + lblLibraryView = new DevLabel("View Any Library: Enabled", "View Any Library: Disabled"); + lblGenerateMana = new DevLabel("Generate Mana"); + lblSetupGame = new DevLabel("Setup Game State"); + lblTutor = new DevLabel("Tutor for card"); + lblCounterPermanent = new DevLabel("Add Counter to Permanent"); + lblTapPermanent = new DevLabel("Tap Permanent"); + lblUntapPermanent = new DevLabel("Untap Permanent"); + lblUnlimitedLands = new DevLabel("Play Unlimited Lands This Turn"); + lblHumanLife = new DevLabel("Set Player Life"); + + String constraints = "w 100%!, gap 0 0 5px 0"; + viewport.add(lblMilling, constraints); + viewport.add(lblHandView, constraints); + viewport.add(lblLibraryView, constraints); + viewport.add(lblGenerateMana, constraints); + viewport.add(lblSetupGame, constraints); + viewport.add(lblTutor, constraints); + viewport.add(lblCounterPermanent, constraints); + viewport.add(lblTapPermanent, constraints); + viewport.add(lblUntapPermanent, constraints); + viewport.add(lblUnlimitedLands, constraints); + viewport.add(lblHumanLife, constraints); + } + + /** Assembles swing components for "console" panel. */ + private void populatePnlConsole() { + JLabel prompt = new JLabel("IN > "); + JTextField input = new JTextField(); + + JTextArea log = new JTextArea(); + log.setBackground(new Color(0, 0, 0, 20)); + log.setWrapStyleWord(true); + log.setLineWrap(true); + log.setWrapStyleWord(true); + log.setEditable(false); + log.setFocusable(false); + log.setForeground(skin.getClrText()); + log.setFont(skin.getFont1().deriveFont(Font.PLAIN, 12)); + log.setBorder(new MatteBorder(1, 0, 0, 0, skin.getClrBorders())); + + log.setText("Not implemented yet. Input codes entered above. " + + "Output data recorded below."); + + pnlConsole.setLayout(new MigLayout("insets 0, gap 0, wrap 2")); + + pnlConsole.add(prompt, "w 28%!, h 10%!, gapleft 2%, gaptop 2%, gapbottom 2%"); + pnlConsole.add(input, "w 68%!, gapright 2%, gaptop 2%, gapbottom 2%"); + pnlConsole.add(log, "w 94%!, h 80%!, gapleft 4%, span 2 1"); + } + + /** + * Labels that act as buttons which control dev mode functions. Labels + * are used to support multiline text. + * + */ + public class DevLabel extends JLabel { + private static final long serialVersionUID = 7917311680519060700L; + + private Color defaultBG = Color.green; + private Color hoverBG = skin.getClrHover(); + private boolean enabled; + private String enabledText, disabledText; + private int w, h, r, i; // Width, height, radius, insets (for paintComponent) + + /** + * Labels that act as buttons which control dev mode functions. Labels + * are used (instead of buttons) to support multiline text. + * + * Constructor for DevLabel which doesn't use enabled/disabled states; + * only single text string required. + * + * @param s0   String text/tooltip of label + */ + public DevLabel(String s0) { + this(s0, s0); + } + + /** + * Labels that act as buttons which control dev mode functions. Labels + * are used (instead of buttons) to support multiline text. + * + * This constructor for DevLabels empowers an "enable" state that + * displays them as green (enabled) or red (disabled). + * + * @param en0   String text/tooltip of label, in "enabled" state + * @param dis0   String text/tooltip of label, in "disabled" state + */ + public DevLabel(String en0, String dis0) { + super(); + this.setUI(MultiLineLabelUI.getLabelUI()); + this.setFont(skin.getFont1().deriveFont(Font.PLAIN, 11)); + this.setBorder(new EmptyBorder(5, 5, 5, 5)); + this.enabledText = en0; + this.disabledText = dis0; + this.r = 6; // Radius (for paintComponent) + this.i = 2; // Insets (for paintComponent) + setEnabled(true); + + this.addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + setBackground(hoverBG); + } + + @Override + public void mouseExited(MouseEvent e) { + setBackground(defaultBG); + } + }); + } + + /** + * Changes enabled state per boolean parameter, automatically updating + * text string and background color. + * + * @param b   boolean + */ + public void setEnabled(boolean b) { + String s; + if (b) { + defaultBG = Color.green; + s = enabledText; + } + else { + defaultBG = Color.red; + s = disabledText; + } + enabled = b; + this.setText(s); + this.setToolTipText(s); + this.setBackground(defaultBG); + } + + /** @return boolean */ + public boolean getEnabled() { + return enabled; + } + + /** + * In many cases, a DevLabel state will just be toggling a boolean. + * This method sets up and evaluates the condition and toggles as appropriate. + * + */ + public void toggleEnabled() { + if (enabled) { + setEnabled(false); + } + else { + setEnabled(true); + } + } + + @Override + protected void paintComponent(Graphics g) { + w = getWidth(); + h = getHeight(); + g.setColor(this.getBackground()); + g.fillRoundRect(i, i, w - 2 * i, h - i, r, r); + super.paintComponent(g); + } + } + + /** A quick JLabel for info in "players" panel, to consolidate styling. */ + @SuppressWarnings("serial") + private class InfoLabel extends JLabel { + public InfoLabel() { + super(); + this.setFont(skin.getFont1().deriveFont(Font.PLAIN, 11)); + this.setForeground(skin.getClrText()); + } + } +} diff --git a/src/main/java/forge/view/match/ViewTopLevel.java b/src/main/java/forge/view/match/ViewTopLevel.java new file mode 100644 index 00000000000..a9dc949f574 --- /dev/null +++ b/src/main/java/forge/view/match/ViewTopLevel.java @@ -0,0 +1,453 @@ +package forge.view.match; + +import java.awt.Graphics; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JTextArea; + +import forge.AllZone; +import forge.Card; +import forge.CardList; +import forge.Constant; +import forge.Display; +import forge.MyButton; +import forge.Player; +import forge.Singletons; +import forge.control.ControlMatchUI; +import forge.control.match.ControlCardviewer; +import forge.control.match.ControlDock; +import forge.control.match.ControlField; +import forge.control.match.ControlHand; +import forge.control.match.ControlInput; +import forge.control.match.ControlTabber; +import forge.gui.skin.FPanel; +import forge.properties.ForgePreferences; + +/** + * - Lays out battle, sidebar, user areas in locked % vals and repaints + * as necessary.
+ * - Instantiates top-level controller for match UI.
+ * - Has access methods for all child controllers
+ * - Implements Display interface used in singleton pattern + * + */ + +@SuppressWarnings("serial") +public class ViewTopLevel extends FPanel implements Display { + private ViewAreaSidebar areaSidebar; + private ViewAreaBattlefield areaBattle; + private ViewAreaUser areaUser; + + private int w, h; + private static final double SIDEBAR_W_PCT = 0.16; + private static final double USER_H_PCT = 0.27; + private ControlMatchUI control; + + /** + * - Lays out battle, sidebar, user areas in locked % vals and repaints + * as necessary.
+ * - Instantiates top-level controller for match UI.
+ * - Has access methods for all child controllers
+ * - Implements Display interface used in singleton pattern + * + */ + public ViewTopLevel() { + super(); + + // Set properties + setOpaque(false); + setBGTexture(AllZone.getSkin().getTexture1()); + setBGImg(AllZone.getSkin().getMatchBG()); + setLayout(null); + + // areaBattle: holds fields for all players in match. + areaBattle = new ViewAreaBattlefield(); + areaBattle.setBounds(0, 0, getWidth() / 2, getHeight() / 2); + add(areaBattle); + + // areaSidebar: holds card detail, info tabber. + areaSidebar = new ViewAreaSidebar(); + areaSidebar.setBounds(0, 0, getWidth() / 2, getHeight() / 2); + add(areaSidebar); + + // areaUser: holds input, hand, dock. + areaUser = new ViewAreaUser(); + areaUser.setBounds(0, 0, getWidth() / 2, getHeight() / 2); + add(areaUser); + + // After all components are in place, instantiate controller. + control = new ControlMatchUI(this); + } + + /** + * The null layout used in MatchFrame has zones split into percentage values + * to prevent child components pushing around the parent layout. A single + * instance of BodyPanel holds these zones, and handles the percentage resizing. + * + * @param g   Graphics object + */ + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + w = getWidth(); + h = getHeight(); + + // Set % boundaries of layout control layer + areaBattle.setBounds(0, 0, (int) (w * (1 - SIDEBAR_W_PCT)), + (int) (h * (1 - USER_H_PCT))); + areaSidebar.setBounds((int) (w * (1 - SIDEBAR_W_PCT)), 0, + (int) (w * SIDEBAR_W_PCT), h); + areaUser.setBounds(0, (int) (h * (1 - USER_H_PCT)), + (int) (w * (1 - SIDEBAR_W_PCT)), (int) (h * USER_H_PCT)); + areaBattle.validate(); + } + + + //========== Retrieval functions for easier interation with children panels. + /** @return ViewAreaSidebar */ + public ViewAreaSidebar getAreaSidebar() { + return areaSidebar; + } + + /** @return ViewAreaBattlefield */ + public ViewAreaBattlefield getAreaBattlefield() { + return areaBattle; + } + + /** @return ViewAreaUser */ + public ViewAreaUser getAreaUser() { + return areaUser; + } + + /** + * Retrieves top level controller (actions, observers, etc.) + * for this UI. + * + * @return {@link java.util.List} + */ + public ControlMatchUI getController() { + return control; + } + + /** @return ControlCardviewer */ + public ControlCardviewer getCardviewerController() { + return areaSidebar.getCardviewer().getController(); + } + + /** @return ControlTabber */ + public ControlTabber getTabberController() { + return areaSidebar.getTabber().getController(); + } + + /** @return ControlInput */ + public ControlInput getInputController() { + return areaUser.getPnlInput().getController(); + } + + /** @return ControlHand */ + public ControlHand getHandController() { + return areaUser.getPnlHand().getController(); + } + + /** @return ControlDock */ + public ControlDock getDockController() { + return areaUser.getPnlDock().getController(); + } + + /** @return List */ + public List getFieldControllers() { + List fields = areaBattle.getFields(); + List controllers = new ArrayList(); + + for (ViewField f : fields) { + controllers.add(f.getController()); + } + + return controllers; + } + + /** @return List */ + public List getFieldViews() { + return areaBattle.getFields(); + } + + //========== Input panel and human hand retrieval functions + // Also due to be deprecated. Access should be handled by child component + // view and/or controller. + + /** @return JTextArea Message area of input panel. */ + public JTextArea getPnlMessage() { + return areaUser.getPnlInput().getTarMessage(); + } + + /** @return ViewHand Retrieves player hand panel. */ + public ViewHand getPnlHand() { + return areaUser.getPnlHand(); + } + + //========== The following methods are required by the Display interface. + // To fit the UI MVC architecture with the previous "mixed nuts" architecture, + // these methods are temporarily required. However, since they are a mix of + // view and control functionalities, they are ALL on the "to-be-deprecated" list. + // The Display interface is to be reworked, eventually, with a better name + // and with interfaces for every screen in the entire UI. + // Doublestrike 23-10-11 + + /** + * Required by Display interface. + * Due to be deprecated in favor of more semantic getBtnCancel(). + * + * @return MyButton + */ + public MyButton getButtonCancel() { + MyButton cancel = new MyButton() { + public void select() { + getInputController().getInputControl().selectButtonCancel(); + } + + public boolean isSelectable() { + return areaUser.getPnlInput().getBtnCancel().isEnabled(); + } + + public void setSelectable(final boolean b) { + areaUser.getPnlInput().getBtnCancel().setEnabled(b); + } + + public String getText() { + return areaUser.getPnlInput().getBtnCancel().getText(); + } + + public void setText(final String text) { + areaUser.getPnlInput().getBtnCancel().setText(text); + } + + public void reset() { + areaUser.getPnlInput().getBtnCancel().setText("Cancel"); + } + }; + return cancel; + } + + /** + * Required by Display interface. + * Due to be deprecated in favor of more semantic getBtnOK(). + * + * @return MyButton + */ + public MyButton getButtonOK() { + MyButton ok = new MyButton() { + public void select() { + getInputController().getInputControl().selectButtonOK(); + } + + public boolean isSelectable() { + return areaUser.getPnlInput().getBtnOK().isEnabled(); + } + + public void setSelectable(final boolean b) { + areaUser.getPnlInput().getBtnOK().setEnabled(b); + } + + public String getText() { + return areaUser.getPnlInput().getBtnOK().getText(); + } + + public void setText(final String text) { + areaUser.getPnlInput().getBtnOK().setText(text); + } + + public void reset() { + areaUser.getPnlInput().getBtnOK().setText("OK"); + } + }; + + return ok; + } + + /** + * Required by Display interface. + * Due to be deprecated: is now and should be handled by ControlMatchUI. + * + * @param s   Message string + */ + public void showMessage(String s) { + getPnlMessage().setText(s); + } + + /** + * Required by Display interface. + * Due to be deprecated: should be handled by ControlMatchUI. + * + * @param s   Message string + */ + public void showCombat(String s) { + getTabberController().getView().updateCombat(s); + } + + /** + * Required by Display interface. + * Due to be deprecated: should be handled by a control class, and + * poorly named; "decking" == "milling" in preferences, same terminology + * should be used throughout project for obvious reasons. Unless "decking" + * is already the correct terminology, in which case, everything else is + * poorly named. + * + * @return boolean + */ + public boolean canLoseByDecking() { + return Constant.Runtime.MILL[0]; + } + + /** + *

loadPrefs.

+ * Required by Display interface. + * Due to be deprecated: will be handled by ControlMatchUI. + * + * + * @return boolean. + */ + public final boolean loadPrefs() { + ForgePreferences fp = Singletons.getModel().getPreferences(); + List fieldViews = getFieldViews(); + + // AI field is at index [0] + fieldViews.get(0).getLblUpkeep().setEnabled(fp.isbAIUpkeep()); + fieldViews.get(0).getLblDraw().setEnabled(fp.isbAIDraw()); + fieldViews.get(0).getLblEndTurn().setEnabled(fp.isbAIEOT()); + fieldViews.get(0).getLblBeginCombat().setEnabled(fp.isbAIBeginCombat()); + fieldViews.get(0).getLblEndCombat().setEnabled(fp.isbAIEndCombat()); + + // Human field is at index [1] + fieldViews.get(1).getLblUpkeep().setEnabled(fp.isbHumanUpkeep()); + fieldViews.get(1).getLblDraw().setEnabled(fp.isbHumanDraw()); + fieldViews.get(1).getLblEndTurn().setEnabled(fp.isbHumanEOT()); + fieldViews.get(1).getLblBeginCombat().setEnabled(fp.isbHumanBeginCombat()); + fieldViews.get(1).getLblEndCombat().setEnabled(fp.isbHumanEndCombat()); + + return true; + } + + /** + *

savePrefs.

+ * Required by Display interface. + * Due to be deprecated: will be handled by ControlMatchUI. + * Also, this functionality is already performed elsewhere in the code base. + * Furthermore, there's a strong possibility this will need bo be broken + * down and can't be in one place - e.g. keyboard shortcuts are + * saved after they're edited. + * + * @return a boolean. + */ + public final boolean savePrefs() { + ForgePreferences fp = Singletons.getModel().getPreferences(); + List fieldViews = getFieldViews(); + + // AI field is at index [0] + fp.setbAIUpkeep(fieldViews.get(0).getLblUpkeep().getEnabled()); + fp.setbAIDraw(fieldViews.get(0).getLblDraw().getEnabled()); + fp.setbAIEOT(fieldViews.get(0).getLblEndTurn().getEnabled()); + fp.setbAIBeginCombat(fieldViews.get(0).getLblBeginCombat().getEnabled()); + fp.setbAIEndCombat(fieldViews.get(0).getLblEndCombat().getEnabled()); + + // Human field is at index [1] + fp.setbHumanUpkeep(fieldViews.get(1).getLblUpkeep().getEnabled()); + fp.setbHumanDraw(fieldViews.get(1).getLblDraw().getEnabled()); + fp.setbHumanEOT(fieldViews.get(1).getLblEndTurn().getEnabled()); + fp.setbHumanBeginCombat(fieldViews.get(1).getLblBeginCombat().getEnabled()); + fp.setbHumanEndCombat(fieldViews.get(1).getLblEndCombat().getEnabled()); + + Constant.Runtime.MILL[0] = this.getTabberController().getView().getLblMilling().getEnabled(); + Constant.Runtime.HANDVIEW[0] = this.getTabberController().getView().getLblHandView().getEnabled(); + Constant.Runtime.LIBRARYVIEW[0] = this.getTabberController().getView().getLblLibraryView().getEnabled(); + + fp.setMillingLossCondition(Constant.Runtime.MILL[0]); + fp.setHandView(Constant.Runtime.HANDVIEW[0]); + fp.setLibraryView(Constant.Runtime.LIBRARYVIEW[0]); + return true; + } + + /** + *

stopAtPhase.

+ * Required by Display interface. + * Due to be deprecated: should be handled by control class. + * + * @param turn   Player object...more info needed + * @param phase   A string...more info needed + * @return a boolean. + */ + public final boolean stopAtPhase(final Player turn, final String phase) { + List fieldControllers = getFieldControllers(); + + // AI field is at index [0] + if (turn.isComputer()) { + if (phase.equals(Constant.Phase.END_OF_TURN)) { + return fieldControllers.get(0).getView().getLblEndTurn().getEnabled(); + } else if (phase.equals(Constant.Phase.UPKEEP)) { + return fieldControllers.get(0).getView().getLblUpkeep().getEnabled(); + } else if (phase.equals(Constant.Phase.DRAW)) { + return fieldControllers.get(0).getView().getLblDraw().getEnabled(); + } else if (phase.equals(Constant.Phase.COMBAT_BEGIN)) { + return fieldControllers.get(0).getView().getLblBeginCombat().getEnabled(); + } else if (phase.equals(Constant.Phase.COMBAT_END)) { + return fieldControllers.get(0).getView().getLblEndCombat().getEnabled(); + } + } + // Human field is at index [1] + else { + if (phase.equals(Constant.Phase.END_OF_TURN)) { + return fieldControllers.get(1).getView().getLblEndTurn().getEnabled(); + } else if (phase.equals(Constant.Phase.UPKEEP)) { + return fieldControllers.get(1).getView().getLblUpkeep().getEnabled(); + } else if (phase.equals(Constant.Phase.DRAW)) { + return fieldControllers.get(1).getView().getLblDraw().getEnabled(); + } else if (phase.equals(Constant.Phase.COMBAT_BEGIN)) { + return fieldControllers.get(1).getView().getLblBeginCombat().getEnabled(); + } else if (phase.equals(Constant.Phase.COMBAT_END)) { + return fieldControllers.get(1).getView().getLblEndCombat().getEnabled(); + } + } + return true; + } + + /** + * Required by display interface. + * Due to be deprecated: handled by control class. + * + * @return a {@link forge.Card} object. + */ + public final Card getCard() { + System.err.println("ViewTopLevel > getCard: Something should happen here!"); + new Exception().printStackTrace(); + return null; //new Card(); //detail.getCard(); + } + + /** + * Required by display interface. + * Due to be deprecated: already handled by controller class. + * + * @param card   a card + */ + public final void setCard(final Card card) { + System.err.println("ViewTopLevel > getCard: Something should happen here!"); + new Exception().printStackTrace(); + } + + /** + * Required by Display interface. + * Assigns damage to multiple blockers. + * Due to be deprecated: Gui_MultipleBlockers4 says "very hacky"; needs + * rewriting. + * + * @param attacker   Card object + * @param blockers   Card objects in CardList form + * @param damage   int + */ + public final void assignDamage(final Card attacker, final CardList blockers, final int damage) { + if (damage <= 0) { + return; + } + + //new Gui_MultipleBlockers4(attacker, blockers, damage, this); + } +} diff --git a/src/main/java/forge/view/swing/ApplicationView.java b/src/main/java/forge/view/swing/ApplicationView.java index 85f8ba43c06..b4911d38f39 100644 --- a/src/main/java/forge/view/swing/ApplicationView.java +++ b/src/main/java/forge/view/swing/ApplicationView.java @@ -97,7 +97,7 @@ public class ApplicationView implements FView { final ForgePreferences preferences = model.getPreferences(); OldGuiNewGame.getUseLAFFonts().setSelected(preferences.isLafFonts()); - // newGuiCheckBox.setSelected(preferences.newGui); + OldGuiNewGame.getOldGuiCheckBox().setSelected(preferences.isOldGui()); OldGuiNewGame.getSmoothLandCheckBox().setSelected(preferences.isStackAiLand()); OldGuiNewGame.getDevModeCheckBox().setSelected(preferences.isDeveloperMode()); OldGuiNewGame.getCardOverlay().setSelected(preferences.isCardOverlay()); diff --git a/src/main/java/forge/view/swing/OldGuiNewGame.java b/src/main/java/forge/view/swing/OldGuiNewGame.java index 30ce46b684e..eae858bfb97 100644 --- a/src/main/java/forge/view/swing/OldGuiNewGame.java +++ b/src/main/java/forge/view/swing/OldGuiNewGame.java @@ -59,6 +59,7 @@ import forge.ImageCache; import forge.MyRandom; import forge.PlayerType; import forge.Singletons; +import forge.control.ControlAllUI; import forge.deck.Deck; import forge.deck.DeckGeneration; import forge.deck.DeckManager; @@ -124,8 +125,8 @@ public class OldGuiNewGame extends JFrame { // @SuppressWarnings("unused") // titledBorder2 - /** Constant newGuiCheckBox. */ - // private static JCheckBox newGuiCheckBox = new JCheckBox("", true); + /** Constant oldGuiCheckBox. */ + private static JCheckBox oldGuiCheckBox = new JCheckBox("", false); /** Constant smoothLandCheckBox. */ private static JCheckBox smoothLandCheckBox = new JCheckBox("", false); /** Constant devModeCheckBox. */ @@ -556,7 +557,7 @@ public class OldGuiNewGame extends JFrame { ForgeProps.getLocalized(NewConstants.Lang.OldGuiNewGame.NewGameText.SETTINGS)); this.jPanel3.setLayout(new MigLayout("align center")); - // newGuiCheckBox.setText(ForgeProps.getLocalized(NEW_GAME_TEXT.NEW_GUI)); + oldGuiCheckBox.setText(ForgeProps.getLocalized(NewConstants.Lang.OldGuiNewGame.NewGameText.OLD_GUI)); OldGuiNewGame.getSmoothLandCheckBox().setText( ForgeProps.getLocalized(NewConstants.Lang.OldGuiNewGame.NewGameText.AI_LAND)); @@ -639,7 +640,7 @@ public class OldGuiNewGame extends JFrame { this.getContentPane().add(this.jPanel3, "span 2, grow"); - // jPanel3.add(newGuiCheckBox, "wrap"); + jPanel3.add(oldGuiCheckBox, "wrap"); this.jPanel3.add(OldGuiNewGame.getSmoothLandCheckBox(), "wrap"); this.jPanel3.add(OldGuiNewGame.getDevModeCheckBox(), "wrap"); this.jPanel3.add(OldGuiNewGame.getUpldDrftCheckBox(), "wrap"); @@ -865,15 +866,19 @@ public class OldGuiNewGame extends JFrame { } } // else - // DO NOT CHANGE THIS ORDER, GuiDisplay needs to be created before cards - // are added - // Constant.Runtime.DevMode[0] = devModeCheckBox.isSelected(); + Constant.Runtime.OLDGUI[0] = oldGuiCheckBox.isSelected(); - // if (newGuiCheckBox.isSelected()) - AllZone.setDisplay(new GuiDisplay()); - // else AllZone.setDisplay(new GuiDisplay3()); + if (Constant.Runtime.OLDGUI[0]) { + AllZone.setDisplay(new GuiDisplay()); + } + else { + ControlAllUI ui = new ControlAllUI(); + AllZone.setDisplay(ui.getMatchView()); + ui.getMatchController().initMatch(); + } Constant.Runtime.SMOOTH[0] = OldGuiNewGame.getSmoothLandCheckBox().isSelected(); + //Constant.Runtime.DEV_MODE[0] = OldGuiNewGame.devModeCheckBox.isSelected(); AllZone.getGameAction().newGame(Constant.Runtime.HUMAN_DECK[0], Constant.Runtime.COMPUTER_DECK[0]); AllZone.getDisplay().setVisible(true); @@ -1714,7 +1719,7 @@ public class OldGuiNewGame extends JFrame { final ForgePreferences preferences = Singletons.getModel().getPreferences(); preferences.setLaf(UIManager.getLookAndFeel().getClass().getName()); preferences.setLafFonts(OldGuiNewGame.getUseLAFFonts().isSelected()); - // preferences.newGui = newGuiCheckBox.isSelected(); + preferences.setOldGui(oldGuiCheckBox.isSelected()); preferences.setStackAiLand(OldGuiNewGame.getSmoothLandCheckBox().isSelected()); preferences.setMillingLossCondition(Constant.Runtime.MILL[0]); preferences.setDeveloperMode(Constant.Runtime.DEV_MODE[0]); @@ -2000,4 +2005,8 @@ public class OldGuiNewGame extends JFrame { // name. } + /** @return JCheckBox */ + public static JCheckBox getOldGuiCheckBox() { + return OldGuiNewGame.oldGuiCheckBox; + } } diff --git a/src/main/java/forge/view/swing/SplashFrame.java b/src/main/java/forge/view/swing/SplashFrame.java index ffb0f97f6b6..3c88feb4fc5 100644 --- a/src/main/java/forge/view/swing/SplashFrame.java +++ b/src/main/java/forge/view/swing/SplashFrame.java @@ -69,7 +69,7 @@ public class SplashFrame extends JFrame { this.setUndecorated(true); // Set preferred JFrame properties. - final ImageIcon bgIcon = skin.getSplash(); + final ImageIcon bgIcon = skin.getSplashBG(); final int splashWidthPx = bgIcon.getIconWidth(); final int splashHeightPx = bgIcon.getIconHeight(); diff --git a/src/main/java/forge/view/swing/WinLoseFrame.java b/src/main/java/forge/view/swing/WinLoseFrame.java index 9759aea282e..d3b622b3519 100644 --- a/src/main/java/forge/view/swing/WinLoseFrame.java +++ b/src/main/java/forge/view/swing/WinLoseFrame.java @@ -17,6 +17,7 @@ import javax.swing.border.AbstractBorder; import net.miginfocom.swing.MigLayout; import forge.AllZone; +import forge.Constant; import forge.Phase; import forge.Player; import forge.gui.skin.FButton; @@ -24,6 +25,7 @@ import forge.gui.skin.FPanel; import forge.properties.ForgeProps; import forge.properties.NewConstants.Lang.WinLoseFrame.WinLoseText; import forge.quest.data.QuestMatchState; +import forge.view.match.ViewTopLevel; /** *

@@ -88,7 +90,7 @@ public class WinLoseFrame extends JFrame { // Place all content in FPanel final FPanel contentPanel = new FPanel(new MigLayout("wrap, fill, insets 20 0 10 10")); - contentPanel.setBGImg(AllZone.getSkin().getTexture1()); + contentPanel.setBGTexture(AllZone.getSkin().getTexture1()); contentPanel.setBorder(new WinLoseBorder()); this.getContentPane().add(contentPanel); @@ -271,8 +273,17 @@ public class WinLoseFrame extends JFrame { * @return {@link javax.swing.JFrame} display frame */ final JFrame closeWinLoseFrame() { + JFrame frame; + // Issue 147 - keep battlefield up following win/loss - final JFrame frame = (JFrame) AllZone.getDisplay(); + if (Constant.Runtime.OLDGUI[0]) { + frame = (JFrame) AllZone.getDisplay(); + } + else { + ViewTopLevel temp = (ViewTopLevel) AllZone.getDisplay(); + frame = (JFrame) temp.getParent().getParent().getParent().getParent(); + } + frame.dispose(); frame.setEnabled(true); this.dispose(); diff --git a/src/main/java/forge/view/toolbox/CardDetailPanel.java b/src/main/java/forge/view/toolbox/CardDetailPanel.java new file mode 100644 index 00000000000..83cf97fb67e --- /dev/null +++ b/src/main/java/forge/view/toolbox/CardDetailPanel.java @@ -0,0 +1,382 @@ +package forge.view.toolbox; + +import java.awt.Color; +import java.awt.Font; +import java.util.Iterator; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.ScrollPaneConstants; +import javax.swing.SwingConstants; + +import net.miginfocom.swing.MigLayout; +import forge.AllZone; +import forge.Card; +import forge.CardContainer; +import forge.Constant.Zone; +import forge.Counters; +import forge.GameEntity; +import forge.GuiDisplayUtil; +import forge.gui.skin.FSkin; + +/** Shows details of card if moused over. */ +@SuppressWarnings("serial") +public class CardDetailPanel extends JPanel implements CardContainer { + private Card card = null; + private JLabel lblCardName, lblCardCost, lblCardType, + lblCardID, lblCardPT, lblCardSet, lblCardDmg1, lblCardDmg2; + private JTextArea tarInfo; + private static final Color PURPLE = new Color(14381203); + + private FSkin skin; + private Font f; + private Color foreground; + private Color zebra; + + /** */ + public CardDetailPanel() { + super(); + setLayout(new MigLayout("wrap, insets 0, gap 0")); + + // Styles used in DetailLabel class + skin = AllZone.getSkin(); + f = skin.getFont1().deriveFont(Font.PLAIN, 11); + foreground = skin.getClrText(); + zebra = skin.getClrZebra(); + + // DetailLabel instances (true = zebra stripe) + lblCardName = new DetailLabel(); + lblCardCost = new DetailLabel(); + lblCardType = new DetailLabel(); + lblCardPT = new DetailLabel(); + lblCardDmg1 = new DetailLabel(); + lblCardDmg2 = new DetailLabel(); + lblCardID = new DetailLabel(); + lblCardSet = new DetailLabel(); + + // Info text area + tarInfo = new JTextArea(); + tarInfo.setFont(f); + tarInfo.setLineWrap(true); + tarInfo.setWrapStyleWord(true); + tarInfo.setFocusable(false); + + add(lblCardName, "w 100%!"); + add(lblCardCost, "w 100%!"); + add(lblCardType, "w 100%!"); + add(lblCardPT, "w 100%!"); + add(lblCardDmg1, "w 100%!"); + add(lblCardDmg2, "w 100%!"); + add(lblCardID, "w 100%!"); + add(lblCardSet, "w 100%!"); + add(new JScrollPane(tarInfo, + ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER), "w 100%!, h 45%!, south"); + setCard(card); + } + + /** + * A fairly long method testing conditions determining which details to show. + * + * @param c   Card obj + */ + public void setCard(Card c) { + lblCardName.setText(" "); + lblCardCost.setText(" "); + lblCardType.setText(" "); + lblCardPT.setText(" "); + lblCardDmg1.setText(" "); + lblCardDmg2.setText(" "); + lblCardID.setText(" "); + lblCardSet.setText(" "); + lblCardSet.setOpaque(true); + lblCardSet.setBorder(null); + tarInfo.setText(" "); + + this.card = c; + if (card == null) { + return; + } + + boolean faceDown = card.isFaceDown() && card.getController() != AllZone.getHumanPlayer(); + + if (!faceDown) { + lblCardName.setText(card.getName()); + if (!card.getManaCost().equals("") && !card.isLand()) { + lblCardCost.setText(card.getManaCost()); + } + } + else { + lblCardName.setText("Morph"); + } + + if (!faceDown) { + lblCardType.setText(GuiDisplayUtil.formatCardType(card)); + } + else { + lblCardType.setText("Creature"); + } + + if (card.isCreature()) { + lblCardPT.setText(card.getNetAttack() + " / " + card.getNetDefense()); + lblCardDmg1.setText("Damage: " + card.getDamage()); + lblCardDmg2.setText("Assigned Damage: " + card.getTotalAssignedDamage()); + } + if (card.isPlaneswalker()) { + lblCardDmg2.setText("Assigned Damage: " + card.getTotalAssignedDamage()); + } + + lblCardID.setText("Card ID " + card.getUniqueNumber()); + + // Rarity and set of a face down card should not be visible to the opponent + if (!card.isFaceDown() || card.getController().isHuman()) { + String s = card.getCurSetCode(); + if (s.equals("")) { + lblCardSet.setText("---"); + } + else { + lblCardSet.setText(s); + } + } + + String csr = card.getCurSetRarity(); + if (csr.equals("Common") || csr.equals("Land")) { + lblCardSet.setBackground(Color.BLACK); + lblCardSet.setForeground(Color.WHITE); + //lblCardSet.setBorder(BorderFactory.createLineBorder(Color.WHITE)); + } else if (csr.equals("Uncommon")) { + lblCardSet.setBackground(Color.LIGHT_GRAY); + lblCardSet.setForeground(Color.BLACK); + //lblCardSet.setBorder(BorderFactory.createLineBorder(Color.BLACK)); + } else if (csr.equals("Rare")) { + lblCardSet.setBackground(Color.YELLOW); + lblCardSet.setForeground(Color.BLACK); + //lblCardSet.setBorder(BorderFactory.createLineBorder(Color.BLACK)); + } else if (csr.equals("Mythic")) { + lblCardSet.setBackground(Color.RED); + lblCardSet.setForeground(Color.BLACK); + //lblCardSet.setBorder(BorderFactory.createLineBorder(Color.BLACK)); + } else if (csr.equals("Special")) { + // "Timeshifted" or other Special Rarity Cards + lblCardSet.setBackground(PURPLE); + lblCardSet.setForeground(Color.BLACK); + //lblCardSet.setBorder(BorderFactory.createLineBorder(Color.BLACK)); + } + + // Fill the card text + StringBuilder str = new StringBuilder(); + + // Token + if (card.isToken()) { str.append("Token"); } + + if (!faceDown) { + if (str.length() != 0) { str.append("\n"); } + String text = card.getText(); + + //LEVEL [0-9]+-[0-9]+ + //LEVEL [0-9]+\+ + String regex = "LEVEL [0-9]+-[0-9]+ "; + text = text.replaceAll(regex, "$0\r\n"); + + regex = "LEVEL [0-9]+\\+ "; + text = text.replaceAll(regex, "\r\n$0\r\n"); + + // Displays keywords that have dots in them a little better: + regex = "\\., "; + text = text.replaceAll(regex, ".\r\n"); + + str.append(text); + } + + // Phasing + if (card.isPhasedOut()) { + if (str.length() != 0) { str.append("\n"); } + str.append("Phased Out"); + } + + // Counter text + Counters[] counters = Counters.values(); + for (Counters counter : counters) { + if (card.getCounters(counter) != 0) { + if (str.length() != 0) { str.append("\n"); } + str.append(counter.getName() + " counters: "); + str.append(card.getCounters(counter)); + } + } + + // Regeneration Shields + int regenShields = card.getShield(); + if (regenShields > 0) { + if (str.length() != 0) { str.append("\n"); } + str.append("Regeneration Shield(s): ").append(regenShields); + } + + // Damage Prevention + int preventNextDamage = card.getPreventNextDamage(); + if (preventNextDamage > 0) { + str.append("\n"); + str.append("Prevent the next ").append(preventNextDamage).append(" damage that would be dealt to "); + str.append(card.getName()).append(" it this turn."); + } + + // Top revealed + if (card.hasKeyword("Play with the top card of your library revealed.") && card.getController() != null + && !card.getController().getZone(Zone.Library).isEmpty()) { + str.append("\r\nTop card: "); + str.append(card.getController().getCardsIn(Zone.Library, 1)); + } + + // Chosen type + if (card.getChosenType() != "") { + if (str.length() != 0) { str.append("\n"); } + str.append("(chosen type: "); + str.append(card.getChosenType()); + str.append(")"); + } + + // Chosen color + if (!card.getChosenColor().isEmpty()) { + if (str.length() != 0) { str.append("\n"); } + str.append("(chosen colors: "); + str.append(card.getChosenColor()); + str.append(")"); + } + + // Named card + if (card.getNamedCard() != "") { + if (str.length() != 0) { str.append("\n"); } + str.append("(named card: "); + str.append(card.getNamedCard()); + str.append(")"); + } + + // Equipping + if (card.getEquipping().size() > 0) { + if (str.length() != 0) { str.append("\n"); } + str.append("=Equipping "); + str.append(card.getEquipping().get(0)); + str.append("="); + } + + // Equipped by + if (card.getEquippedBy().size() > 0) { + if (str.length() != 0) { str.append("\n"); } + str.append("=Equipped by "); + for (Iterator it = card.getEquippedBy().iterator(); it.hasNext();) { + str.append(it.next()); + if (it.hasNext()) { str.append(", "); } + } + str.append("="); + } + + // Enchanting + GameEntity entity = card.getEnchanting(); + if (entity != null) { + if (str.length() != 0) { str.append("\n"); } + str.append("*Enchanting "); + + if (entity instanceof Card) { + Card temp = (Card) entity; + if (temp.isFaceDown() && temp.getController().isComputer()) { + str.append("Morph ("); + str.append(card.getUniqueNumber()); + str.append(")"); + } + else { + str.append(entity); + } + } + else { + str.append(entity); + } + str.append("*"); + } + + // Enchanted by + if (card.getEnchantedBy().size() > 0) { + if (str.length() != 0) { str.append("\n"); } + str.append("*Enchanted by "); + for (Iterator it = card.getEnchantedBy().iterator(); it.hasNext();) { + str.append(it.next()); + if (it.hasNext()) { str.append(", "); } + } + str.append("*"); + } + + // Controlling + if (card.getGainControlTargets().size() > 0) { + if (str.length() != 0) { str.append("\n"); } + str.append("+Controlling: "); + for (Iterator it = card.getGainControlTargets().iterator(); it.hasNext();) { + str.append(it.next()); + if (it.hasNext()) { str.append(", "); } + } + str.append("+"); + } + + // Cloned via + if (card.getCloneOrigin() != null) { + if (str.length() != 0) { str.append("\n"); } + str.append("^Cloned via: "); + str.append(card.getCloneOrigin().getName()); + str.append("^"); + } + + // Imprint + if (!card.getImprinted().isEmpty()) { + if (str.length() != 0) { str.append("\n"); } + str.append("^Imprinting: "); + for (Iterator it = card.getImprinted().iterator(); it.hasNext();) { + str.append(it.next()); + if (it.hasNext()) { str.append(", "); } + } + str.append("^"); + } + + // Uncastable + /*if (card.isUnCastable()) { + if (str.length() != 0) str.append("\n"); + str.append("This card can't be cast."); + }*/ + + if (card.hasAttachedCardsByMindsDesire()) { + if (str.length() != 0) { str.append("\n"); } + Card[] cards = card.getAttachedCardsByMindsDesire(); + str.append("=Attached: "); + for (Card temp : cards) { + str.append(temp.getName()); + str.append(" "); + } + str.append("="); + } + + tarInfo.setText(str.toString()); + } + + /** @return Card */ + public Card getCard() { + return this.card; + } + + /** A brief JLabel to consolidate styling. */ + private class DetailLabel extends JLabel { + public DetailLabel(boolean zebra0) { + super(); + + if (zebra0) { + this.setBackground(zebra); + this.setOpaque(true); + } + + this.setFont(f); + this.setForeground(foreground); + this.setHorizontalAlignment(SwingConstants.CENTER); + } + + public DetailLabel() { + this(false); + } + } +} diff --git a/src/main/java/forge/view/toolbox/CardViewer.java b/src/main/java/forge/view/toolbox/CardViewer.java new file mode 100644 index 00000000000..548a8d72c42 --- /dev/null +++ b/src/main/java/forge/view/toolbox/CardViewer.java @@ -0,0 +1,88 @@ +package forge.view.toolbox; + +import javax.swing.AbstractListModel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import forge.AllZone; +import forge.Card; +import forge.CardUtil; +import forge.gui.game.CardDetailPanel; +import forge.gui.game.CardPicturePanel; +import forge.item.CardPrinted; + +import java.util.List; + +import static java.util.Collections.unmodifiableList; + +/** + * A simple JPanel that shows three columns: card list, pic, and description.. + * + * @author Forge + * @version $Id: ListChooser.java 9708 2011-08-09 19:34:12Z jendave $ + */ +@SuppressWarnings("serial") +public class CardViewer extends JPanel { + + //Data and number of choices for the list + private List list; + + //initialized before; listeners may be added to it + private JList jList; + private CardDetailPanel detail; + private CardPicturePanel picture; + + public CardViewer(List list) { + this.list = unmodifiableList(list); + jList = new JList(new ChooserListModel()); + detail = new CardDetailPanel(null); + picture = new CardPicturePanel(null); + + this.add(new JScrollPane(jList)); + this.add(picture); + this.add(detail); + this.setLayout( new java.awt.GridLayout(1, 3, 6, 0) ); + + // selection is here + jList.getSelectionModel().addListSelectionListener(new SelListener()); + jList.setSelectedIndex(0); + } + + private class ChooserListModel extends AbstractListModel { + + private static final long serialVersionUID = 3871965346333840556L; + + public int getSize() { return list.size(); } + public Object getElementAt(int index) { return list.get(index); } + } + + + private class SelListener implements ListSelectionListener { + private Card[] cache = null; + + public void valueChanged(final ListSelectionEvent e) { + int row = jList.getSelectedIndex(); + // (String) jList.getSelectedValue(); + if (row >= 0 && row < list.size()) { + CardPrinted cp = list.get(row); + ensureCacheHas(row, cp); + detail.setCard(cache[row]); + picture.setCard(cp); + } + } + + private void ensureCacheHas(int row, CardPrinted cp) { + if (cache == null) { cache = new Card[list.size()]; } + if (null == cache[row]) { + Card card = AllZone.getCardFactory().getCard(cp.getName(), null); + card.setCurSetCode(cp.getSet()); + card.setImageFilename(CardUtil.buildFilename(card)); + cache[row] = card; + } + } + } + +} diff --git a/src/main/java/forge/view/toolbox/FOverlay.java b/src/main/java/forge/view/toolbox/FOverlay.java new file mode 100644 index 00000000000..08bac3418d1 --- /dev/null +++ b/src/main/java/forge/view/toolbox/FOverlay.java @@ -0,0 +1,78 @@ +package forge.view.toolbox; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JPanel; + +/** + * Semi-transparent overlay panel. Should be used with layered panes. + * + */ + +// Currently used only once, in top level UI, with layering already in place. +// Getter in AllZone: getOverlay() +@SuppressWarnings("serial") +public class FOverlay extends JPanel { + private JButton btnClose; + + /** + * Semi-transparent overlay panel. Should be used with layered panes. + */ + public FOverlay() { + super(); + btnClose = new JButton("X"); + btnClose.setForeground(Color.white); + btnClose.setBorder(BorderFactory.createLineBorder(Color.white)); + btnClose.setOpaque(false); + btnClose.setBackground(new Color(0, 0, 0)); + btnClose.setFocusPainted(false); + + btnClose.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + hideOverlay(); + } + }); + } + + /** */ + public void showOverlay() { + setVisible(true); + } + + /** */ + public void hideOverlay() { + setVisible(false); + } + + /** + * Gets the close button, which must be added dynamically since + * different overlays have different layouts. The overlay does + * not have the close button by default, but a fully working + * instance is available if required. + * + * @return JButton + */ + public JButton getBtnClose() { + return btnClose; + } + + /** + * For some reason, the alpha channel background doesn't work + * properly on Windows 7, so the paintComponent override is + * required for a semi-transparent overlay. + * + * @param g   Graphics object + */ + @Override + public void paintComponent(Graphics g) { + super.paintComponent(g); + g.setColor(new Color(0, 0, 0, 200)); + g.fillRect(0, 0, getWidth(), getHeight()); + } +} diff --git a/src/main/java/forge/view/toolbox/FVerticalTabPanel.java b/src/main/java/forge/view/toolbox/FVerticalTabPanel.java new file mode 100644 index 00000000000..2889288ba07 --- /dev/null +++ b/src/main/java/forge/view/toolbox/FVerticalTabPanel.java @@ -0,0 +1,190 @@ +package forge.view.toolbox; + +import java.awt.CardLayout; +import java.awt.Color; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.geom.AffineTransform; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.MatteBorder; + +import net.miginfocom.swing.MigLayout; +import forge.AllZone; +import forge.gui.skin.FPanel; +import forge.gui.skin.FSkin; + +/** + * TODO: Write javadoc for this type. + * + */ +@SuppressWarnings("serial") +public class FVerticalTabPanel extends FPanel { + private CardLayout cards; + private JPanel pnlContent; + private List allVTabs; + + private FSkin skin; + private int active; + private Color activeColor, inactiveColor, hoverColor; + private Border inactiveBorder, hoverBorder; + + /** + * Assembles vertical tab panel from list of child panels. Tooltip + * on tab is same as tooltip on child panel. Title of tab is same + * as name of child panel. + * + * @param childPanels   JPanels to be placed in tabber + */ + public FVerticalTabPanel(List childPanels) { + // General inits and skin settings + super(); + setLayout(new MigLayout("insets 0, gap 0, wrap 2")); + setOpaque(false); + int size = childPanels.size(); + skin = AllZone.getSkin(); + hoverColor = skin.getClrHover(); + activeColor = skin.getClrActive(); + inactiveColor = skin.getClrInactive(); + hoverBorder = new MatteBorder(1, 0, 1, 1, skin.getClrBorders()); + inactiveBorder = new MatteBorder(1, 0, 1, 1, new Color(0, 0, 0, 0)); + + final int pctTabH = (int) ((100 - 2 - 2) / size); + final int pctTabW = 11; + final int pctInsetH = 3; + final int pctSpacing = 1; + + // Content panel and card layout inits + cards = new CardLayout(); + pnlContent = new JPanel(); + pnlContent.setOpaque(false); + pnlContent.setLayout(cards); + pnlContent.setBorder(new MatteBorder(0, 0, 0, 1, skin.getClrBorders())); + + add(pnlContent, "span 1 " + (size + 2) + ", w " + (100 - pctTabW) + "%!, h 100%!"); + + JPanel topSpacer = new JPanel(); + topSpacer.setOpaque(false); + add(topSpacer, "w " + pctTabW + "%!, h " + pctInsetH + "%!"); + + // Add all tabs + VTab tab; + allVTabs = new ArrayList(); + + for (int i = 0; i < size; i++) { + tab = new VTab(childPanels.get(i).getName(), i); + tab.setToolTipText(childPanels.get(i).getToolTipText()); + + if (i == 0) { + tab.setBackground(activeColor); + active = 0; + } + else { + tab.setBackground(inactiveColor); + } + + add(tab, "w " + pctTabW + "%!, h " + (pctTabH - pctSpacing) + "%!, gapbottom " + pctSpacing + "%!"); + allVTabs.add(tab); + + // Add card to content panel + pnlContent.add(childPanels.get(i), "CARD" + i); + } + + JPanel bottomSpacer = new JPanel(); + bottomSpacer.setOpaque(false); + add(bottomSpacer, "w 10%!, h " + (pctInsetH + pctSpacing) + "%!"); + } + + /** + * Programatically flips tab layout to specified number (without needing + * a mouse event). + * + * @param index   Tab number, starting from 0 + */ + public void showTab(int index) { + if (index >= this.allVTabs.size()) { + return; + } + + allVTabs.get(active).setBackground(inactiveColor); + active = index; + cards.show(pnlContent, "CARD" + index); + allVTabs.get(active).setBackground(activeColor); + } + + /** @return JPanel */ + public JPanel getContentPanel() { + return pnlContent; + } + + /** + * A single instance of a vertical tab, with paintComponent overridden + * to provide vertical-ness. Also manages root level hover and click effects. + * + */ + private class VTab extends JPanel { + private String msg; + private int id, w; + + // ID is used to retrieve this tab from the list of allVTabs. + VTab(String txt, int i) { + super(); + setLayout(new MigLayout("insets 0, gap 0")); + setOpaque(true); + msg = txt; + id = i; + + addMouseListener(new MouseAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + if (id != active) { + setBorder(hoverBorder); + setBackground(hoverColor); + } + } + + @Override + public void mouseExited(MouseEvent e) { + if (id != active) { + setBorder(inactiveBorder); + setBackground(inactiveColor); + } + } + + @Override + public void mousePressed(MouseEvent e) { + allVTabs.get(active).setBackground(inactiveColor); + active = id; + cards.show(pnlContent, "CARD" + id); + setBackground(activeColor); + } + }); + } + + protected void paintComponent(Graphics g) { + super.paintComponent(g); + w = getWidth(); + + // Careful with this font scale factor; the vertical tabs will be unreadable + // if small window, too big if large window. + g.setFont(skin.getFont1().deriveFont(Font.PLAIN, (int) (w * 0.68))); + + // Rotate, draw string, rotate back (to allow hover border to be painted properly) + Graphics2D g2d = (Graphics2D) g; + AffineTransform at = g2d.getTransform(); + at.rotate(Math.toRadians(90), 0, 0); + g2d.setTransform(at); + g2d.setColor(AllZone.getSkin().getClrText()); + g2d.drawString(msg, 5, -4); + + at.rotate(Math.toRadians(-90), 0, 0); + g2d.setTransform(at); + } + } +}