KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > core > NbKeymap


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.core;
21
22 import java.awt.event.KeyEvent JavaDoc;
23 import java.util.ArrayList JavaDoc;
24 import java.util.Arrays JavaDoc;
25 import java.util.Collections JavaDoc;
26 import java.util.Comparator JavaDoc;
27 import java.util.HashMap JavaDoc;
28 import java.util.HashSet JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.List JavaDoc;
31 import java.util.Map JavaDoc;
32 import java.util.Map.Entry;
33 import java.util.Observable JavaDoc;
34 import java.util.Set JavaDoc;
35 import javax.swing.Action JavaDoc;
36 import javax.swing.KeyStroke JavaDoc;
37 import javax.swing.text.Keymap JavaDoc;
38 import org.openide.awt.StatusDisplayer;
39 import org.openide.util.Mutex;
40
41 /** Implementation of standard key - action mappings.
42 *
43 * @author Dafe Simonek
44 */

45 public final class NbKeymap extends Observable JavaDoc implements Keymap JavaDoc, Comparator JavaDoc<KeyStroke JavaDoc> {
46     /** Name of this keymap */
47     String JavaDoc name;
48     /** Parent keymap */
49     Keymap JavaDoc parent;
50     /** Hashtable holding KeyStroke > Action mappings */
51     Map JavaDoc<KeyStroke JavaDoc,Action JavaDoc> bindings;
52     /** Default action */
53     Action JavaDoc defaultAction;
54     /** hash table to map (Action -> ArrayList of KeyStrokes) */
55     Map JavaDoc<Action JavaDoc,List JavaDoc<KeyStroke JavaDoc>> actions;
56     
57     private static List JavaDoc<KeyStroke JavaDoc> context = new ArrayList JavaDoc<KeyStroke JavaDoc>();
58     
59     public static void resetContext() {
60         context.clear();
61         StatusDisplayer.getDefault().setStatusText("");
62     }
63
64     public static KeyStroke JavaDoc[] getContext() {
65         return (KeyStroke JavaDoc[]) context.toArray(new KeyStroke JavaDoc[context.size()]);
66     }
67     
68     public static void shiftContext(KeyStroke JavaDoc stroke) {
69         context.add(stroke);
70
71         StringBuilder JavaDoc text = new StringBuilder JavaDoc();
72         for (KeyStroke JavaDoc ks: context) {
73             text.append(getKeyText(ks)).append(' ');
74         }
75         StatusDisplayer.getDefault().setStatusText(text.toString());
76     }
77     
78     private static String JavaDoc getKeyText (KeyStroke JavaDoc keyStroke) {
79         if (keyStroke == null) return "";
80         String JavaDoc modifText = KeyEvent.getKeyModifiersText
81             (keyStroke.getModifiers ());
82         if ("".equals (modifText))
83             return KeyEvent.getKeyText (keyStroke.getKeyCode ());
84         return modifText + "+" + // NOI18N
85
KeyEvent.getKeyText (keyStroke.getKeyCode ());
86     }
87            
88     private final Action JavaDoc NO_ACTION = new KeymapAction(null, null);
89     
90     public Action JavaDoc createMapAction(Keymap JavaDoc k, KeyStroke JavaDoc stroke) {
91         return new KeymapAction(k, stroke);
92     }
93
94     /** Default constructor
95     */

96     public NbKeymap() {
97         this("Default", null); // NOI18N
98
}
99
100     NbKeymap(final String JavaDoc name, final Keymap JavaDoc parent) {
101         this.name = name;
102         this.parent = parent;
103         bindings = new HashMap JavaDoc<KeyStroke JavaDoc,Action JavaDoc>();
104     }
105
106     public Action JavaDoc getDefaultAction() {
107         if (defaultAction != null) {
108             return defaultAction;
109         }
110         return (parent != null) ? parent.getDefaultAction() : null;
111     }
112
113     public void setDefaultAction(Action JavaDoc a) {
114         defaultAction = a;
115         setChanged();
116         notifyObservers();
117     }
118
119     public String JavaDoc getName() {
120         return name;
121     }
122
123     public Action JavaDoc getAction(KeyStroke JavaDoc key) {
124         Action JavaDoc a;
125
126         KeyStroke JavaDoc[] ctx = getContext();
127         Keymap JavaDoc activ = this;
128         for (int i=0; i<ctx.length; i++) {
129             if (activ == this) {
130                 a = bindings.get(ctx[i]);
131                 if ((a == null) && (parent != null)) {
132                     a = parent.getAction(ctx[i]);
133                 }
134             } else {
135                 a = activ.getAction(ctx[i]);
136             }
137             
138             if (a instanceof KeymapAction) {
139                 activ = ((KeymapAction)a).keymap;
140             } else { // unknown ctx
141
int code = key.getKeyCode();
142                 if (code != KeyEvent.VK_CONTROL &&
143                         code != KeyEvent.VK_ALT &&
144                         code != KeyEvent.VK_ALT_GRAPH &&
145                         code != KeyEvent.VK_SHIFT &&
146                         code != KeyEvent.VK_META) resetContext();
147                 return null;
148             }
149         }
150         
151         if (activ == this) {
152             a = bindings.get(key);
153             if ((a == null) && (parent != null)) {
154                 a = parent.getAction(key);
155             }
156             return a;
157         } else {
158             a = activ.getAction(key);
159         }
160         
161         if (a != null) {
162             if (!(a instanceof KeymapAction)) {
163                 resetContext();
164             }
165             return a;
166         }
167             
168         // no action, should we reset?
169
if (key.isOnKeyRelease() ||
170             (key.getKeyChar() != 0 && key.getKeyChar() != KeyEvent.CHAR_UNDEFINED)) {
171                 return null;
172         }
173             
174         switch (key.getKeyCode()) {
175             case KeyEvent.VK_SHIFT:
176             case KeyEvent.VK_CONTROL:
177             case KeyEvent.VK_ALT:
178             case KeyEvent.VK_META:
179                 return null;
180             default:
181                 resetContext();
182                 return NO_ACTION;
183         }
184     }
185
186     public KeyStroke JavaDoc[] getBoundKeyStrokes() {
187         int i = 0;
188         KeyStroke JavaDoc[] keys = null;
189         synchronized (this) {
190             keys = new KeyStroke JavaDoc[bindings.size()];
191             for (KeyStroke JavaDoc ks: bindings.keySet()) {
192                 keys[i++] = ks;
193             }
194         }
195         return keys;
196     }
197
198     public Action JavaDoc[] getBoundActions() {
199         int i = 0;
200         Action JavaDoc[] actionsArray = null;
201         synchronized (this) {
202             actionsArray = new Action JavaDoc[bindings.size()];
203             for (Iterator JavaDoc iter = bindings.values().iterator(); iter.hasNext(); ) {
204                 actionsArray[i++] = (Action JavaDoc) iter.next();
205             }
206         }
207         return actionsArray;
208     }
209
210     public KeyStroke JavaDoc[] getKeyStrokesForAction(Action JavaDoc a) {
211         Map JavaDoc<Action JavaDoc,List JavaDoc<KeyStroke JavaDoc>> localActions = actions;
212         if (localActions == null) {
213             localActions = buildReverseMapping ();
214         }
215
216         List JavaDoc<KeyStroke JavaDoc> strokes = localActions.get (a);
217         if (strokes != null) {
218             return strokes.toArray(new KeyStroke JavaDoc[strokes.size ()]);
219         } else {
220             return new KeyStroke JavaDoc[0];
221         }
222     }
223
224     private Map JavaDoc<Action JavaDoc,List JavaDoc<KeyStroke JavaDoc>> buildReverseMapping () {
225         Map JavaDoc<Action JavaDoc,List JavaDoc<KeyStroke JavaDoc>> localActions = actions = new HashMap JavaDoc<Action JavaDoc,List JavaDoc<KeyStroke JavaDoc>> ();
226
227         synchronized (this) {
228             for (Map.Entry JavaDoc<KeyStroke JavaDoc,Action JavaDoc> curEntry: bindings.entrySet()) {
229                 Action JavaDoc curAction = curEntry.getValue();
230                 KeyStroke JavaDoc curKey = curEntry.getKey();
231
232                 List JavaDoc<KeyStroke JavaDoc> keysForAction = localActions.get (curAction);
233                 if (keysForAction == null) {
234                     keysForAction = Collections.synchronizedList (new ArrayList JavaDoc<KeyStroke JavaDoc> (1));
235                     localActions.put (curAction, keysForAction);
236                 }
237                 keysForAction.add (curKey);
238             }
239         }
240
241         return localActions;
242     }
243
244     public synchronized boolean isLocallyDefined(KeyStroke JavaDoc key) {
245         return bindings.containsKey(key);
246     }
247
248     /** Updates action accelerator. */
249     private void updateActionAccelerator(final Action JavaDoc a) {
250         if(a == null) {
251             return;
252         }
253         
254         Mutex.EVENT.writeAccess(new Runnable JavaDoc() {
255             public void run() {
256                 KeyStroke JavaDoc[] keystrokes = getKeyStrokesForAction(a);
257                 Arrays.sort (keystrokes, NbKeymap.this);
258                 a.putValue(Action.ACCELERATOR_KEY, keystrokes.length > 0 ? keystrokes[0] : null);
259             }
260         });
261     }
262     
263     public int compare(KeyStroke JavaDoc k1, KeyStroke JavaDoc k2) {
264         //#47024 and 32733 - "Find" should not be shown as an accelerator,
265
//nor should "Backspace" for Delete. Solution: The shorter text wins.
266
return KeyEvent.getKeyText(k1.getKeyCode()).length() -
267             KeyEvent.getKeyText(k2.getKeyCode()).length();
268     }
269     
270     
271     public void addActionForKeyStroke(KeyStroke JavaDoc key, Action JavaDoc a) {
272         // Update reverse binding for old action too (#30455):
273
Action JavaDoc old;
274         synchronized (this) {
275             old = bindings.put(key, a);
276             actions = null;
277         }
278         
279         updateActionAccelerator(a);
280         updateActionAccelerator(old);
281         setChanged();
282         notifyObservers();
283     }
284
285     void addActionForKeyStrokeMap(Map JavaDoc<KeyStroke JavaDoc,Action JavaDoc> map) {
286         Set JavaDoc<Action JavaDoc> actionsSet = new HashSet JavaDoc<Action JavaDoc>();
287         synchronized (this) {
288             for (Entry<KeyStroke JavaDoc,Action JavaDoc> entry: map.entrySet ()) {
289                 KeyStroke JavaDoc key = entry.getKey();
290                 Action JavaDoc value = entry.getValue();
291                 // Add both old and new action:
292
actionsSet.add(value);
293                 actionsSet.add(bindings.put(key, value));
294             }
295             actions = null;
296         }
297         
298         for(Action JavaDoc a: actionsSet) {
299             updateActionAccelerator(a);
300         }
301         
302         setChanged();
303         notifyObservers();
304     }
305
306     public void removeKeyStrokeBinding(KeyStroke JavaDoc key) {
307         Action JavaDoc a;
308         synchronized (this) {
309             a = bindings.remove(key);
310             actions = null;
311         }
312         updateActionAccelerator(a);
313         setChanged();
314         notifyObservers();
315     }
316
317     public void removeBindings() {
318         Set JavaDoc<Action JavaDoc> actionsSet;
319         synchronized (this) {
320             actionsSet = new HashSet JavaDoc<Action JavaDoc>(bindings.values());
321             bindings.clear();
322             actions = null;
323         }
324         
325         for(Action JavaDoc a: actionsSet) {
326             updateActionAccelerator(a);
327         }
328         
329         setChanged();
330         notifyObservers();
331     }
332
333     public Keymap JavaDoc getResolveParent() {
334         return parent;
335     }
336
337     public void setResolveParent(Keymap JavaDoc parent) {
338         this.parent = parent;
339         setChanged();
340         notifyObservers();
341     }
342
343     /** Returns string representation - can be looong.
344     */

345     public String JavaDoc toString() {
346         return "Keymap[" + name + "]" + bindings; // NOI18N
347
}
348     
349     public static class SubKeymap implements Keymap JavaDoc {
350         Object JavaDoc hold;
351         Keymap JavaDoc parent;
352         Map JavaDoc<KeyStroke JavaDoc, Action JavaDoc> bindings;
353         Action JavaDoc defaultAction;
354
355         public SubKeymap(Object JavaDoc hold) {
356             this.hold = hold;
357             bindings = new HashMap JavaDoc<KeyStroke JavaDoc, Action JavaDoc>();
358         }
359         
360         public String JavaDoc getName() {
361             return "name";
362         }
363         
364         public void setResolveParent(Keymap JavaDoc parent) {
365             this.parent = parent;
366         }
367
368         public Keymap JavaDoc getResolveParent() {
369             return parent;
370         }
371
372         public void addActionForKeyStroke(KeyStroke JavaDoc key, Action JavaDoc a) {
373             bindings.put(key, a);
374         }
375
376         public KeyStroke JavaDoc[] getKeyStrokesForAction(Action JavaDoc a) {
377             return new KeyStroke JavaDoc[0];
378         }
379
380         public void setDefaultAction(Action JavaDoc a) {
381                 defaultAction = a;
382         }
383
384         public Action JavaDoc getAction(KeyStroke JavaDoc key) {
385             return bindings.get(key);
386         }
387
388         public boolean isLocallyDefined(KeyStroke JavaDoc key) {
389             return bindings.containsKey(key);
390         }
391
392         public void removeKeyStrokeBinding(KeyStroke JavaDoc keys) {
393             bindings.remove(keys);
394         }
395
396         public Action JavaDoc[] getBoundActions() {
397             synchronized (this) {
398                 return bindings.values().toArray(new Action JavaDoc[0]);
399             }
400         }
401
402         public KeyStroke JavaDoc[] getBoundKeyStrokes() {
403             synchronized (this) {
404                 return bindings.keySet().toArray(new KeyStroke JavaDoc[0]);
405             }
406         }
407   
408         public Action JavaDoc getDefaultAction() {
409             return defaultAction;
410         }
411
412         public void removeBindings() {
413             bindings.clear();
414         }
415     
416     }
417     
418     public static class KeymapAction extends javax.swing.AbstractAction JavaDoc {
419         private Keymap JavaDoc keymap;
420         private KeyStroke JavaDoc stroke;
421     
422         public KeymapAction(Keymap JavaDoc keymap, KeyStroke JavaDoc stroke) {
423             this.keymap = keymap;
424             this.stroke = stroke;
425         }
426         
427         public Keymap JavaDoc getSubMap() {
428             return keymap;
429         }
430         
431         public void actionPerformed(java.awt.event.ActionEvent JavaDoc e) {
432             if (stroke == null) { // NO_ACTION -> reset
433
resetContext();
434             } else {
435                 shiftContext(stroke);
436             }
437         }
438     }
439 }
440
Popular Tags