diff --git a/forge-gui-desktop/src/main/java/forge/control/KeyboardShortcuts.java b/forge-gui-desktop/src/main/java/forge/control/KeyboardShortcuts.java index 8b2de20b8de..746850ff9f2 100644 --- a/forge-gui-desktop/src/main/java/forge/control/KeyboardShortcuts.java +++ b/forge-gui-desktop/src/main/java/forge/control/KeyboardShortcuts.java @@ -13,6 +13,7 @@ import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.KeyStroke; +import javax.swing.SwingUtilities; import org.apache.commons.lang3.StringUtils; @@ -27,6 +28,7 @@ import forge.screens.match.CMatchUI; import forge.toolbox.special.CardZoomer; import forge.util.Localizer; +import forge.view.KeyboardShortcutsDialog; /** * Consolidates keyboard shortcut assembly into one location @@ -36,10 +38,16 @@ * and you're done. */ public class KeyboardShortcuts { + private static List cachedShortcuts; + public static List getKeyboardShortcuts() { return attachKeyboardShortcuts(null); } + public static List getCachedShortcuts() { + return cachedShortcuts != null ? cachedShortcuts : getKeyboardShortcuts(); + } + /** * Attaches all keyboard shortcuts for match UI, * and returns a list of shortcuts with necessary properties for later access. @@ -93,6 +101,16 @@ public void actionPerformed(final ActionEvent e) { } }; + /** Undo last action. */ + final Action actUndo = new AbstractAction() { + @Override + public void actionPerformed(final ActionEvent e) { + if (!Singletons.getControl().getCurrentScreen().isMatchScreen()) { return; } + if (matchUI == null) { return; } + matchUI.getGameController().undoLastAction(); + } + }; + /** Concede game. */ final Action actConcede = new AbstractAction() { @Override @@ -200,12 +218,52 @@ public void actionPerformed(ActionEvent e) { } }; + /** Toggle panel tabs. */ + final Action actPanelTabs = new AbstractAction() { + @Override + public void actionPerformed(final ActionEvent e) { + if (!Singletons.getControl().getCurrentScreen().isMatchScreen()) { return; } + final ForgePreferences prefs = FModel.getPreferences(); + final boolean showTabs = prefs.getPrefBoolean(FPref.UI_HIDE_GAME_TABS); + forge.view.FView.SINGLETON_INSTANCE.refreshAllCellLayouts(showTabs); + prefs.setPref(FPref.UI_HIDE_GAME_TABS, !showTabs); + prefs.save(); + } + }; + + /** Toggle card overlays. */ + final Action actCardOverlays = new AbstractAction() { + @Override + public void actionPerformed(final ActionEvent e) { + if (!Singletons.getControl().getCurrentScreen().isMatchScreen()) { return; } + final ForgePreferences prefs = FModel.getPreferences(); + final boolean isOverlayEnabled = !prefs.getPrefBoolean(FPref.UI_SHOW_CARD_OVERLAYS); + prefs.setPref(FPref.UI_SHOW_CARD_OVERLAYS, isOverlayEnabled); + prefs.save(); + if (matchUI != null) { + matchUI.repaintCardOverlays(); + } + } + }; + + /** Show keyboard shortcuts dialog. */ + final Action actShowHotkeys = new AbstractAction() { + @Override + public void actionPerformed(final ActionEvent e) { + if (!Singletons.getControl().getCurrentScreen().isMatchScreen()) { return; } + // Defer so the triggering key event is fully consumed before the dialog opens, + // preventing it from being captured by a KeyboardShortcutField. + SwingUtilities.invokeLater(() -> new KeyboardShortcutsDialog().setVisible(true)); + } + }; + final Localizer localizer = Localizer.getInstance(); //========== Instantiate shortcut objects and add to list. list.add(new Shortcut(FPref.SHORTCUT_SHOWSTACK, localizer.getMessage("lblSHORTCUT_SHOWSTACK"), actShowStack, am, im)); list.add(new Shortcut(FPref.SHORTCUT_SHOWCOMBAT, localizer.getMessage("lblSHORTCUT_SHOWCOMBAT"), actShowCombat, am, im)); list.add(new Shortcut(FPref.SHORTCUT_SHOWCONSOLE, localizer.getMessage("lblSHORTCUT_SHOWCONSOLE"), actShowConsole, am, im)); list.add(new Shortcut(FPref.SHORTCUT_SHOWDEV, localizer.getMessage("lblSHORTCUT_SHOWDEV"), actShowDev, am, im)); + list.add(new Shortcut(FPref.SHORTCUT_UNDO, localizer.getMessage("lblSHORTCUT_UNDO"), actUndo, am, im)); list.add(new Shortcut(FPref.SHORTCUT_CONCEDE, localizer.getMessage("lblSHORTCUT_CONCEDE"), actConcede, am, im)); list.add(new Shortcut(FPref.SHORTCUT_ENDTURN, localizer.getMessage("lblSHORTCUT_ENDTURN"), actEndTurn, am, im)); list.add(new Shortcut(FPref.SHORTCUT_ALPHASTRIKE, localizer.getMessage("lblSHORTCUT_ALPHASTRIKE"), actAllAttack, am, im)); @@ -215,6 +273,10 @@ public void actionPerformed(ActionEvent e) { list.add(new Shortcut(FPref.SHORTCUT_MACRO_RECORD, localizer.getMessage("lblSHORTCUT_MACRO_RECORD"), actMacroRecord, am, im)); list.add(new Shortcut(FPref.SHORTCUT_MACRO_NEXT_ACTION, localizer.getMessage("lblSHORTCUT_MACRO_NEXT_ACTION"), actMacroNextAction, am, im)); list.add(new Shortcut(FPref.SHORTCUT_CARD_ZOOM, localizer.getMessage("lblSHORTCUT_CARD_ZOOM"), actZoomCard, am, im)); + list.add(new Shortcut(FPref.SHORTCUT_SHOWHOTKEYS, localizer.getMessage("lblSHORTCUT_SHOWHOTKEYS"), actShowHotkeys, am, im)); + list.add(new Shortcut(FPref.SHORTCUT_PANELTABS, localizer.getMessage("lblSHORTCUT_PANELTABS"), actPanelTabs, am, im)); + list.add(new Shortcut(FPref.SHORTCUT_CARDOVERLAYS, localizer.getMessage("lblSHORTCUT_CARDOVERLAYS"), actCardOverlays, am, im)); + cachedShortcuts = list; return list; } // End initMatchShortcuts() @@ -297,6 +359,18 @@ public void detach() { } } // End class Shortcut + /** + * Returns the KeyStroke currently assigned to a shortcut preference, + * or {@code null} if the preference is empty. + */ + public static KeyStroke getKeyStrokeForPref(final FPref pref) { + final String str = FModel.getPreferences().getPref(pref); + if (str == null || str.isEmpty()) { + return null; + } + return assembleKeystrokes(str.split(" ")); + } + private static KeyStroke assembleKeystrokes(final String[] keys0) { int[] inputEvents = new int[2]; int modifier = 0; diff --git a/forge-gui-desktop/src/main/java/forge/menus/HelpMenu.java b/forge-gui-desktop/src/main/java/forge/menus/HelpMenu.java index 465b035f4b0..e371d509bb6 100644 --- a/forge-gui-desktop/src/main/java/forge/menus/HelpMenu.java +++ b/forge-gui-desktop/src/main/java/forge/menus/HelpMenu.java @@ -15,6 +15,7 @@ import forge.util.BuildInfo; import forge.util.FileUtil; import forge.util.Localizer; +import forge.view.KeyboardShortcutsDialog; import static forge.localinstance.properties.ForgeConstants.GITHUB_FORGE_URL; @@ -27,6 +28,7 @@ public static JMenu getMenu() { menu.setMnemonic(KeyEvent.VK_H); menu.add(getMenu_GettingStarted()); menu.add(getMenu_Troubleshooting()); + menu.add(getMenuItem_KeyboardShortcuts()); menu.addSeparator(); menu.add(getMenuItem_ReleaseNotes()); menu.add(getMenuItem_License()); @@ -67,6 +69,13 @@ private static JMenu getMenu_GettingStarted() { return mnu; } + private static JMenuItem getMenuItem_KeyboardShortcuts() { + final Localizer localizer = Localizer.getInstance(); + JMenuItem menuItem = new JMenuItem(localizer.getMessage("lblKeyboardShortcuts")); + menuItem.addActionListener(e -> new KeyboardShortcutsDialog().setVisible(true)); + return menuItem; + } + private static JMenuItem getMenuItem_HowToPlayFile() { final Localizer localizer = Localizer.getInstance(); JMenuItem menuItem = new JMenuItem(localizer.getMessage("lblHowtoPlay")); diff --git a/forge-gui-desktop/src/main/java/forge/menus/LayoutMenu.java b/forge-gui-desktop/src/main/java/forge/menus/LayoutMenu.java index abc44897990..60296172ad5 100644 --- a/forge-gui-desktop/src/main/java/forge/menus/LayoutMenu.java +++ b/forge-gui-desktop/src/main/java/forge/menus/LayoutMenu.java @@ -14,6 +14,7 @@ import forge.Singletons; import forge.control.FControl; +import forge.control.KeyboardShortcuts; import forge.gui.GuiChoose; import forge.gui.MouseUtil; import forge.gui.framework.FScreen; @@ -134,7 +135,8 @@ private static ActionListener getShowBackgroundImageAction(final JCheckBoxMenuIt private static JMenuItem getMenuItem_ShowTabs() { final Localizer localizer = Localizer.getInstance(); final JCheckBoxMenuItem menuItem = new JCheckBoxMenuItem(localizer.getMessage("lblPanelTabs")); - menuItem.setAccelerator(MenuUtil.getAcceleratorKey(KeyEvent.VK_T)); + final KeyStroke ks = KeyboardShortcuts.getKeyStrokeForPref(FPref.SHORTCUT_PANELTABS); + if (ks != null) { menuItem.setAccelerator(ks); } menuItem.setState(!prefs.getPrefBoolean(FPref.UI_HIDE_GAME_TABS)); menuItem.addActionListener(getShowTabsAction(menuItem)); return menuItem; diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java b/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java index 7daa2778b8c..2702b975d55 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java @@ -573,7 +573,7 @@ private NoteLabel(final String txt0) { * into characters and (dis)assembly of keycode stack. */ @SuppressWarnings("serial") - public class KeyboardShortcutField extends SkinnedTextField { + public static class KeyboardShortcutField extends SkinnedTextField { private String codeString; /** @@ -648,7 +648,7 @@ public final void setCodeString(final String str0) { } } - this.setText(StringUtils.join(displayText, ' ')); + this.setText(StringUtils.join(displayText, '+')); } } diff --git a/forge-gui-desktop/src/main/java/forge/screens/match/menus/CardOverlaysMenu.java b/forge-gui-desktop/src/main/java/forge/screens/match/menus/CardOverlaysMenu.java index afbbede6095..ce5dc4e4d20 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/match/menus/CardOverlaysMenu.java +++ b/forge-gui-desktop/src/main/java/forge/screens/match/menus/CardOverlaysMenu.java @@ -2,16 +2,16 @@ import java.awt.Component; import java.awt.event.ActionListener; -import java.awt.event.KeyEvent; import javax.swing.JCheckBoxMenuItem; import javax.swing.JMenu; import javax.swing.JMenuItem; +import javax.swing.KeyStroke; import javax.swing.SwingUtilities; +import forge.control.KeyboardShortcuts; import forge.localinstance.properties.ForgePreferences; import forge.localinstance.properties.ForgePreferences.FPref; -import forge.menus.MenuUtil; import forge.model.FModel; import forge.screens.match.CMatchUI; import forge.util.Localizer; @@ -48,7 +48,8 @@ private JMenuItem getMenuItem_CardOverlay(String menuCaption, FPref pref) { private JMenuItem getMenuItem_ShowOverlays() { JCheckBoxMenuItem menu = new JCheckBoxMenuItem(Localizer.getInstance().getMessage("lblShow")); - menu.setAccelerator(MenuUtil.getAcceleratorKey(KeyEvent.VK_O)); + final KeyStroke ks = KeyboardShortcuts.getKeyStrokeForPref(FPref.SHORTCUT_CARDOVERLAYS); + if (ks != null) { menu.setAccelerator(ks); } menu.setState(prefs.getPrefBoolean(FPref.UI_SHOW_CARD_OVERLAYS)); menu.addActionListener(getShowOverlaysAction()); return menu; diff --git a/forge-gui-desktop/src/main/java/forge/screens/match/menus/GameMenu.java b/forge-gui-desktop/src/main/java/forge/screens/match/menus/GameMenu.java index 3d7db593366..2f9cd2c1cd4 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/match/menus/GameMenu.java +++ b/forge-gui-desktop/src/main/java/forge/screens/match/menus/GameMenu.java @@ -6,9 +6,11 @@ import javax.swing.ButtonGroup; import javax.swing.JMenu; import javax.swing.JPopupMenu; +import javax.swing.KeyStroke; import com.google.common.primitives.Ints; +import forge.control.KeyboardShortcuts; import forge.localinstance.properties.ForgePreferences; import forge.localinstance.properties.ForgePreferences.FPref; import forge.localinstance.skin.FSkinProp; @@ -76,7 +78,7 @@ private static void toggleGameSoundEffects() { private SkinnedMenuItem getMenuItem_Undo() { final Localizer localizer = Localizer.getInstance(); final SkinnedMenuItem menuItem = new SkinnedMenuItem(localizer.getMessage("lblUndo")); - menuItem.setAccelerator(MenuUtil.getAcceleratorKey(KeyEvent.VK_Z)); + setAcceleratorFromPref(menuItem, FPref.SHORTCUT_UNDO); menuItem.addActionListener(getUndoAction()); return menuItem; } @@ -88,7 +90,7 @@ private ActionListener getUndoAction() { private SkinnedMenuItem getMenuItem_Concede() { SkinnedMenuItem menuItem = new SkinnedMenuItem(matchUI.getConcedeCaption()); menuItem.setIcon((showIcons ? MenuUtil.getMenuIcon(FSkinProp.ICO_CONCEDE) : null)); - menuItem.setAccelerator(MenuUtil.getAcceleratorKey(KeyEvent.VK_Q)); + setAcceleratorFromPref(menuItem, FPref.SHORTCUT_CONCEDE); menuItem.addActionListener(getConcedeAction()); return menuItem; } @@ -101,7 +103,7 @@ private SkinnedMenuItem getMenuItem_AlphaStrike() { final Localizer localizer = Localizer.getInstance(); final SkinnedMenuItem menuItem = new SkinnedMenuItem(localizer.getMessage("lblAlphaStrike")); menuItem.setIcon((showIcons ? MenuUtil.getMenuIcon(FSkinProp.ICO_ALPHASTRIKE) : null)); - menuItem.setAccelerator(MenuUtil.getAcceleratorKey(KeyEvent.VK_A)); + setAcceleratorFromPref(menuItem, FPref.SHORTCUT_ALPHASTRIKE); menuItem.addActionListener(getAlphaStrikeAction()); return menuItem; } @@ -114,7 +116,7 @@ private SkinnedMenuItem getMenuItem_EndTurn() { final Localizer localizer = Localizer.getInstance(); final SkinnedMenuItem menuItem = new SkinnedMenuItem(localizer.getMessage("lblEndTurn")); menuItem.setIcon((showIcons ? MenuUtil.getMenuIcon(FSkinProp.ICO_ENDTURN) : null)); - menuItem.setAccelerator(MenuUtil.getAcceleratorKey(KeyEvent.VK_E)); + setAcceleratorFromPref(menuItem, FPref.SHORTCUT_ENDTURN); menuItem.addActionListener(getEndTurnAction()); return menuItem; } @@ -123,6 +125,14 @@ private ActionListener getEndTurnAction() { return e -> matchUI.getGameController().passPriorityUntilEndOfTurn(); } + /** Sets a menu item's accelerator display from a shortcut preference. */ + private static void setAcceleratorFromPref(final SkinnedMenuItem menuItem, final FPref pref) { + final KeyStroke ks = KeyboardShortcuts.getKeyStrokeForPref(pref); + if (ks != null) { + menuItem.setAccelerator(ks); + } + } + private SkinnedMenu getMenuItem_TargetingArcs() { final Localizer localizer = Localizer.getInstance(); final SkinnedMenu menu = new SkinnedMenu(localizer.getMessage("lblTargetingArcs")); diff --git a/forge-gui-desktop/src/main/java/forge/view/KeyboardShortcutsDialog.java b/forge-gui-desktop/src/main/java/forge/view/KeyboardShortcutsDialog.java new file mode 100644 index 00000000000..0052592aad0 --- /dev/null +++ b/forge-gui-desktop/src/main/java/forge/view/KeyboardShortcutsDialog.java @@ -0,0 +1,135 @@ +package forge.view; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Rectangle; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; + +import javax.swing.BorderFactory; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.Scrollable; +import javax.swing.ScrollPaneConstants; +import javax.swing.SwingConstants; +import javax.swing.UIManager; + +import forge.control.KeyboardShortcuts; +import forge.control.KeyboardShortcuts.Shortcut; +import forge.screens.home.settings.VSubmenuPreferences.KeyboardShortcutField; +import forge.toolbox.FLabel; +import forge.toolbox.FScrollPane; +import forge.toolbox.FSkin; +import forge.util.Localizer; +import net.miginfocom.swing.MigLayout; + +@SuppressWarnings("serial") +public class KeyboardShortcutsDialog extends FDialog { + + public KeyboardShortcutsDialog() { + super(false, true, "10"); + final Localizer localizer = Localizer.getInstance(); + setTitle(localizer.getMessage("lblKeyboardShortcuts")); + setSize(500, 600); + setMinimumSize(new Dimension(350, 300)); + + // Scrollable panel that always matches viewport width, preventing horizontal scroll. + final JPanel content = new ScrollablePanel(new MigLayout("insets 10 15 20 15, gap 5, wrap 2, fillx", "[grow][120!]")); + content.setOpaque(false); + + // Section 1: Configurable shortcuts + for (final Shortcut shortcut : KeyboardShortcuts.getCachedShortcuts()) { + final FLabel descLabel = new FLabel.Builder() + .text(shortcut.getDescription()) + .fontAlign(SwingConstants.LEFT) + .fontSize(11).build(); + content.add(descLabel, "growx"); + + final KeyboardShortcutField field = new KeyboardShortcutField(shortcut); + field.setFont(FSkin.getRelativeFont(11)); + // Clear existing binding on click so a new key press replaces it. + field.addMouseListener(new java.awt.event.MouseAdapter() { + @Override + public void mousePressed(final java.awt.event.MouseEvent e) { + field.setCodeString(""); + } + }); + // Transfer focus away once a non-modifier key completes the binding. + field.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(final KeyEvent e) { + final int code = e.getKeyCode(); + if (code != KeyEvent.VK_SHIFT && code != KeyEvent.VK_CONTROL + && code != KeyEvent.VK_ALT && code != KeyEvent.VK_META) { + content.requestFocusInWindow(); + } + } + }); + content.add(field, "w 120!, h 22!"); + } + + // Section 2: Fixed menu accelerators + final FLabel headerFixed = new FLabel.Builder() + .text(localizer.getMessage("lblMenuShortcuts")) + .fontAlign(SwingConstants.LEFT) + .fontSize(14).fontStyle(Font.BOLD).build(); + content.add(headerFixed, "span 2, gaptop 10, gapbottom 5, wrap"); + + addFixedRow(content, "Forge Wiki", KeyEvent.getKeyText(KeyEvent.VK_F1)); + addFixedRow(content, "Full Screen", KeyEvent.getKeyText(KeyEvent.VK_F11)); + + final FScrollPane scrollPane = new FScrollPane(content, false, + ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + add(scrollPane, "w 100%!, h 100%!"); + setDefaultFocus(scrollPane); + } + + private static void addFixedRow(final JPanel panel, final String description, final String keyText) { + final FLabel descLabel = new FLabel.Builder() + .text(description).fontAlign(SwingConstants.LEFT).fontSize(11).build(); + panel.add(descLabel, "growx"); + + final JLabel field = new JLabel(keyText); + field.setOpaque(true); + field.setBackground(new Color(200, 200, 200)); + field.setFont(FSkin.getRelativeFont(11).getBaseFont()); + field.setBorder(BorderFactory.createCompoundBorder( + UIManager.getBorder("TextField.border"), + BorderFactory.createEmptyBorder(0, 2, 0, 2))); + panel.add(field, "w 120!, h 22!"); + } + + /** JPanel that implements Scrollable to track viewport width, preventing horizontal scrolling. */ + private static class ScrollablePanel extends JPanel implements Scrollable { + ScrollablePanel(java.awt.LayoutManager layout) { + super(layout); + } + + @Override + public Dimension getPreferredScrollableViewportSize() { + return getPreferredSize(); + } + + @Override + public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { + return 16; + } + + @Override + public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { + return 64; + } + + @Override + public boolean getScrollableTracksViewportWidth() { + return true; + } + + @Override + public boolean getScrollableTracksViewportHeight() { + return false; + } + } +} diff --git a/forge-gui/res/languages/en-US.properties b/forge-gui/res/languages/en-US.properties index 3205a9ae2ca..a14009757ff 100644 --- a/forge-gui/res/languages/en-US.properties +++ b/forge-gui/res/languages/en-US.properties @@ -458,6 +458,7 @@ lblSHORTCUT_SHOWSTACK=Match: show stack panel lblSHORTCUT_SHOWCOMBAT=Match: show combat panel lblSHORTCUT_SHOWCONSOLE=Match: show console panel lblSHORTCUT_SHOWDEV=Match: show dev panel +lblSHORTCUT_UNDO=Match: undo last action lblSHORTCUT_CONCEDE=Match: concede game lblSHORTCUT_ENDTURN=Match: pass priority until EOT or next stack event lblSHORTCUT_ALPHASTRIKE=Match: Alpha Strike (attack with all available) @@ -467,6 +468,11 @@ lblSHORTCUT_AUTOYIELD_ALWAYS_NO=Match: auto-yield ability on stack (Always No) lblSHORTCUT_MACRO_RECORD=Match: record a macro sequence of actions lblSHORTCUT_MACRO_NEXT_ACTION=Match: execute next action in a recorded macro lblSHORTCUT_CARD_ZOOM=Match: zoom the currently selected card +lblSHORTCUT_SHOWHOTKEYS=Match: show keyboard shortcuts +lblSHORTCUT_PANELTABS=Match: toggle panel tabs +lblSHORTCUT_CARDOVERLAYS=Match: toggle card overlays +lblKeyboardShortcuts=Keyboard Shortcuts +lblMenuShortcuts=Menu Shortcuts (Not Configurable) #VSubmenuDraft.java lblBoosterDraft=Booster Draft lblHeaderBoosterDraft=Sanctioned Format: Booster Draft diff --git a/forge-gui/src/main/java/forge/localinstance/properties/ForgePreferences.java b/forge-gui/src/main/java/forge/localinstance/properties/ForgePreferences.java index 7395e63a326..fbbca057f6a 100644 --- a/forge-gui/src/main/java/forge/localinstance/properties/ForgePreferences.java +++ b/forge-gui/src/main/java/forge/localinstance/properties/ForgePreferences.java @@ -278,15 +278,19 @@ public enum FPref implements PreferencesStore.IPref { SHORTCUT_SHOWCOMBAT ("67"), SHORTCUT_SHOWCONSOLE ("76"), SHORTCUT_SHOWDEV ("68"), - SHORTCUT_CONCEDE ("17"), - SHORTCUT_ENDTURN ("69"), - SHORTCUT_ALPHASTRIKE ("65"), + SHORTCUT_UNDO ("17 90"), + SHORTCUT_CONCEDE ("17 81"), + SHORTCUT_ENDTURN ("17 69"), + SHORTCUT_ALPHASTRIKE ("17 65"), SHORTCUT_SHOWTARGETING ("84"), SHORTCUT_AUTOYIELD_ALWAYS_YES ("89"), SHORTCUT_AUTOYIELD_ALWAYS_NO ("78"), SHORTCUT_MACRO_RECORD ("16 82"), SHORTCUT_MACRO_NEXT_ACTION ("16 50"), SHORTCUT_CARD_ZOOM("90"), + SHORTCUT_SHOWHOTKEYS("72"), + SHORTCUT_PANELTABS("17 84"), + SHORTCUT_CARDOVERLAYS("17 79"), LAST_IMPORTED_CUBE_ID(""); @@ -336,6 +340,25 @@ public String getDefault() { /** Instantiates a ForgePreferences object. */ public ForgePreferences() { super(ForgeConstants.MAIN_PREFS_FILE, FPref.class); + migrateShortcutDefaults(); + } + + /** + * Migrates shortcut preferences from old defaults to new Ctrl+key defaults. + * Previously, Concede/EndTurn/AlphaStrike had configurable shortcuts that + * differed from their hardcoded menu accelerators. Now the menu accelerators + * are driven by the configurable preferences, so the defaults must match. + */ + private void migrateShortcutDefaults() { + migrateIfOldDefault(FPref.SHORTCUT_CONCEDE, "17", "17 81"); + migrateIfOldDefault(FPref.SHORTCUT_ENDTURN, "69", "17 69"); + migrateIfOldDefault(FPref.SHORTCUT_ALPHASTRIKE, "65", "17 65"); + } + + private void migrateIfOldDefault(final FPref pref, final String oldDefault, final String newDefault) { + if (oldDefault.equals(getPref(pref))) { + setPref(pref, newDefault); + } } @Override