001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.widgets; 003 004import java.awt.event.FocusEvent; 005import java.awt.event.FocusListener; 006import java.awt.event.KeyEvent; 007import java.util.ArrayList; 008import java.util.HashSet; 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; 016import javax.swing.text.Document; 017 018import org.openstreetmap.josm.Main; 019import org.openstreetmap.josm.actions.JosmAction; 020import org.openstreetmap.josm.tools.Pair; 021import org.openstreetmap.josm.tools.Shortcut; 022 023/** 024 * A JTextField that disabled all JOSM shortcuts composed of a single key without modifier (except F1 to F12), 025 * in order to avoid them to be triggered while typing. 026 * This allows to include text fields in toggle dialogs (needed for relation filter). 027 * @since 5696 028 */ 029public class DisableShortcutsOnFocusGainedTextField extends JosmTextField implements FocusListener { 030 031 /** 032 * Constructs a new <code>TextField</code>. A default model is created, 033 * the initial string is <code>null</code>, 034 * and the number of columns is set to 0. 035 */ 036 public DisableShortcutsOnFocusGainedTextField() { 037 init(); 038 } 039 040 /** 041 * Constructs a new <code>TextField</code> initialized with the 042 * specified text. A default model is created and the number of 043 * columns is 0. 044 * 045 * @param text the text to be displayed, or <code>null</code> 046 */ 047 public DisableShortcutsOnFocusGainedTextField(String text) { 048 super(text); 049 init(); 050 } 051 052 /** 053 * Constructs a new empty <code>TextField</code> with the specified 054 * number of columns. 055 * A default model is created and the initial string is set to 056 * <code>null</code>. 057 * 058 * @param columns the number of columns to use to calculate 059 * the preferred width; if columns is set to zero, the 060 * preferred width will be whatever naturally results from 061 * the component implementation 062 */ 063 public DisableShortcutsOnFocusGainedTextField(int columns) { 064 super(columns); 065 init(); 066 } 067 068 /** 069 * Constructs a new <code>TextField</code> initialized with the 070 * specified text and columns. A default model is created. 071 * 072 * @param text the text to be displayed, or <code>null</code> 073 * @param columns the number of columns to use to calculate 074 * the preferred width; if columns is set to zero, the 075 * preferred width will be whatever naturally results from 076 * the component implementation 077 */ 078 public DisableShortcutsOnFocusGainedTextField(String text, int columns) { 079 super(text, columns); 080 init(); 081 } 082 083 /** 084 * Constructs a new <code>JTextField</code> that uses the given text 085 * storage model and the given number of columns. 086 * This is the constructor through which the other constructors feed. 087 * If the document is <code>null</code>, a default model is created. 088 * 089 * @param doc the text storage to use; if this is <code>null</code>, 090 * a default will be provided by calling the 091 * <code>createDefaultModel</code> method 092 * @param text the initial string to display, or <code>null</code> 093 * @param columns the number of columns to use to calculate 094 * the preferred width >= 0; if <code>columns</code> 095 * is set to zero, the preferred width will be whatever 096 * naturally results from the component implementation 097 * @exception IllegalArgumentException if <code>columns</code> < 0 098 */ 099 public DisableShortcutsOnFocusGainedTextField(Document doc, String text, int columns) { 100 super(doc, text, columns); 101 init(); 102 } 103 104 private final List<Pair<Action,Shortcut>> unregisteredActionShortcuts = new ArrayList<>(); 105 private final Set<JosmAction> disabledMenuActions = new HashSet<>(); 106 107 protected final void init() { 108 addFocusListener(this); 109 } 110 111 @Override 112 public void focusGained(FocusEvent e) { 113 disableMenuActions(); 114 unregisterActionShortcuts(); 115 } 116 117 @Override 118 public void focusLost(FocusEvent e) { 119 restoreActionShortcuts(); 120 restoreMenuActions(); 121 } 122 123 /** 124 * Disables all relevant menu actions. 125 * @see #hasToBeDisabled 126 */ 127 protected void disableMenuActions() { 128 disabledMenuActions.clear(); 129 for (int i = 0; i < Main.main.menu.getMenuCount(); i++) { 130 JMenu menu = Main.main.menu.getMenu(i); 131 if (menu != null) { 132 for (int j = 0; j < menu.getItemCount(); j++) { 133 JMenuItem item = menu.getItem(j); 134 if (item != null) { 135 Action action = item.getAction(); 136 if (action instanceof JosmAction && action.isEnabled()) { 137 Shortcut shortcut = ((JosmAction) action).getShortcut(); 138 if (shortcut != null) { 139 KeyStroke ks = shortcut.getKeyStroke(); 140 if (hasToBeDisabled(ks)) { 141 action.setEnabled(false); 142 disabledMenuActions.add((JosmAction) action); 143 } 144 } 145 } 146 } 147 } 148 } 149 } 150 } 151 152 /** 153 * Unregisters all relevant action shortcuts. 154 * @see #hasToBeDisabled 155 */ 156 protected void unregisterActionShortcuts() { 157 unregisteredActionShortcuts.clear(); 158 // Unregister all actions without modifiers to avoid them to be triggered by typing in this text field 159 for (Shortcut shortcut : Shortcut.listAll()) { 160 KeyStroke ks = shortcut.getKeyStroke(); 161 if (hasToBeDisabled(ks)) { 162 Action action = Main.getRegisteredActionShortcut(shortcut); 163 if (action != null) { 164 Main.unregisterActionShortcut(action, shortcut); 165 unregisteredActionShortcuts.add(new Pair<>(action,shortcut)); 166 } 167 } 168 } 169 } 170 171 /** 172 * Returns true if the given shortcut has no modifier and is not an actions key. 173 * @see KeyEvent#isActionKey() 174 */ 175 protected boolean hasToBeDisabled(KeyStroke ks) { 176 return ks != null && ks.getModifiers() == 0 && !new KeyEvent( 177 this, KeyEvent.KEY_PRESSED, 0, ks.getModifiers(), ks.getKeyCode(), ks.getKeyChar()).isActionKey(); 178 } 179 180 /** 181 * Restore all actions previously disabled 182 */ 183 protected void restoreMenuActions() { 184 for (JosmAction a : disabledMenuActions) { 185 a.setEnabled(true); 186 } 187 disabledMenuActions.clear(); 188 } 189 190 /** 191 * Restore all action shortcuts previously unregistered 192 */ 193 protected void restoreActionShortcuts() { 194 for (Pair<Action,Shortcut> p : unregisteredActionShortcuts) { 195 Main.registerActionShortcut(p.a, p.b); 196 } 197 unregisteredActionShortcuts.clear(); 198 } 199}