HotkeyUtil.java

/*******************************************************************************
 * Copyhacked (H) 2012-2020.
 * This program and the accompanying materials
 * are made available under no term at all, use it like
 * you want, but share and discuss about it
 * every time possible with every body.
 * 
 * Contributors:
 *      ron190 at ymail dot com - initial implementation
 ******************************************************************************/
package com.jsql.view.swing.action;

import com.jsql.view.swing.menubar.Menubar;
import com.jsql.view.swing.util.MediatorHelper;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.util.HashSet;
import java.util.Set;

/**
 * Keyword shortcut definition. <br>
 * - ctrl TAB: switch to next tab, <br>
 * - ctrl shift TAB: switch to previous tab, <br>
 * - ctrl W: delete tab
 */
public final class HotkeyUtil {
    
    private static final String STR_CTRL_TAB = "ctrl TAB";
    private static final String STR_CTRL_SHIFT_TAB = "ctrl shift TAB";
    private static final String STR_SELECT_TAB = "actionString-selectTab";
    
    private HotkeyUtil() {
        // Utility class
    }
    
    /**
     * Select all textfield content when focused.
     */
    public static void addTextFieldShortcutSelectAll() {
        
        KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener(
            "permanentFocusOwner",
            propertyChangeEvent -> {
                if (propertyChangeEvent.getNewValue() instanceof JTextField) {
                    SwingUtilities.invokeLater(() -> {
                        
                        JTextField textField = (JTextField) propertyChangeEvent.getNewValue();
                        textField.selectAll();
                    });
                }
            }
        );
    }
    
    /**
     * Add action to a single tabbedpane (ctrl-tab, ctrl-shift-tab).
     */
    public static void addShortcut(JTabbedPane tabbedPane) {
        
        var ctrlTab = KeyStroke.getKeyStroke(STR_CTRL_TAB);
        var ctrlShiftTab = KeyStroke.getKeyStroke(STR_CTRL_SHIFT_TAB);

        // Remove ctrl-tab from normal focus traversal
        Set<AWTKeyStroke> forwardKeys = new HashSet<>(tabbedPane.getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS));
        forwardKeys.remove(ctrlTab);
        tabbedPane.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, forwardKeys);

        // Remove ctrl-shift-tab from normal focus traversal
        Set<AWTKeyStroke> backwardKeys = new HashSet<>(tabbedPane.getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS));
        backwardKeys.remove(ctrlShiftTab);
        tabbedPane.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, backwardKeys);

        // Add keys to the tab's input map
        var inputMap = tabbedPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
        inputMap.put(ctrlTab, "navigateNext");
        inputMap.put(ctrlShiftTab, "navigatePrevious");
    }
    
    /**
     * Add action to global root (ctrl-tab, ctrl-shift-tab, ctrl-W).
     */
        public static void addShortcut(JRootPane rootPane, final JTabbedPane valuesTabbedPane) {
        
        Action closeTab = new ActionCloseTabResult();
        
        Action nextTab = new AbstractAction() {
            
            @Override
            public void actionPerformed(ActionEvent e) {
                if (valuesTabbedPane.getTabCount() > 0) {
                    
                    int selectedIndex = valuesTabbedPane.getSelectedIndex();
                    
                    if (selectedIndex + 1 < valuesTabbedPane.getTabCount()) {
                        valuesTabbedPane.setSelectedIndex(selectedIndex + 1);
                    } else {
                        valuesTabbedPane.setSelectedIndex(0);
                    }
                }
            }
        };
        
        Action previousTab = new AbstractAction() {
            
            @Override
            public void actionPerformed(ActionEvent e) {
                if (valuesTabbedPane.getTabCount() > 0) {
                    
                    int selectedIndex = valuesTabbedPane.getSelectedIndex();
                    
                    if (selectedIndex - 1 > -1) {
                        valuesTabbedPane.setSelectedIndex(selectedIndex - 1);
                    } else {
                        valuesTabbedPane.setSelectedIndex(valuesTabbedPane.getTabCount() - 1);
                    }
                }
            }
        };
        
        Set<AWTKeyStroke> forwardKeys = new HashSet<>(rootPane.getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS));
        forwardKeys.remove(KeyStroke.getKeyStroke(STR_CTRL_TAB));
        rootPane.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, forwardKeys);
        
        Set<AWTKeyStroke> backwardKeys = new HashSet<>(rootPane.getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS));
        backwardKeys.remove(KeyStroke.getKeyStroke(STR_CTRL_SHIFT_TAB));
        rootPane.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, backwardKeys);
        
        var inputMap = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
        var actionMap = rootPane.getActionMap();

        inputMap.put(KeyStroke.getKeyStroke("ctrl W"), "actionString-closeTab");
        actionMap.put("actionString-closeTab", closeTab);
        
        inputMap.put(KeyStroke.getKeyStroke(STR_CTRL_TAB), "actionString-nextTab");
        actionMap.put("actionString-nextTab", nextTab);

        inputMap.put(KeyStroke.getKeyStroke(STR_CTRL_SHIFT_TAB), "actionString-previousTab");
        actionMap.put("actionString-previousTab", previousTab);
        
        int tabCount = MediatorHelper.tabManagers().getTabCount();
        
        for (var currentTab = 1 ; currentTab <= tabCount ; currentTab++) {
            
            inputMap.put(KeyStroke.getKeyStroke("ctrl "+ currentTab), STR_SELECT_TAB + currentTab);
            inputMap.put(KeyStroke.getKeyStroke("ctrl NUMPAD"+ currentTab), STR_SELECT_TAB + currentTab);
            
            final int currentTabFinal = currentTab;
            actionMap.put(STR_SELECT_TAB + currentTab, new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    MediatorHelper.tabManagers().setSelectedIndex(currentTabFinal - 1);
                }
            });
        }
        
        inputMap.put(KeyStroke.getKeyStroke("ctrl S"), "actionString-saveTab");
        actionMap.put("actionString-saveTab", new ActionSaveTab());
    }

    /**
     * Create Alt shortcut to display menubar ; remove menubar when focus is set to a component.
     * @param menubar The menubar to display
     */
    public static void addShortcut(final Menubar menubar) {
        
        /* Hide Menubar when focusing any component */
        KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener(
            "permanentFocusOwner",
            propertyChangeEvent -> SwingUtilities.invokeLater(() -> {
                if (
                    // Fix #40924: NullPointerException on MediatorGui.panelAddressBar()
                    MediatorHelper.panelAddressBar() != null
                    && !MediatorHelper.panelAddressBar().isAdvanceActivated()
                ) {
                    menubar.setVisible(false);
                }
            })
        );
        
        /* Show/Hide the Menubar with Alt key (not Alt Graph) */
        KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(
            new AltKeyEventDispatcher()
        );
    }
}