KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > core > windows > ShortcutAndMenuKeyEventProcessor


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.windows;
21
22 import java.util.Collections JavaDoc;
23 import java.util.Set JavaDoc;
24 import org.netbeans.core.NbKeymap;
25 import org.netbeans.core.windows.view.ui.KeyboardPopupSwitcher;
26 import org.openide.actions.ActionManager;
27 import org.openide.awt.StatusDisplayer;
28 import org.openide.util.Lookup;
29 import org.openide.util.Utilities;
30
31 import javax.swing.*;
32 import javax.swing.text.Keymap JavaDoc;
33 import java.awt.*;
34 import java.awt.event.ActionEvent JavaDoc;
35 import java.awt.event.InputEvent JavaDoc;
36 import java.awt.event.KeyEvent JavaDoc;
37 import java.lang.reflect.Method JavaDoc;
38 import org.netbeans.core.NbTopManager;
39
40
41 /**
42  * this class registers itself to the KeyboardFocusManager as a key event
43  * post-processor as well as a key event dispatcher. It invokes the action
44  * bound to the key stroke, or routes unconsumed key events to the menu bar.
45  * If a menu is already shown, all key events are routed to the main menu bar.
46  *
47  * @author Tran Duc Trung
48  */

49 final class ShortcutAndMenuKeyEventProcessor implements KeyEventDispatcher, KeyEventPostProcessor {
50     
51     private static ShortcutAndMenuKeyEventProcessor defaultInstance;
52     
53     private static boolean installed = false;
54
55     /* holds original set of focus forward traversal keys */
56     private static Set JavaDoc<AWTKeyStroke> defaultForward;
57     /* holds original set of focus backward traversal keys */
58     private static Set JavaDoc<AWTKeyStroke> defaultBackward;
59     
60     
61     private ShortcutAndMenuKeyEventProcessor() {
62     }
63     
64
65     private static synchronized ShortcutAndMenuKeyEventProcessor getDefault() {
66         if(defaultInstance == null) {
67             defaultInstance = new ShortcutAndMenuKeyEventProcessor();
68         }
69         
70         return defaultInstance;
71     }
72     
73     
74     public static synchronized void install() {
75         if(installed) {
76             return;
77         }
78         
79         ShortcutAndMenuKeyEventProcessor instance = getDefault();
80         
81         KeyboardFocusManager keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
82         keyboardFocusManager.addKeyEventDispatcher(instance);
83         keyboardFocusManager.addKeyEventPostProcessor(instance);
84         // #63252: Disable focus traversal functionality of Ctrl+Tab and Ctrl+Shift+Tab,
85
// to allow our own document switching (RecentViewListAction)
86
defaultForward = keyboardFocusManager.getDefaultFocusTraversalKeys(
87                             KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
88         defaultBackward = keyboardFocusManager.getDefaultFocusTraversalKeys(
89                             KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);
90         keyboardFocusManager.setDefaultFocusTraversalKeys(
91             KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
92             Collections.singleton(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, 0))
93         );
94         keyboardFocusManager.setDefaultFocusTraversalKeys(
95             KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
96             Collections.singleton(AWTKeyStroke.getAWTKeyStroke(KeyEvent.VK_TAB, KeyEvent.SHIFT_DOWN_MASK))
97         );
98     }
99     
100     public static synchronized void uninstall() {
101         if(!installed) {
102             return;
103         }
104         
105         ShortcutAndMenuKeyEventProcessor instance = getDefault();
106         
107         KeyboardFocusManager keyboardFocusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
108         keyboardFocusManager.removeKeyEventDispatcher(instance);
109         keyboardFocusManager.removeKeyEventPostProcessor(instance);
110         // reset default focus traversal keys
111
keyboardFocusManager.setDefaultFocusTraversalKeys(
112                 KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, defaultForward
113         );
114         keyboardFocusManager.setDefaultFocusTraversalKeys(
115                 KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, defaultBackward
116         );
117         defaultBackward = null;
118         defaultForward = null;
119     }
120
121     private boolean wasPopupDisplayed;
122     private int lastModifiers;
123     private char lastKeyChar;
124     private boolean lastSampled = false;
125     private boolean skipNextTyped = false;
126     
127     public boolean postProcessKeyEvent(KeyEvent JavaDoc ev) {
128         if (ev.isConsumed())
129             return false;
130
131         if (processShortcut(ev))
132             return true;
133
134         Window w = SwingUtilities.windowForComponent(ev.getComponent());
135         if (w instanceof Dialog && !WindowManagerImpl.isSeparateWindow(w))
136             return false;
137         
138         JFrame mw = (JFrame)WindowManagerImpl.getInstance().getMainWindow();
139         if (w == mw) {
140             return false;
141         }
142
143         JMenuBar mb = mw.getJMenuBar();
144         if (mb == null)
145             return false;
146         boolean pressed = (ev.getID() == KeyEvent.KEY_PRESSED);
147         boolean res = invokeProcessKeyBindingsForAllComponents(ev, mw, pressed);
148         
149         if (res)
150             ev.consume();
151         return res;
152     }
153
154     public boolean dispatchKeyEvent(KeyEvent JavaDoc ev) {
155         // XXX(-ttran) Sun JDK 1.4 on Linux: pressing Alt key produces
156
// KeyEvent.VK_ALT, but Alt+<key> produces Meta+<key>
157
// XXX is this still a problem? -jglick
158
if (Utilities.getOperatingSystem() == Utilities.OS_LINUX) {
159             int mods = ev.getModifiers();
160             if (mods == InputEvent.META_MASK) {
161                 mods = (mods & ~ InputEvent.META_MASK) | InputEvent.ALT_MASK;
162                 ev.setModifiers(mods);
163             }
164         }
165         
166         // in some ctx, may need event filtering
167
if (NbKeymap.getContext().length != 0) {
168             // Ignore anything but KeyPressed inside ctx, #67187
169
if (ev.getID() != KeyEvent.KEY_PRESSED) {
170                 ev.consume();
171                 return true;
172             }
173             
174             skipNextTyped = true;
175
176             Component comp = ev.getComponent();
177             if (!(comp instanceof JComponent) ||
178                 ((JComponent)comp).getClientProperty("context-api-aware") == null) {
179                     // not context api aware, don't pass subsequent events
180
processShortcut(ev);
181                 // ignore processShortcut result, consume everything while in ctx
182
return true;
183             }
184         }
185  
186         if (ev.getID() == KeyEvent.KEY_PRESSED
187             && ev.getModifiers() == (InputEvent.CTRL_MASK | InputEvent.SHIFT_MASK)
188             && (ev.getKeyCode() == KeyEvent.VK_PAUSE
189                 || ev.getKeyCode() == KeyEvent.VK_CANCEL)
190             ) {
191             Object JavaDoc source = ev.getSource();
192             if (source instanceof Component) {
193                 Component focused = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
194                 System.err.println("*** ShortcutAndMenuKeyEventProcessor: current focus owner = " + focused); // NOI18N
195
}
196             ev.consume();
197             return true;
198         }
199         
200     
201     // multi-shortcut in middle
202
if (ev.getID() == KeyEvent.KEY_TYPED && skipNextTyped) {
203             ev.consume();
204             skipNextTyped = false;
205             return true;
206         }
207
208         if (ev.getID() == KeyEvent.KEY_RELEASED) {
209             skipNextTyped = false;
210         }
211         
212         if (ev.getID() == KeyEvent.KEY_PRESSED) {
213             // decompose to primitive fields to avoid memory profiler confusion (keyEvent keeps source reference)
214
lastKeyChar = ev.getKeyChar();
215             lastModifiers = ev.getModifiers();
216             lastSampled = true;
217         }
218         
219         MenuElement[] arr = MenuSelectionManager.defaultManager().getSelectedPath();
220         if (arr == null || arr.length == 0) {
221             wasPopupDisplayed = false;
222
223             // XXX(-ttran) special case for Shift+F10 on braindead Windoze.
224
// Shortcuts are handled in postProcessKeyEvent() so that the
225
// focused components can decide to handle and consume the key
226
// event itself. Buttons' and labels' mnemonics in components will
227
// work even though they conflict with shortcuts. But if we do so
228
// for Shift+F10 on Windoze then for some mysterious reason the
229
// system menu (left-upper icon in the native window caption) will
230
// be invoked, no matter how hard we try to consume the event.
231

232             if (Utilities.isWindows()
233                 && ev.getModifiers() == InputEvent.SHIFT_MASK
234                 && ev.getKeyCode() == KeyEvent.VK_F10
235                 ) {
236                 return processShortcut(ev);
237             }
238
239             // Only here for fix #41477:
240
// To be able to catch and dispatch Ctrl+TAB and Ctrl+Shift+Tab
241
// in our own way, it's needed to do as soon as here, because
242
// otherwise Swing will use these keys as focus traversals, which
243
// means that TopComponent which contains focusCycleRoot inside itself
244
// will grab these shortcuts, which is not desirable
245
return KeyboardPopupSwitcher.processShortcut(ev);
246         }
247
248         if (!wasPopupDisplayed
249             && lastSampled == true
250             && ev.getID() == KeyEvent.KEY_TYPED
251             && lastModifiers == InputEvent.ALT_MASK
252             && ev.getModifiers() == InputEvent.ALT_MASK
253             && lastKeyChar == ev.getKeyChar()
254             ) {
255             wasPopupDisplayed = true;
256             ev.consume();
257             return true;
258         }
259
260         wasPopupDisplayed = true;
261         
262         MenuSelectionManager.defaultManager().processKeyEvent(ev);
263         
264         if (!ev.isConsumed() && arr[0] instanceof JMenuBar) {
265             ev.setSource(WindowManagerImpl.getInstance().getMainWindow());
266         }
267         return ev.isConsumed();
268     }
269
270     private boolean processShortcut(KeyEvent JavaDoc ev) {
271         //ignore shortcut keys when the IDE is shutting down
272
if( NbTopManager.get().isExiting() ) {
273             ev.consume();
274             return true;
275         }
276         
277         KeyStroke ks = KeyStroke.getKeyStrokeForEvent(ev);
278         Window w = SwingUtilities.windowForComponent(ev.getComponent());
279
280         // don't process shortcuts if this is a help frame
281
if ((w instanceof JFrame) && ((JFrame)w).getRootPane().getClientProperty("netbeans.helpframe") != null) // NOI18N
282
return true;
283         
284         // don't let action keystrokes to propagate from both
285
// modal and nonmodal dialogs, but propagate from separate floating windows,
286
// even if they are backed by JDialog
287
if ((w instanceof Dialog) &&
288             !WindowManagerImpl.getInstance().isSeparateWindow(w) &&
289             !isTransmodalAction(ks)) {
290             return false;
291         }
292         
293         // Provide a reasonably useful action event that identifies what was focused
294
// when the key was pressed, as well as what keystroke ran the action.
295
ActionEvent JavaDoc aev = new ActionEvent JavaDoc(
296             ev.getSource(), ActionEvent.ACTION_PERFORMED, Utilities.keyToString(ks));
297             
298         Keymap root = (Keymap)Lookup.getDefault().lookup(Keymap.class);
299         Action a = root.getAction (ks);
300         if (a != null && a.isEnabled()) {
301             ActionManager am = (ActionManager)Lookup.getDefault().lookup(ActionManager.class);
302             am.invokeAction(a, aev);
303             ev.consume();
304             return true;
305         }
306         return false;
307     }
308
309     private static boolean invokeProcessKeyBindingsForAllComponents(
310         KeyEvent JavaDoc e, Container container, boolean pressed)
311     {
312         try {
313             Method JavaDoc m = JComponent.class.getDeclaredMethod(
314                 "processKeyBindingsForAllComponents", // NOI18N
315
new Class JavaDoc[] { KeyEvent JavaDoc.class, Container.class, Boolean.TYPE });
316             if (m == null)
317                 return false;
318
319             m.setAccessible(true);
320             Boolean JavaDoc b = (Boolean JavaDoc) m.invoke(null, new Object JavaDoc[] { e, container, pressed ? Boolean.TRUE : Boolean.FALSE });
321             return b.booleanValue();
322         } catch (Exception JavaDoc ex) {
323             //ex.printStackTrace();
324
}
325         
326         return false;
327     }
328
329     /**
330      * Checks to see if a given keystroke is bound to an action which should
331      * function on all focused components. This includes the Main Window,
332      * dialogs, popup menus, etc. Otherwise only the Main Window and
333      * TopComponents will receive the keystroke. By default, off, unless the
334      * action has a property named <code>OpenIDE-Transmodal-Action</code> which
335      * is set to {@link Boolean#TRUE}.
336      * @param key the keystroke to check
337      * @return <code>true</code> if transmodal; <code>false</code> if a normal
338      * action, or the key is not bound to anything in the global keymap
339      */

340     private static boolean isTransmodalAction (KeyStroke key) {
341         Keymap root = (Keymap)Lookup.getDefault().lookup(Keymap.class);
342         Action a = root.getAction (key);
343         if (a == null) return false;
344         Object JavaDoc val = a.getValue ("OpenIDE-Transmodal-Action"); // NOI18N
345
return val != null && val.equals (Boolean.TRUE);
346     }
347
348 }
349
Popular Tags