001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.widgets; 003 004import java.awt.Component; 005import java.awt.event.FocusEvent; 006import java.awt.event.FocusListener; 007import java.awt.event.InputEvent; 008import java.awt.event.KeyEvent; 009import java.util.List; 010import java.util.Set; 011 012import javax.swing.Action; 013import javax.swing.JMenu; 014import javax.swing.JMenuItem; 015import javax.swing.KeyStroke; 016 017import org.openstreetmap.josm.actions.JosmAction; 018import org.openstreetmap.josm.gui.MainApplication; 019import org.openstreetmap.josm.tools.Pair; 020import org.openstreetmap.josm.tools.Shortcut; 021 022/** 023 * An interface for components with code that can be used for disabling shortcuts while they hold focus. 024 * 025 * @author Taylor Smock 026 * @since 18285 (code extracted for {@link DisableShortcutsOnFocusGainedTextField} 027 */ 028public interface DisableShortcutsOnFocusGainedComponent extends FocusListener { 029 030 @Override 031 default void focusGained(FocusEvent e) { 032 disableMenuActions(); 033 unregisterActionShortcuts(); 034 } 035 036 @Override 037 default void focusLost(FocusEvent e) { 038 restoreActionShortcuts(); 039 restoreMenuActions(); 040 } 041 042 /** 043 * Get the unregistered action shortcuts. 044 * This should not be used outside the {@link DisableShortcutsOnFocusGainedComponent} interface. 045 * @return The list of unregistered action shortcuts (modifiable) 046 */ 047 List<Pair<Action, Shortcut>> getUnregisteredActionShortcuts(); 048 049 /** 050 * Get the disabled menu action list 051 * This should not be used outside the {@link DisableShortcutsOnFocusGainedComponent} interface. 052 * @return The list of disabled menu actions (modifiable) 053 */ 054 Set<JosmAction> getDisabledMenuActions(); 055 056 /** 057 * Disables all relevant menu actions. 058 * Note: This was protected 059 * @see #hasToBeDisabled 060 */ 061 default void disableMenuActions() { 062 getDisabledMenuActions().clear(); 063 for (int i = 0; i < MainApplication.getMenu().getMenuCount(); i++) { 064 JMenu menu = MainApplication.getMenu().getMenu(i); 065 if (menu != null) { 066 for (int j = 0; j < menu.getItemCount(); j++) { 067 JMenuItem item = menu.getItem(j); 068 if (item != null) { 069 Action action = item.getAction(); 070 if (action instanceof JosmAction && action.isEnabled()) { 071 Shortcut shortcut = ((JosmAction) action).getShortcut(); 072 if (shortcut != null) { 073 KeyStroke ks = shortcut.getKeyStroke(); 074 if (hasToBeDisabled(ks)) { 075 action.setEnabled(false); 076 getDisabledMenuActions().add((JosmAction) action); 077 } 078 } 079 } 080 } 081 } 082 } 083 } 084 } 085 086 /** 087 * Unregisters all relevant action shortcuts. 088 * Note: This was protected 089 * @see #hasToBeDisabled 090 */ 091 default void unregisterActionShortcuts() { 092 getUnregisteredActionShortcuts().clear(); 093 // Unregister all actions with Shift modifier or without modifiers to avoid them to be triggered by typing in this text field 094 for (Shortcut shortcut : Shortcut.listAll()) { 095 KeyStroke ks = shortcut.getKeyStroke(); 096 if (hasToBeDisabled(ks)) { 097 Action action = MainApplication.getRegisteredActionShortcut(shortcut); 098 if (action != null) { 099 MainApplication.unregisterActionShortcut(action, shortcut); 100 getUnregisteredActionShortcuts().add(new Pair<>(action, shortcut)); 101 } 102 } 103 } 104 } 105 106 /** 107 * Returns true if the given shortcut has Shift modifier or no modifier and is not an actions key. 108 * Note: This was protected 109 * @param ks key stroke 110 * @return {@code true} if the given shortcut has to be disabled 111 * @see KeyEvent#isActionKey() 112 */ 113 default boolean hasToBeDisabled(KeyStroke ks) { 114 if (this instanceof Component) { 115 return ks != null && (ks.getModifiers() == 0 || isOnlyShift(ks.getModifiers())) && !new KeyEvent((Component) this, 116 KeyEvent.KEY_PRESSED, 0, ks.getModifiers(), ks.getKeyCode(), ks.getKeyChar()).isActionKey(); 117 } 118 throw new UnsupportedOperationException(this.getClass().getSimpleName() + " is not an instanceof Component"); 119 } 120 121 /** 122 * Check if the modifiers is only shift 123 * Note: This was private 124 * @param modifiers The modifiers to check 125 * @return {@code true} if the only modifier is {@link InputEvent#SHIFT_DOWN_MASK} 126 */ 127 static boolean isOnlyShift(int modifiers) { 128 return (modifiers & InputEvent.SHIFT_DOWN_MASK) != 0 129 && (modifiers & InputEvent.CTRL_DOWN_MASK) == 0 130 && (modifiers & InputEvent.ALT_DOWN_MASK) == 0 131 && (modifiers & InputEvent.ALT_GRAPH_DOWN_MASK) == 0 132 && (modifiers & InputEvent.META_DOWN_MASK) == 0; 133 } 134 135 /** 136 * Restore all actions previously disabled 137 * Note: This was protected 138 */ 139 default void restoreMenuActions() { 140 for (JosmAction a : getDisabledMenuActions()) { 141 a.setEnabled(true); 142 } 143 getDisabledMenuActions().clear(); 144 } 145 146 /** 147 * Restore all action shortcuts previously unregistered 148 * Note: This was protected 149 */ 150 default void restoreActionShortcuts() { 151 for (Pair<Action, Shortcut> p : getUnregisteredActionShortcuts()) { 152 MainApplication.registerActionShortcut(p.a, p.b); 153 } 154 getUnregisteredActionShortcuts().clear(); 155 } 156}