Lines 41-60
Link Here
|
41 |
|
41 |
|
42 |
package org.netbeans.core; |
42 |
package org.netbeans.core; |
43 |
|
43 |
|
|
|
44 |
import java.awt.EventQueue; |
45 |
import java.awt.Toolkit; |
44 |
import java.awt.event.ActionEvent; |
46 |
import java.awt.event.ActionEvent; |
45 |
import java.awt.event.KeyEvent; |
47 |
import java.awt.event.KeyEvent; |
46 |
import java.util.ArrayList; |
48 |
import java.util.ArrayList; |
47 |
import java.util.Arrays; |
|
|
48 |
import java.util.Collections; |
49 |
import java.util.Collections; |
49 |
import java.util.Comparator; |
50 |
import java.util.Comparator; |
50 |
import java.util.HashMap; |
51 |
import java.util.HashMap; |
51 |
import java.util.HashSet; |
|
|
52 |
import java.util.Iterator; |
53 |
import java.util.List; |
52 |
import java.util.List; |
54 |
import java.util.Map; |
53 |
import java.util.Map; |
55 |
import java.util.Map.Entry; |
54 |
import java.util.TreeMap; |
56 |
import java.util.Observable; |
55 |
import java.util.WeakHashMap; |
57 |
import java.util.Set; |
|
|
58 |
import java.util.logging.Level; |
56 |
import java.util.logging.Level; |
59 |
import java.util.logging.Logger; |
57 |
import java.util.logging.Logger; |
60 |
import javax.swing.AbstractAction; |
58 |
import javax.swing.AbstractAction; |
Lines 62-100
Link Here
|
62 |
import javax.swing.KeyStroke; |
60 |
import javax.swing.KeyStroke; |
63 |
import javax.swing.text.Keymap; |
61 |
import javax.swing.text.Keymap; |
64 |
import org.openide.awt.StatusDisplayer; |
62 |
import org.openide.awt.StatusDisplayer; |
65 |
import org.openide.util.Mutex; |
63 |
import org.openide.cookies.InstanceCookie; |
|
|
64 |
import org.openide.filesystems.FileAttributeEvent; |
65 |
import org.openide.filesystems.FileChangeAdapter; |
66 |
import org.openide.filesystems.FileChangeListener; |
67 |
import org.openide.filesystems.FileEvent; |
68 |
import org.openide.filesystems.FileObject; |
69 |
import org.openide.filesystems.FileRenameEvent; |
70 |
import org.openide.filesystems.FileUtil; |
71 |
import org.openide.loaders.DataObject; |
72 |
import org.openide.loaders.DataObjectNotFoundException; |
73 |
import org.openide.loaders.DataShadow; |
74 |
import org.openide.util.Utilities; |
66 |
import org.openide.util.actions.SystemAction; |
75 |
import org.openide.util.actions.SystemAction; |
67 |
import org.openide.util.lookup.ServiceProvider; |
76 |
import org.openide.util.lookup.ServiceProvider; |
68 |
|
77 |
|
69 |
/** Implementation of standard key - action mappings. |
|
|
70 |
* |
71 |
* @author Dafe Simonek |
72 |
*/ |
73 |
@ServiceProvider(service=Keymap.class) |
78 |
@ServiceProvider(service=Keymap.class) |
74 |
public final class NbKeymap extends Observable implements Keymap, Comparator<KeyStroke> { |
79 |
public final class NbKeymap implements Keymap, Comparator<KeyStroke> { |
75 |
/** Name of this keymap */ |
80 |
|
76 |
String name; |
81 |
private static final Action BROKEN = new AbstractAction("<broken>") { // NOI18N |
77 |
/** Parent keymap */ |
82 |
public void actionPerformed(ActionEvent e) { |
78 |
Keymap parent; |
83 |
Toolkit.getDefaultToolkit().beep(); |
79 |
/** Hashtable holding KeyStroke > Action mappings */ |
84 |
} |
80 |
Map<KeyStroke,Action> bindings; |
85 |
}; |
81 |
/** Default action */ |
86 |
|
82 |
Action defaultAction; |
87 |
/** Represents a binding of a keystroke. */ |
83 |
/** hash table to map (Action -> ArrayList of KeyStrokes) */ |
88 |
private static class Binding { |
84 |
Map<Action,List<KeyStroke>> actions; |
89 |
/** file defining an action; null if nested is not null */ |
|
|
90 |
final FileObject actionDefinition; |
91 |
/** lazily instantiated actual action, in case actionDefinition is not null */ |
92 |
private Action action; |
93 |
/** nested bindings; null if actionDefinition is not null */ |
94 |
final Map<KeyStroke,Binding> nested; |
95 |
Binding(FileObject def) { |
96 |
actionDefinition = def; |
97 |
nested = null; |
98 |
} |
99 |
Binding() { |
100 |
actionDefinition = null; |
101 |
nested = new HashMap<KeyStroke,Binding>(); |
102 |
} |
103 |
synchronized Action loadAction() { |
104 |
assert actionDefinition != null; |
105 |
if (action == null) { |
106 |
try { |
107 |
DataObject d = DataObject.find(actionDefinition); |
108 |
InstanceCookie ic = d.getLookup().lookup(InstanceCookie.class); |
109 |
if (ic == null) { |
110 |
return null; |
111 |
} |
112 |
action = (Action) ic.instanceCreate(); |
113 |
} catch (/*ClassNotFoundException,IOException,ClassCastException*/Exception x) { |
114 |
LOG.log(Level.INFO, "could not load action for " + actionDefinition.getPath(), x); |
115 |
} |
116 |
} |
117 |
if (action == null) { |
118 |
action = BROKEN; |
119 |
} |
120 |
return action; |
121 |
} |
122 |
} |
123 |
|
124 |
private Map<KeyStroke,Binding> bindings; |
125 |
private Map<String,KeyStroke> id2Stroke; |
126 |
private final Map<Action,String> action2Id = new WeakHashMap<Action,String>(); |
127 |
private FileChangeListener keymapListener; |
128 |
private FileChangeListener bindingsListener = new FileChangeAdapter() { |
129 |
public @Override void fileDataCreated(FileEvent fe) { |
130 |
refreshBindings(); |
131 |
} |
132 |
public @Override void fileAttributeChanged(FileAttributeEvent fe) { |
133 |
refreshBindings(); |
134 |
} |
135 |
public @Override void fileChanged(FileEvent fe) { |
136 |
refreshBindings(); |
137 |
} |
138 |
public @Override void fileRenamed(FileRenameEvent fe) { |
139 |
refreshBindings(); |
140 |
} |
141 |
public @Override void fileDeleted(FileEvent fe) { |
142 |
refreshBindings(); |
143 |
} |
144 |
}; |
145 |
|
146 |
private synchronized void refreshBindings() { |
147 |
bindings = null; |
148 |
bindings(); |
149 |
} |
150 |
|
151 |
private synchronized Map<KeyStroke,Binding> bindings() { |
152 |
if (bindings == null) { |
153 |
bindings = new HashMap<KeyStroke,Binding>(); |
154 |
boolean refresh = id2Stroke != null; |
155 |
id2Stroke = new TreeMap<String,KeyStroke>(); |
156 |
List<FileObject> dirs = new ArrayList<FileObject>(2); |
157 |
dirs.add(FileUtil.getConfigFile("Shortcuts")); // NOI18N |
158 |
FileObject keymaps = FileUtil.getConfigFile("Keymaps"); // NOI18N |
159 |
if (keymaps != null) { |
160 |
String curr = (String) keymaps.getAttribute("currentKeymap"); // NOI18N |
161 |
if (curr == null) { |
162 |
curr = "NetBeans"; // NOI18N |
163 |
} |
164 |
dirs.add(keymaps.getFileObject(curr)); |
165 |
if (keymapListener == null) { |
166 |
keymapListener = new FileChangeAdapter() { |
167 |
public @Override void fileAttributeChanged(FileAttributeEvent fe) { |
168 |
refreshBindings(); |
169 |
} |
170 |
}; |
171 |
keymaps.addFileChangeListener(keymapListener); |
172 |
} |
173 |
} |
174 |
for (FileObject dir : dirs) { |
175 |
if (dir != null) { |
176 |
for (FileObject def : dir.getChildren()) { |
177 |
if (def.isData()) { |
178 |
KeyStroke[] strokes = Utilities.stringToKeys(def.getName()); |
179 |
if (strokes == null || strokes.length == 0) { |
180 |
LOG.log(Level.WARNING, "could not load parse name of " + def.getPath()); |
181 |
continue; |
182 |
} |
183 |
Map<KeyStroke,Binding> binder = bindings; |
184 |
for (int i = 0; i < strokes.length - 1; i++) { |
185 |
Binding sub = binder.get(strokes[i]); |
186 |
if (sub != null && sub.nested == null) { |
187 |
LOG.log(Level.WARNING, "conflict between " + sub.actionDefinition.getPath() + " and " + def.getPath()); |
188 |
sub = null; |
189 |
} |
190 |
if (sub == null) { |
191 |
binder.put(strokes[i], sub = new Binding()); |
192 |
} |
193 |
binder = sub.nested; |
194 |
} |
195 |
// XXX warn about conflicts here too: |
196 |
binder.put(strokes[strokes.length - 1], new Binding(def)); |
197 |
if (strokes.length == 1) { |
198 |
String id = idForFile(def); |
199 |
KeyStroke former = id2Stroke.get(id); |
200 |
if (former == null || compare(former, strokes[0]) > 0) { |
201 |
id2Stroke.put(id, strokes[0]); |
202 |
} |
203 |
} |
204 |
} |
205 |
} |
206 |
dir.removeFileChangeListener(bindingsListener); |
207 |
dir.addFileChangeListener(bindingsListener); |
208 |
} |
209 |
} |
210 |
if (refresh) { |
211 |
// Update accelerators of existing actions after switching keymap. |
212 |
EventQueue.invokeLater(new Runnable() { |
213 |
public void run() { |
214 |
for (Map.Entry<Action, String> entry : action2Id.entrySet()) { |
215 |
entry.getKey().putValue(Action.ACCELERATOR_KEY, id2Stroke.get(entry.getValue())); |
216 |
} |
217 |
} |
218 |
}); |
219 |
} |
220 |
if (LOG.isLoggable(Level.FINE)) { |
221 |
for (Map.Entry<String,KeyStroke> entry : id2Stroke.entrySet()) { |
222 |
LOG.fine(entry.getValue() + " => " + entry.getKey()); |
223 |
} |
224 |
} |
225 |
} |
226 |
return bindings; |
227 |
} |
85 |
|
228 |
|
86 |
private static List<KeyStroke> context = new ArrayList<KeyStroke>(); |
229 |
private static List<KeyStroke> context; // accessed reflectively from org.netbeans.editor.MultiKeymap |
87 |
|
230 |
|
88 |
public static void resetContext() { |
231 |
private static void resetContext() { |
89 |
context.clear(); |
232 |
context.clear(); |
90 |
StatusDisplayer.getDefault().setStatusText(""); |
233 |
StatusDisplayer.getDefault().setStatusText(""); |
91 |
} |
234 |
} |
92 |
|
235 |
|
93 |
public static KeyStroke[] getContext() { |
236 |
public static KeyStroke[] getContext() { // called from ShortcutAndMenuKeyEventProcessor |
94 |
return context.toArray(new KeyStroke[context.size()]); |
237 |
return context.toArray(new KeyStroke[context.size()]); |
95 |
} |
238 |
} |
96 |
|
239 |
|
97 |
public static void shiftContext(KeyStroke stroke) { |
240 |
private static void shiftContext(KeyStroke stroke) { |
98 |
context.add(stroke); |
241 |
context.add(stroke); |
99 |
|
242 |
|
100 |
StringBuilder text = new StringBuilder(); |
243 |
StringBuilder text = new StringBuilder(); |
Lines 114-304
Link Here
|
114 |
KeyEvent.getKeyText (keyStroke.getKeyCode ()); |
257 |
KeyEvent.getKeyText (keyStroke.getKeyCode ()); |
115 |
} |
258 |
} |
116 |
|
259 |
|
117 |
private final Action NO_ACTION = new KeymapAction(null, null); |
|
|
118 |
private static final Logger LOG = Logger.getLogger(NbKeymap.class.getName()); |
260 |
private static final Logger LOG = Logger.getLogger(NbKeymap.class.getName()); |
119 |
|
261 |
|
120 |
public Action createMapAction(Keymap k, KeyStroke stroke) { |
|
|
121 |
return new KeymapAction(k, stroke); |
122 |
} |
123 |
|
124 |
/** Default constructor |
125 |
*/ |
126 |
public NbKeymap() { |
262 |
public NbKeymap() { |
127 |
this("Default", null); // NOI18N |
263 |
context = new ArrayList<KeyStroke>(); |
128 |
} |
|
|
129 |
|
130 |
NbKeymap(final String name, final Keymap parent) { |
131 |
this.name = name; |
132 |
this.parent = parent; |
133 |
bindings = new HashMap<KeyStroke,Action>(); |
134 |
} |
264 |
} |
135 |
|
265 |
|
136 |
public Action getDefaultAction() { |
266 |
public Action getDefaultAction() { |
137 |
LOG.log(Level.FINE, "getDefaultAction"); |
267 |
return null; |
138 |
if (defaultAction != null) { |
|
|
139 |
return defaultAction; |
140 |
} |
141 |
return (parent != null) ? parent.getDefaultAction() : null; |
142 |
} |
268 |
} |
143 |
|
269 |
|
144 |
public void setDefaultAction(Action a) { |
270 |
public void setDefaultAction(Action a) { |
145 |
LOG.log(Level.FINE, "setDefaultAction {0}", id(a)); |
271 |
throw new UnsupportedOperationException(); |
146 |
defaultAction = a; |
|
|
147 |
setChanged(); |
148 |
notifyObservers(); |
149 |
} |
272 |
} |
150 |
|
273 |
|
151 |
public String getName() { |
274 |
public String getName() { |
152 |
return name; |
275 |
return "Default"; // NOI18N |
153 |
} |
276 |
} |
154 |
|
277 |
|
155 |
public Action getAction(KeyStroke key) { |
278 |
public Action getAction(final KeyStroke key) { |
|
|
279 |
switch (key.getKeyCode()) { |
280 |
case KeyEvent.VK_SHIFT: |
281 |
case KeyEvent.VK_CONTROL: |
282 |
case KeyEvent.VK_ALT: |
283 |
case KeyEvent.VK_ALT_GRAPH: |
284 |
case KeyEvent.VK_META: |
285 |
case KeyEvent.VK_UNDEFINED: |
286 |
case KeyEvent.CHAR_UNDEFINED: |
287 |
// Not actually a bindable key press. |
288 |
return null; |
289 |
} |
290 |
if (key.isOnKeyRelease()) { |
291 |
// Again, not really our business here. |
292 |
return null; |
293 |
} |
156 |
LOG.log(Level.FINE, "getAction {0}", key); |
294 |
LOG.log(Level.FINE, "getAction {0}", key); |
157 |
|
295 |
Map<KeyStroke,Binding> binder = bindings(); |
158 |
Action a; |
296 |
for (KeyStroke ctx : context) { |
159 |
|
297 |
Binding sub = binder.get(ctx); |
160 |
KeyStroke[] ctx = getContext(); |
298 |
if (sub == null) { |
161 |
Keymap activ = this; |
299 |
resetContext(); |
162 |
for (int i=0; i<ctx.length; i++) { |
300 |
return BROKEN; // no entry found after known prefix |
163 |
if (activ == this) { |
|
|
164 |
a = bindings.get(ctx[i]); |
165 |
if ((a == null) && (parent != null)) { |
166 |
a = parent.getAction(ctx[i]); |
167 |
} |
168 |
} else { |
169 |
a = activ.getAction(ctx[i]); |
170 |
} |
301 |
} |
171 |
|
302 |
binder = sub.nested; |
172 |
if (a instanceof KeymapAction) { |
303 |
if (binder == null) { |
173 |
activ = ((KeymapAction)a).keymap; |
304 |
resetContext(); |
174 |
} else { // unknown ctx |
305 |
return BROKEN; // anomalous, expected to find submap here |
175 |
int code = key.getKeyCode(); |
|
|
176 |
if (code != KeyEvent.VK_CONTROL && |
177 |
code != KeyEvent.VK_ALT && |
178 |
code != KeyEvent.VK_ALT_GRAPH && |
179 |
code != KeyEvent.VK_SHIFT && |
180 |
code != KeyEvent.VK_META) resetContext(); |
181 |
return null; |
182 |
} |
306 |
} |
183 |
} |
307 |
} |
184 |
|
308 |
Binding b = binder.get(key); |
185 |
if (activ == this) { |
309 |
if (b == null) { |
186 |
a = bindings.get(key); |
310 |
resetContext(); |
187 |
if ((a == null) && (parent != null)) { |
311 |
return null; // normal, not found |
188 |
a = parent.getAction(key); |
312 |
} |
189 |
} |
313 |
if (b.nested == null) { |
190 |
return a; |
314 |
resetContext(); |
|
|
315 |
return b.loadAction(); // found real action |
191 |
} else { |
316 |
} else { |
192 |
a = activ.getAction(key); |
317 |
return new AbstractAction() { |
193 |
} |
318 |
public void actionPerformed(ActionEvent e) { |
194 |
|
319 |
shiftContext(key); // entering submap |
195 |
if (a != null) { |
320 |
} |
196 |
if (!(a instanceof KeymapAction)) { |
321 |
}; |
197 |
resetContext(); |
|
|
198 |
} |
199 |
return a; |
200 |
} |
201 |
|
202 |
// no action, should we reset? |
203 |
if (key.isOnKeyRelease() || |
204 |
(key.getKeyChar() != 0 && key.getKeyChar() != KeyEvent.CHAR_UNDEFINED)) { |
205 |
return null; |
206 |
} |
207 |
|
208 |
switch (key.getKeyCode()) { |
209 |
case KeyEvent.VK_SHIFT: |
210 |
case KeyEvent.VK_CONTROL: |
211 |
case KeyEvent.VK_ALT: |
212 |
case KeyEvent.VK_META: |
213 |
return null; |
214 |
default: |
215 |
resetContext(); |
216 |
return NO_ACTION; |
217 |
} |
322 |
} |
218 |
} |
323 |
} |
219 |
|
324 |
|
220 |
public KeyStroke[] getBoundKeyStrokes() { |
325 |
public KeyStroke[] getBoundKeyStrokes() { |
221 |
LOG.log(Level.FINE, "getBoundKeyStrokes"); |
326 |
assert false; |
222 |
int i = 0; |
327 |
return null; |
223 |
KeyStroke[] keys = null; |
|
|
224 |
synchronized (this) { |
225 |
keys = new KeyStroke[bindings.size()]; |
226 |
for (KeyStroke ks: bindings.keySet()) { |
227 |
keys[i++] = ks; |
228 |
} |
229 |
} |
230 |
return keys; |
231 |
} |
328 |
} |
232 |
|
329 |
|
233 |
public Action[] getBoundActions() { |
330 |
public Action[] getBoundActions() { |
234 |
LOG.log(Level.FINE, "getBoundActions"); |
331 |
assert false; |
235 |
int i = 0; |
332 |
return null; |
236 |
Action[] actionsArray = null; |
|
|
237 |
synchronized (this) { |
238 |
actionsArray = new Action[bindings.size()]; |
239 |
for (Iterator iter = bindings.values().iterator(); iter.hasNext(); ) { |
240 |
actionsArray[i++] = (Action) iter.next(); |
241 |
} |
242 |
} |
243 |
return actionsArray; |
244 |
} |
333 |
} |
245 |
|
334 |
|
246 |
public KeyStroke[] getKeyStrokesForAction(Action a) { |
335 |
public KeyStroke[] getKeyStrokesForAction(Action a) { |
247 |
LOG.log(Level.FINE, "getKeyStrokesForAction {0}", id(a)); |
336 |
FileObject definingFile = (FileObject) a.getValue("definingFile"); // cf. o.o.awt.Toolbar.setAccelerator |
248 |
|
337 |
if (definingFile == null) { |
249 |
Map<Action,List<KeyStroke>> localActions = actions; |
338 |
LOG.log(Level.FINE, "no defining file known for {0}", id(a)); |
250 |
if (localActions == null) { |
|
|
251 |
localActions = buildReverseMapping (); |
252 |
} |
253 |
|
254 |
List<KeyStroke> strokes = localActions.get (a); |
255 |
if (strokes != null) { |
256 |
return strokes.toArray(new KeyStroke[strokes.size ()]); |
257 |
} else { |
258 |
return new KeyStroke[0]; |
339 |
return new KeyStroke[0]; |
259 |
} |
340 |
} |
|
|
341 |
String id = idForFile(definingFile); |
342 |
bindings(); |
343 |
action2Id.put(a, id); |
344 |
KeyStroke k = id2Stroke.get(id); |
345 |
LOG.log(Level.FINE, "found keystroke {0} for {1} with ID {2}", new Object[] {k, id(a), id}); |
346 |
return k != null ? new KeyStroke[] {k} : new KeyStroke[0]; |
260 |
} |
347 |
} |
261 |
|
348 |
/** |
262 |
private Map<Action,List<KeyStroke>> buildReverseMapping () { |
349 |
* Traverses shadow files to origin. |
263 |
Map<Action,List<KeyStroke>> localActions = actions = new HashMap<Action,List<KeyStroke>> (); |
350 |
* Returns impl class name if that is obvious (common for SystemAction's); |
264 |
|
351 |
* else just returns file path (usual for more modern registrations). |
265 |
synchronized (this) { |
352 |
*/ |
266 |
for (Map.Entry<KeyStroke,Action> curEntry: bindings.entrySet()) { |
353 |
private static String idForFile(FileObject f) { |
267 |
Action curAction = curEntry.getValue(); |
354 |
if (f.hasExt("shadow")) { |
268 |
KeyStroke curKey = curEntry.getKey(); |
355 |
String path = (String) f.getAttribute("originalFile"); |
269 |
|
356 |
if (path != null) { |
270 |
List<KeyStroke> keysForAction = localActions.get (curAction); |
357 |
f = FileUtil.getConfigFile(path); |
271 |
if (keysForAction == null) { |
358 |
} else { |
272 |
keysForAction = Collections.synchronizedList (new ArrayList<KeyStroke> (1)); |
359 |
try { |
273 |
localActions.put (curAction, keysForAction); |
360 |
DataObject d = DataObject.find(f); |
|
|
361 |
if (d instanceof DataShadow) { |
362 |
f = ((DataShadow) d).getOriginal().getPrimaryFile(); |
363 |
} |
364 |
} catch (DataObjectNotFoundException x) { |
365 |
LOG.log(Level.INFO, f.getPath(), x); |
274 |
} |
366 |
} |
275 |
keysForAction.add (curKey); |
|
|
276 |
} |
367 |
} |
277 |
} |
368 |
} |
278 |
|
369 |
// Cannot actually load instanceCreate methodvalue=... attribute; just want to see if it is there. |
279 |
return localActions; |
370 |
if (f.hasExt("instance") && !Collections.list(f.getAttributes()).contains("instanceCreate")) { |
|
|
371 |
String clazz = (String) f.getAttribute("instanceClass"); |
372 |
if (clazz != null) { |
373 |
return clazz; |
374 |
} else { |
375 |
return f.getName().replace('-', '.'); |
376 |
} |
377 |
} |
378 |
return f.getPath(); |
280 |
} |
379 |
} |
281 |
|
380 |
|
282 |
public synchronized boolean isLocallyDefined(KeyStroke key) { |
381 |
public synchronized boolean isLocallyDefined(KeyStroke key) { |
283 |
LOG.log(Level.FINE, "isLocallyDefined {0}", key); |
382 |
assert false; |
284 |
return bindings.containsKey(key); |
383 |
return false; |
285 |
} |
384 |
} |
286 |
|
385 |
|
287 |
/** Updates action accelerator. */ |
|
|
288 |
private void updateActionAccelerator(final Action a) { |
289 |
if(a == null) { |
290 |
return; |
291 |
} |
292 |
|
293 |
Mutex.EVENT.writeAccess(new Runnable() { |
294 |
public void run() { |
295 |
KeyStroke[] keystrokes = getKeyStrokesForAction(a); |
296 |
Arrays.sort (keystrokes, NbKeymap.this); |
297 |
a.putValue(Action.ACCELERATOR_KEY, keystrokes.length > 0 ? keystrokes[0] : null); |
298 |
} |
299 |
}); |
300 |
} |
301 |
|
302 |
public int compare(KeyStroke k1, KeyStroke k2) { |
386 |
public int compare(KeyStroke k1, KeyStroke k2) { |
303 |
//#47024 and 32733 - "Find" should not be shown as an accelerator, |
387 |
//#47024 and 32733 - "Find" should not be shown as an accelerator, |
304 |
//nor should "Backspace" for Delete. Solution: The shorter text wins. |
388 |
//nor should "Backspace" for Delete. Solution: The shorter text wins. |
Lines 308-395
Link Here
|
308 |
|
392 |
|
309 |
|
393 |
|
310 |
public void addActionForKeyStroke(KeyStroke key, Action a) { |
394 |
public void addActionForKeyStroke(KeyStroke key, Action a) { |
311 |
LOG.log(Level.FINE, "addActionForKeyStroke {0} => {1}", new Object[] { key, id(a) }); |
395 |
assert false; |
312 |
// Update reverse binding for old action too (#30455): |
|
|
313 |
Action old; |
314 |
synchronized (this) { |
315 |
old = bindings.put(key, a); |
316 |
actions = null; |
317 |
} |
318 |
|
319 |
updateActionAccelerator(a); |
320 |
updateActionAccelerator(old); |
321 |
setChanged(); |
322 |
notifyObservers(); |
323 |
} |
324 |
|
325 |
void addActionForKeyStrokeMap(Map<KeyStroke,Action> map) { |
326 |
Set<Action> actionsSet = new HashSet<Action>(); |
327 |
synchronized (this) { |
328 |
for (Entry<KeyStroke,Action> entry: map.entrySet ()) { |
329 |
KeyStroke key = entry.getKey(); |
330 |
Action value = entry.getValue(); |
331 |
// Add both old and new action: |
332 |
actionsSet.add(value); |
333 |
actionsSet.add(bindings.put(key, value)); |
334 |
} |
335 |
actions = null; |
336 |
} |
337 |
|
338 |
for(Action a: actionsSet) { |
339 |
updateActionAccelerator(a); |
340 |
} |
341 |
|
342 |
setChanged(); |
343 |
notifyObservers(); |
344 |
} |
396 |
} |
345 |
|
397 |
|
346 |
public void removeKeyStrokeBinding(KeyStroke key) { |
398 |
public void removeKeyStrokeBinding(KeyStroke key) { |
347 |
LOG.log(Level.FINE, "removeKeyStrokeBinding {0}", key); |
399 |
assert false; |
348 |
|
|
|
349 |
Action a; |
350 |
synchronized (this) { |
351 |
a = bindings.remove(key); |
352 |
actions = null; |
353 |
} |
354 |
updateActionAccelerator(a); |
355 |
setChanged(); |
356 |
notifyObservers(); |
357 |
} |
400 |
} |
358 |
|
401 |
|
359 |
public void removeBindings() { |
402 |
public void removeBindings() { |
360 |
LOG.log(Level.FINE, "removeBindings"); |
403 |
assert false; |
361 |
|
|
|
362 |
Set<Action> actionsSet; |
363 |
synchronized (this) { |
364 |
actionsSet = new HashSet<Action>(bindings.values()); |
365 |
bindings.clear(); |
366 |
actions = null; |
367 |
} |
368 |
|
369 |
for(Action a: actionsSet) { |
370 |
updateActionAccelerator(a); |
371 |
} |
372 |
|
373 |
setChanged(); |
374 |
notifyObservers(); |
375 |
} |
404 |
} |
376 |
|
405 |
|
377 |
public Keymap getResolveParent() { |
406 |
public Keymap getResolveParent() { |
378 |
return parent; |
407 |
return null; |
379 |
} |
408 |
} |
380 |
|
409 |
|
381 |
public void setResolveParent(Keymap parent) { |
410 |
public void setResolveParent(Keymap parent) { |
382 |
LOG.log(Level.FINE, "setResolveParent {0}", parent == null ? null : parent.getClass()); |
411 |
throw new UnsupportedOperationException(); |
383 |
this.parent = parent; |
|
|
384 |
setChanged(); |
385 |
notifyObservers(); |
386 |
} |
387 |
|
388 |
/** Returns string representation - can be looong. |
389 |
*/ |
390 |
@Override |
391 |
public String toString() { |
392 |
return "Keymap[" + name + "]" + bindings; // NOI18N |
393 |
} |
412 |
} |
394 |
|
413 |
|
395 |
private static Object id(Action a) { |
414 |
private static Object id(Action a) { |
Lines 399-492
Link Here
|
399 |
return a; |
418 |
return a; |
400 |
} |
419 |
} |
401 |
|
420 |
|
402 |
public static class SubKeymap implements Keymap { |
|
|
403 |
Object hold; |
404 |
Keymap parent; |
405 |
Map<KeyStroke, Action> bindings; |
406 |
Action defaultAction; |
407 |
|
408 |
public SubKeymap(Object hold) { |
409 |
this.hold = hold; |
410 |
bindings = new HashMap<KeyStroke, Action>(); |
411 |
} |
412 |
|
413 |
public String getName() { |
414 |
return "name"; |
415 |
} |
416 |
|
417 |
public void setResolveParent(Keymap parent) { |
418 |
this.parent = parent; |
419 |
} |
420 |
|
421 |
public Keymap getResolveParent() { |
422 |
return parent; |
423 |
} |
424 |
|
425 |
public void addActionForKeyStroke(KeyStroke key, Action a) { |
426 |
bindings.put(key, a); |
427 |
} |
428 |
|
429 |
public KeyStroke[] getKeyStrokesForAction(Action a) { |
430 |
return new KeyStroke[0]; |
431 |
} |
432 |
|
433 |
public void setDefaultAction(Action a) { |
434 |
defaultAction = a; |
435 |
} |
436 |
|
437 |
public Action getAction(KeyStroke key) { |
438 |
return bindings.get(key); |
439 |
} |
440 |
|
441 |
public boolean isLocallyDefined(KeyStroke key) { |
442 |
return bindings.containsKey(key); |
443 |
} |
444 |
|
445 |
public void removeKeyStrokeBinding(KeyStroke keys) { |
446 |
bindings.remove(keys); |
447 |
} |
448 |
|
449 |
public Action[] getBoundActions() { |
450 |
synchronized (this) { |
451 |
return bindings.values().toArray(new Action[0]); |
452 |
} |
453 |
} |
454 |
|
455 |
public KeyStroke[] getBoundKeyStrokes() { |
456 |
synchronized (this) { |
457 |
return bindings.keySet().toArray(new KeyStroke[0]); |
458 |
} |
459 |
} |
460 |
|
461 |
public Action getDefaultAction() { |
462 |
return defaultAction; |
463 |
} |
464 |
|
465 |
public void removeBindings() { |
466 |
bindings.clear(); |
467 |
} |
468 |
|
469 |
} |
470 |
|
471 |
public static class KeymapAction extends AbstractAction { |
472 |
private Keymap keymap; |
473 |
private KeyStroke stroke; |
474 |
|
475 |
public KeymapAction(Keymap keymap, KeyStroke stroke) { |
476 |
this.keymap = keymap; |
477 |
this.stroke = stroke; |
478 |
} |
479 |
|
480 |
public Keymap getSubMap() { |
481 |
return keymap; |
482 |
} |
483 |
|
484 |
public void actionPerformed(ActionEvent e) { |
485 |
if (stroke == null) { // NO_ACTION -> reset |
486 |
resetContext(); |
487 |
} else { |
488 |
shiftContext(stroke); |
489 |
} |
490 |
} |
491 |
} |
492 |
} |
421 |
} |