HotkeyUtil.java

1
/*******************************************************************************
2
 * Copyhacked (H) 2012-2025.
3
 * This program and the accompanying materials
4
 * are made available under no term at all, use it like
5
 * you want, but share and discuss it
6
 * every time possible with every body.
7
 * 
8
 * Contributors:
9
 *      ron190 at ymail dot com - initial implementation
10
 ******************************************************************************/
11
package com.jsql.view.swing.action;
12
13
import com.jsql.view.swing.menubar.AppMenubar;
14
import com.jsql.view.swing.util.MediatorHelper;
15
16
import javax.swing.*;
17
import java.awt.*;
18
import java.awt.event.ActionEvent;
19
import java.util.HashSet;
20
import java.util.Set;
21
22
/**
23
 * Keyword shortcut definition. <br>
24
 * - ctrl TAB: switch to next tab, <br>
25
 * - ctrl shift TAB: switch to previous tab, <br>
26
 * - ctrl W: delete tab
27
 */
28
public final class HotkeyUtil {
29
    
30
    private static final String STR_CTRL_TAB = "ctrl TAB";
31
    private static final String STR_CTRL_SHIFT_TAB = "ctrl shift TAB";
32
    private static final String STR_SELECT_TAB = "actionString-selectTab";
33
    
34
    private HotkeyUtil() {
35
        // Utility class
36
    }
37
    
38
    /**
39
     * Select all textfield content when focused.
40
     */
41
    public static void addTextFieldShortcutSelectAll() {
42 1 1. addTextFieldShortcutSelectAll : removed call to java/awt/KeyboardFocusManager::addPropertyChangeListener → NO_COVERAGE
        KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener(
43
            "permanentFocusOwner",
44
            propertyChangeEvent -> {
45 1 1. lambda$addTextFieldShortcutSelectAll$0 : negated conditional → NO_COVERAGE
                if (propertyChangeEvent.getNewValue() instanceof JTextField textField) {
46 1 1. lambda$addTextFieldShortcutSelectAll$0 : removed call to javax/swing/SwingUtilities::invokeLater → NO_COVERAGE
                    SwingUtilities.invokeLater(textField::selectAll);
47
                }
48
            }
49
        );
50
    }
51
    
52
    /**
53
     * Add action to a single tabbedpane (ctrl-tab, ctrl-shift-tab).
54
     */
55
    public static void addShortcut(JTabbedPane tabbedPane) {
56
        var ctrlTab = KeyStroke.getKeyStroke(HotkeyUtil.STR_CTRL_TAB);
57
        var ctrlShiftTab = KeyStroke.getKeyStroke(HotkeyUtil.STR_CTRL_SHIFT_TAB);
58
59
        // Remove ctrl-tab from default focus traversal
60
        Set<AWTKeyStroke> forwardKeys = new HashSet<>(tabbedPane.getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS));
61
        forwardKeys.remove(ctrlTab);
62 1 1. addShortcut : removed call to javax/swing/JTabbedPane::setFocusTraversalKeys → NO_COVERAGE
        tabbedPane.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, forwardKeys);
63
64
        // Remove ctrl-shift-tab from default focus traversal
65
        Set<AWTKeyStroke> backwardKeys = new HashSet<>(tabbedPane.getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS));
66
        backwardKeys.remove(ctrlShiftTab);
67 1 1. addShortcut : removed call to javax/swing/JTabbedPane::setFocusTraversalKeys → NO_COVERAGE
        tabbedPane.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, backwardKeys);
68
69
        // Add keys to the tab's input map
70
        var inputMap = tabbedPane.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
71 1 1. addShortcut : removed call to javax/swing/InputMap::put → NO_COVERAGE
        inputMap.put(ctrlTab, "navigateNext");
72 1 1. addShortcut : removed call to javax/swing/InputMap::put → NO_COVERAGE
        inputMap.put(ctrlShiftTab, "navigatePrevious");
73
    }
74
    
75
    /**
76
     * Add action to global root (ctrl-tab, ctrl-shift-tab, ctrl-W).
77
     */
78
    public static void addShortcut(JRootPane rootPane, final JTabbedPane valuesTabbedPane) {
79
        Action closeTab = new ActionCloseTabResult();
80
        Action nextTab = new AbstractAction() {
81
            @Override
82
            public void actionPerformed(ActionEvent e) {
83 2 1. actionPerformed : changed conditional boundary → NO_COVERAGE
2. actionPerformed : negated conditional → NO_COVERAGE
                if (valuesTabbedPane.getTabCount() > 0) {
84
                    int selectedIndex = valuesTabbedPane.getSelectedIndex();
85 3 1. actionPerformed : Replaced integer addition with subtraction → NO_COVERAGE
2. actionPerformed : changed conditional boundary → NO_COVERAGE
3. actionPerformed : negated conditional → NO_COVERAGE
                    if (selectedIndex + 1 < valuesTabbedPane.getTabCount()) {
86 2 1. actionPerformed : Replaced integer addition with subtraction → NO_COVERAGE
2. actionPerformed : removed call to javax/swing/JTabbedPane::setSelectedIndex → NO_COVERAGE
                        valuesTabbedPane.setSelectedIndex(selectedIndex + 1);
87
                    } else {
88 1 1. actionPerformed : removed call to javax/swing/JTabbedPane::setSelectedIndex → NO_COVERAGE
                        valuesTabbedPane.setSelectedIndex(0);
89
                    }
90
                }
91
            }
92
        };
93
        Action previousTab = new AbstractAction() {
94
            @Override
95
            public void actionPerformed(ActionEvent e) {
96 2 1. actionPerformed : changed conditional boundary → NO_COVERAGE
2. actionPerformed : negated conditional → NO_COVERAGE
                if (valuesTabbedPane.getTabCount() > 0) {
97
                    int selectedIndex = valuesTabbedPane.getSelectedIndex();
98 3 1. actionPerformed : Replaced integer subtraction with addition → NO_COVERAGE
2. actionPerformed : changed conditional boundary → NO_COVERAGE
3. actionPerformed : negated conditional → NO_COVERAGE
                    if (selectedIndex - 1 > -1) {
99 2 1. actionPerformed : removed call to javax/swing/JTabbedPane::setSelectedIndex → NO_COVERAGE
2. actionPerformed : Replaced integer subtraction with addition → NO_COVERAGE
                        valuesTabbedPane.setSelectedIndex(selectedIndex - 1);
100
                    } else {
101 2 1. actionPerformed : Replaced integer subtraction with addition → NO_COVERAGE
2. actionPerformed : removed call to javax/swing/JTabbedPane::setSelectedIndex → NO_COVERAGE
                        valuesTabbedPane.setSelectedIndex(valuesTabbedPane.getTabCount() - 1);
102
                    }
103
                }
104
            }
105
        };
106
        
107
        Set<AWTKeyStroke> forwardKeys = new HashSet<>(rootPane.getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS));
108
        forwardKeys.remove(KeyStroke.getKeyStroke(HotkeyUtil.STR_CTRL_TAB));
109 1 1. addShortcut : removed call to javax/swing/JRootPane::setFocusTraversalKeys → NO_COVERAGE
        rootPane.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, forwardKeys);
110
        
111
        Set<AWTKeyStroke> backwardKeys = new HashSet<>(rootPane.getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS));
112
        backwardKeys.remove(KeyStroke.getKeyStroke(HotkeyUtil.STR_CTRL_SHIFT_TAB));
113 1 1. addShortcut : removed call to javax/swing/JRootPane::setFocusTraversalKeys → NO_COVERAGE
        rootPane.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, backwardKeys);
114
        
115
        var inputMap = rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
116
        var actionMap = rootPane.getActionMap();
117
118 1 1. addShortcut : removed call to javax/swing/InputMap::put → NO_COVERAGE
        inputMap.put(KeyStroke.getKeyStroke("ctrl W"), "actionString-closeTab");
119 1 1. addShortcut : removed call to javax/swing/ActionMap::put → NO_COVERAGE
        actionMap.put("actionString-closeTab", closeTab);
120
        
121 1 1. addShortcut : removed call to javax/swing/InputMap::put → NO_COVERAGE
        inputMap.put(KeyStroke.getKeyStroke(HotkeyUtil.STR_CTRL_TAB), "actionString-nextTab");
122 1 1. addShortcut : removed call to javax/swing/ActionMap::put → NO_COVERAGE
        actionMap.put("actionString-nextTab", nextTab);
123
124 1 1. addShortcut : removed call to javax/swing/InputMap::put → NO_COVERAGE
        inputMap.put(KeyStroke.getKeyStroke(HotkeyUtil.STR_CTRL_SHIFT_TAB), "actionString-previousTab");
125 1 1. addShortcut : removed call to javax/swing/ActionMap::put → NO_COVERAGE
        actionMap.put("actionString-previousTab", previousTab);
126
127
        int tabCount = MediatorHelper.tabManagersCards().getComponentCount();
128
        
129 2 1. addShortcut : changed conditional boundary → NO_COVERAGE
2. addShortcut : negated conditional → NO_COVERAGE
        for (var currentTab = 1 ; currentTab <= tabCount ; currentTab++) {
130 1 1. addShortcut : removed call to javax/swing/InputMap::put → NO_COVERAGE
            inputMap.put(KeyStroke.getKeyStroke("ctrl "+ currentTab), HotkeyUtil.STR_SELECT_TAB + currentTab);
131 1 1. addShortcut : removed call to javax/swing/InputMap::put → NO_COVERAGE
            inputMap.put(KeyStroke.getKeyStroke("ctrl NUMPAD"+ currentTab), HotkeyUtil.STR_SELECT_TAB + currentTab);
132
            
133
            final int currentTabFinal = currentTab;
134 1 1. addShortcut : removed call to javax/swing/ActionMap::put → NO_COVERAGE
            actionMap.put(HotkeyUtil.STR_SELECT_TAB + currentTab, new AbstractAction() {
135
                @Override
136
                public void actionPerformed(ActionEvent e) {
137 2 1. actionPerformed : Replaced integer subtraction with addition → NO_COVERAGE
2. actionPerformed : removed call to com/jsql/view/swing/tab/TabManagers::setSelectedIndex → NO_COVERAGE
                    MediatorHelper.frame().getTabManagers().setSelectedIndex(currentTabFinal - 1);
138
                }
139
            });
140
        }
141
        
142 1 1. addShortcut : removed call to javax/swing/InputMap::put → NO_COVERAGE
        inputMap.put(KeyStroke.getKeyStroke("ctrl S"), "actionString-saveTab");
143 1 1. addShortcut : removed call to javax/swing/ActionMap::put → NO_COVERAGE
        actionMap.put("actionString-saveTab", new ActionSaveTab());
144
    }
145
146
    /**
147
     * Create Alt shortcut to display menubar ; remove menubar when focus is set to a component.
148
     * @param appMenubar The menubar to display
149
     */
150
    public static void addShortcut(final AppMenubar appMenubar) {
151 1 1. addShortcut : removed call to java/awt/KeyboardFocusManager::addPropertyChangeListener → NO_COVERAGE
        KeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener(  // Hide Menubar when focusing any component
152
            "permanentFocusOwner",
153 1 1. lambda$addShortcut$2 : removed call to javax/swing/SwingUtilities::invokeLater → NO_COVERAGE
            propertyChangeEvent -> SwingUtilities.invokeLater(() -> {
154
                if (
155
                    // Fix #40924: NullPointerException on MediatorGui.panelAddressBar()
156 1 1. lambda$addShortcut$1 : negated conditional → NO_COVERAGE
                    MediatorHelper.panelAddressBar() != null
157 1 1. lambda$addShortcut$1 : negated conditional → NO_COVERAGE
                    && MediatorHelper.panelAddressBar().isAdvanceActivated()
158
                ) {
159 1 1. lambda$addShortcut$1 : removed call to com/jsql/view/swing/menubar/AppMenubar::setVisible → NO_COVERAGE
                    appMenubar.setVisible(false);
160
                }
161
            })
162
        );
163 1 1. addShortcut : removed call to java/awt/KeyboardFocusManager::addKeyEventDispatcher → NO_COVERAGE
        KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(  // Show/Hide the Menubar with Alt key (not Alt Graph)
164
            new AltKeyEventDispatcher()
165
        );
166
    }
167
}

Mutations

42

1.1
Location : addTextFieldShortcutSelectAll
Killed by : none
removed call to java/awt/KeyboardFocusManager::addPropertyChangeListener → NO_COVERAGE

45

1.1
Location : lambda$addTextFieldShortcutSelectAll$0
Killed by : none
negated conditional → NO_COVERAGE

46

1.1
Location : lambda$addTextFieldShortcutSelectAll$0
Killed by : none
removed call to javax/swing/SwingUtilities::invokeLater → NO_COVERAGE

62

1.1
Location : addShortcut
Killed by : none
removed call to javax/swing/JTabbedPane::setFocusTraversalKeys → NO_COVERAGE

67

1.1
Location : addShortcut
Killed by : none
removed call to javax/swing/JTabbedPane::setFocusTraversalKeys → NO_COVERAGE

71

1.1
Location : addShortcut
Killed by : none
removed call to javax/swing/InputMap::put → NO_COVERAGE

72

1.1
Location : addShortcut
Killed by : none
removed call to javax/swing/InputMap::put → NO_COVERAGE

83

1.1
Location : actionPerformed
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : actionPerformed
Killed by : none
negated conditional → NO_COVERAGE

85

1.1
Location : actionPerformed
Killed by : none
Replaced integer addition with subtraction → NO_COVERAGE

2.2
Location : actionPerformed
Killed by : none
changed conditional boundary → NO_COVERAGE

3.3
Location : actionPerformed
Killed by : none
negated conditional → NO_COVERAGE

86

1.1
Location : actionPerformed
Killed by : none
Replaced integer addition with subtraction → NO_COVERAGE

2.2
Location : actionPerformed
Killed by : none
removed call to javax/swing/JTabbedPane::setSelectedIndex → NO_COVERAGE

88

1.1
Location : actionPerformed
Killed by : none
removed call to javax/swing/JTabbedPane::setSelectedIndex → NO_COVERAGE

96

1.1
Location : actionPerformed
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : actionPerformed
Killed by : none
negated conditional → NO_COVERAGE

98

1.1
Location : actionPerformed
Killed by : none
Replaced integer subtraction with addition → NO_COVERAGE

2.2
Location : actionPerformed
Killed by : none
changed conditional boundary → NO_COVERAGE

3.3
Location : actionPerformed
Killed by : none
negated conditional → NO_COVERAGE

99

1.1
Location : actionPerformed
Killed by : none
removed call to javax/swing/JTabbedPane::setSelectedIndex → NO_COVERAGE

2.2
Location : actionPerformed
Killed by : none
Replaced integer subtraction with addition → NO_COVERAGE

101

1.1
Location : actionPerformed
Killed by : none
Replaced integer subtraction with addition → NO_COVERAGE

2.2
Location : actionPerformed
Killed by : none
removed call to javax/swing/JTabbedPane::setSelectedIndex → NO_COVERAGE

109

1.1
Location : addShortcut
Killed by : none
removed call to javax/swing/JRootPane::setFocusTraversalKeys → NO_COVERAGE

113

1.1
Location : addShortcut
Killed by : none
removed call to javax/swing/JRootPane::setFocusTraversalKeys → NO_COVERAGE

118

1.1
Location : addShortcut
Killed by : none
removed call to javax/swing/InputMap::put → NO_COVERAGE

119

1.1
Location : addShortcut
Killed by : none
removed call to javax/swing/ActionMap::put → NO_COVERAGE

121

1.1
Location : addShortcut
Killed by : none
removed call to javax/swing/InputMap::put → NO_COVERAGE

122

1.1
Location : addShortcut
Killed by : none
removed call to javax/swing/ActionMap::put → NO_COVERAGE

124

1.1
Location : addShortcut
Killed by : none
removed call to javax/swing/InputMap::put → NO_COVERAGE

125

1.1
Location : addShortcut
Killed by : none
removed call to javax/swing/ActionMap::put → NO_COVERAGE

129

1.1
Location : addShortcut
Killed by : none
changed conditional boundary → NO_COVERAGE

2.2
Location : addShortcut
Killed by : none
negated conditional → NO_COVERAGE

130

1.1
Location : addShortcut
Killed by : none
removed call to javax/swing/InputMap::put → NO_COVERAGE

131

1.1
Location : addShortcut
Killed by : none
removed call to javax/swing/InputMap::put → NO_COVERAGE

134

1.1
Location : addShortcut
Killed by : none
removed call to javax/swing/ActionMap::put → NO_COVERAGE

137

1.1
Location : actionPerformed
Killed by : none
Replaced integer subtraction with addition → NO_COVERAGE

2.2
Location : actionPerformed
Killed by : none
removed call to com/jsql/view/swing/tab/TabManagers::setSelectedIndex → NO_COVERAGE

142

1.1
Location : addShortcut
Killed by : none
removed call to javax/swing/InputMap::put → NO_COVERAGE

143

1.1
Location : addShortcut
Killed by : none
removed call to javax/swing/ActionMap::put → NO_COVERAGE

151

1.1
Location : addShortcut
Killed by : none
removed call to java/awt/KeyboardFocusManager::addPropertyChangeListener → NO_COVERAGE

153

1.1
Location : lambda$addShortcut$2
Killed by : none
removed call to javax/swing/SwingUtilities::invokeLater → NO_COVERAGE

156

1.1
Location : lambda$addShortcut$1
Killed by : none
negated conditional → NO_COVERAGE

157

1.1
Location : lambda$addShortcut$1
Killed by : none
negated conditional → NO_COVERAGE

159

1.1
Location : lambda$addShortcut$1
Killed by : none
removed call to com/jsql/view/swing/menubar/AppMenubar::setVisible → NO_COVERAGE

163

1.1
Location : addShortcut
Killed by : none
removed call to java/awt/KeyboardFocusManager::addKeyEventDispatcher → NO_COVERAGE

Active mutators

Tests examined


Report generated by PIT 1.22.1