Source Code Cross Referenced for WorkbenchKeyboard.java in  » IDE-Eclipse » ui-workbench » org » eclipse » ui » internal » keys » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » IDE Eclipse » ui workbench » org.eclipse.ui.internal.keys 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*******************************************************************************
002:         * Copyright (c) 2000, 2007 IBM Corporation and others.
003:         * All rights reserved. This program and the accompanying materials
004:         * are made available under the terms of the Eclipse Public License v1.0
005:         * which accompanies this distribution, and is available at
006:         * http://www.eclipse.org/legal/epl-v10.html
007:         *
008:         * Contributors:
009:         *     IBM Corporation - initial API and implementation
010:         *******************************************************************************/package org.eclipse.ui.internal.keys;
011:
012:        import java.util.ArrayList;
013:        import java.util.Collection;
014:        import java.util.Iterator;
015:        import java.util.List;
016:        import java.util.ResourceBundle;
017:
018:        import org.eclipse.core.commands.Command;
019:        import org.eclipse.core.commands.NotEnabledException;
020:        import org.eclipse.core.commands.NotHandledException;
021:        import org.eclipse.core.commands.ParameterizedCommand;
022:        import org.eclipse.core.commands.common.CommandException;
023:        import org.eclipse.core.commands.common.NotDefinedException;
024:        import org.eclipse.core.commands.util.Tracing;
025:        import org.eclipse.core.runtime.IStatus;
026:        import org.eclipse.core.runtime.Status;
027:        import org.eclipse.jface.bindings.Binding;
028:        import org.eclipse.jface.bindings.keys.KeySequence;
029:        import org.eclipse.jface.bindings.keys.KeyStroke;
030:        import org.eclipse.jface.bindings.keys.ParseException;
031:        import org.eclipse.jface.bindings.keys.SWTKeySupport;
032:        import org.eclipse.jface.internal.InternalPolicy;
033:        import org.eclipse.swt.SWT;
034:        import org.eclipse.swt.custom.StyledText;
035:        import org.eclipse.swt.widgets.Combo;
036:        import org.eclipse.swt.widgets.Control;
037:        import org.eclipse.swt.widgets.Display;
038:        import org.eclipse.swt.widgets.Event;
039:        import org.eclipse.swt.widgets.Listener;
040:        import org.eclipse.swt.widgets.Shell;
041:        import org.eclipse.swt.widgets.Text;
042:        import org.eclipse.swt.widgets.Widget;
043:        import org.eclipse.ui.IWindowListener;
044:        import org.eclipse.ui.IWorkbench;
045:        import org.eclipse.ui.IWorkbenchWindow;
046:        import org.eclipse.ui.contexts.IContextService;
047:        import org.eclipse.ui.handlers.IHandlerService;
048:        import org.eclipse.ui.internal.Workbench;
049:        import org.eclipse.ui.internal.WorkbenchPlugin;
050:        import org.eclipse.ui.internal.contexts.ContextService;
051:        import org.eclipse.ui.internal.handlers.HandlerService;
052:        import org.eclipse.ui.internal.misc.Policy;
053:        import org.eclipse.ui.internal.misc.StatusUtil;
054:        import org.eclipse.ui.internal.util.Util;
055:        import org.eclipse.ui.keys.IBindingService;
056:        import org.eclipse.ui.statushandlers.StatusManager;
057:
058:        import com.ibm.icu.text.MessageFormat;
059:
060:        /**
061:         * <p>
062:         * Controls the keyboard input into the workbench key binding architecture. This
063:         * allows key events to be programmatically pushed into the key binding
064:         * architecture -- potentially triggering the execution of commands. It is used
065:         * by the <code>Workbench</code> to listen for events on the
066:         * <code>Display</code>.
067:         * </p>
068:         * <p>
069:         * This class is not designed to be thread-safe. It is assumed that all access
070:         * to the <code>press</code> method is done through the event loop. Accessing
071:         * this method outside the event loop can cause corruption of internal state.
072:         * </p>
073:         * 
074:         * @since 3.0
075:         */
076:        public final class WorkbenchKeyboard {
077:
078:            /**
079:             * A display filter for handling key bindings. This filter can either be
080:             * enabled or disabled. If disabled, the filter does not process incoming
081:             * events. The filter starts enabled.
082:             * 
083:             * @since 3.1
084:             */
085:            public final class KeyDownFilter implements  Listener {
086:
087:                /**
088:                 * Whether the filter is enabled.
089:                 */
090:                private transient boolean enabled = true;
091:
092:                /**
093:                 * Handles an incoming traverse or key down event.
094:                 * 
095:                 * @param event
096:                 *            The event to process; must not be <code>null</code>.
097:                 */
098:                public final void handleEvent(final Event event) {
099:                    if (!enabled) {
100:                        return;
101:                    }
102:
103:                    if (DEBUG && DEBUG_VERBOSE) {
104:                        final StringBuffer buffer = new StringBuffer(
105:                                "Listener.handleEvent(type = "); //$NON-NLS-1$
106:                        switch (event.type) {
107:                        case SWT.KeyDown:
108:                            buffer.append("KeyDown"); //$NON-NLS-1$
109:                            break;
110:                        case SWT.Traverse:
111:                            buffer.append("Traverse"); //$NON-NLS-1$
112:                            break;
113:                        default:
114:                            buffer.append(event.type);
115:                        }
116:                        buffer.append(", stateMask = 0x" //$NON-NLS-1$
117:                                + Integer.toHexString(event.stateMask)
118:                                + ", keyCode = 0x" //$NON-NLS-1$
119:                                + Integer.toHexString(event.keyCode)
120:                                + ", time = " //$NON-NLS-1$
121:                                + event.time + ", character = 0x" //$NON-NLS-1$
122:                                + Integer.toHexString(event.character) + ")"); //$NON-NLS-1$
123:                        Tracing.printTrace("KEYS", buffer.toString()); //$NON-NLS-1$
124:                    }
125:
126:                    filterKeySequenceBindings(event);
127:                }
128:
129:                /**
130:                 * Returns whether the key binding filter is enabled.
131:                 * 
132:                 * @return Whether the key filter is enabled.
133:                 */
134:                public final boolean isEnabled() {
135:                    return enabled;
136:                }
137:
138:                /**
139:                 * Sets whether this filter should be enabled or disabled.
140:                 * 
141:                 * @param enabled
142:                 *            Whether key binding filter should be enabled.
143:                 */
144:                public final void setEnabled(final boolean enabled) {
145:                    this .enabled = enabled;
146:                }
147:            }
148:
149:            /**
150:             * Whether the keyboard should kick into debugging mode. This causes real
151:             * key bindings trapped by the key binding architecture to be reported.
152:             */
153:            private static final boolean DEBUG = Policy.DEBUG_KEY_BINDINGS;
154:
155:            /**
156:             * Whether the keyboard should report every event received by its global
157:             * filter.
158:             */
159:            private static final boolean DEBUG_VERBOSE = Policy.DEBUG_KEY_BINDINGS_VERBOSE;
160:
161:            /**
162:             * The time in milliseconds to wait after pressing a key before displaying
163:             * the key assist dialog.
164:             */
165:            private static final int DELAY = 1000;
166:
167:            /** The collection of keys that are to be processed out-of-order. */
168:            static KeySequence outOfOrderKeys;
169:
170:            /**
171:             * The translation bundle in which to look up internationalized text.
172:             */
173:            private final static ResourceBundle RESOURCE_BUNDLE = ResourceBundle
174:                    .getBundle(WorkbenchKeyboard.class.getName());
175:
176:            static {
177:
178:                try {
179:                    outOfOrderKeys = KeySequence.getInstance("ESC DEL"); //$NON-NLS-1$
180:                } catch (ParseException e) {
181:                    outOfOrderKeys = KeySequence.getInstance();
182:                    String message = "Could not parse out-of-order keys definition: 'ESC DEL'.  Continuing with no out-of-order keys."; //$NON-NLS-1$
183:                    WorkbenchPlugin.log(message, new Status(IStatus.ERROR,
184:                            WorkbenchPlugin.PI_WORKBENCH, 0, message, e));
185:                }
186:            }
187:
188:            /**
189:             * Generates any key strokes that are near matches to the given event. The
190:             * first such key stroke is always the exactly matching key stroke.
191:             * 
192:             * @param event
193:             *            The event from which the key strokes should be generated; must
194:             *            not be <code>null</code>.
195:             * @return The set of nearly matching key strokes. It is never
196:             *         <code>null</code>, but may be empty.
197:             */
198:            public static List generatePossibleKeyStrokes(Event event) {
199:                final List keyStrokes = new ArrayList(3);
200:
201:                /*
202:                 * If this is not a keyboard event, then there are no key strokes. This
203:                 * can happen if we are listening to focus traversal events.
204:                 */
205:                if ((event.stateMask == 0) && (event.keyCode == 0)
206:                        && (event.character == 0)) {
207:                    return keyStrokes;
208:                }
209:
210:                // Add each unique key stroke to the list for consideration.
211:                final int firstAccelerator = SWTKeySupport
212:                        .convertEventToUnmodifiedAccelerator(event);
213:                keyStrokes.add(SWTKeySupport
214:                        .convertAcceleratorToKeyStroke(firstAccelerator));
215:
216:                // We shouldn't allow delete to undergo shift resolution.
217:                if (event.character == SWT.DEL) {
218:                    return keyStrokes;
219:                }
220:
221:                final int secondAccelerator = SWTKeySupport
222:                        .convertEventToUnshiftedModifiedAccelerator(event);
223:                if (secondAccelerator != firstAccelerator) {
224:                    keyStrokes.add(SWTKeySupport
225:                            .convertAcceleratorToKeyStroke(secondAccelerator));
226:                }
227:
228:                final int thirdAccelerator = SWTKeySupport
229:                        .convertEventToModifiedAccelerator(event);
230:                if ((thirdAccelerator != secondAccelerator)
231:                        && (thirdAccelerator != firstAccelerator)) {
232:                    keyStrokes.add(SWTKeySupport
233:                            .convertAcceleratorToKeyStroke(thirdAccelerator));
234:                }
235:
236:                return keyStrokes;
237:            }
238:
239:            /**
240:             * <p>
241:             * Determines whether the given event represents a key press that should be
242:             * handled as an out-of-order event. An out-of-order key press is one that
243:             * is passed to the focus control first. Only if the focus control fails to
244:             * respond will the regular key bindings get applied.
245:             * </p>
246:             * <p>
247:             * Care must be taken in choosing which keys are chosen as out-of-order
248:             * keys. This method has only been designed and test to work with the
249:             * unmodified "Escape" key stroke.
250:             * </p>
251:             * 
252:             * @param keyStrokes
253:             *            The key stroke in which to look for out-of-order keys; must
254:             *            not be <code>null</code>.
255:             * @return <code>true</code> if the key is an out-of-order key;
256:             *         <code>false</code> otherwise.
257:             */
258:            private static boolean isOutOfOrderKey(List keyStrokes) {
259:                // Compare to see if one of the possible key strokes is out of order.
260:                final KeyStroke[] outOfOrderKeyStrokes = outOfOrderKeys
261:                        .getKeyStrokes();
262:                final int outOfOrderKeyStrokesLength = outOfOrderKeyStrokes.length;
263:                for (int i = 0; i < outOfOrderKeyStrokesLength; i++) {
264:                    if (keyStrokes.contains(outOfOrderKeyStrokes[i])) {
265:                        return true;
266:                    }
267:                }
268:                return false;
269:            }
270:
271:            /**
272:             * The binding manager to be used to resolve key bindings. This member
273:             * variable will be <code>null</code> if it has not yet been initialized.
274:             */
275:            private IBindingService bindingService = null;
276:
277:            /**
278:             * The <code>KeyAssistDialog</code> displayed to the user to assist them
279:             * in completing a multi-stroke keyboard shortcut.
280:             * 
281:             * @since 3.1
282:             */
283:            private KeyAssistDialog keyAssistDialog = null;
284:
285:            /**
286:             * The listener that runs key events past the global key bindings.
287:             */
288:            private final KeyDownFilter keyDownFilter = new KeyDownFilter();
289:
290:            /**
291:             * The single out-of-order listener used by the workbench. This listener is
292:             * attached to one widget at a time, and is used to catch key down events
293:             * after all processing is done. This technique is used so that some keys
294:             * will have their native behaviour happen first.
295:             * 
296:             * @since 3.1
297:             */
298:            private final OutOfOrderListener outOfOrderListener = new OutOfOrderListener(
299:                    this );
300:
301:            /**
302:             * The single out-of-order verify listener used by the workbench. This
303:             * listener is attached to one</code> StyledText</code> at a time, and is
304:             * used to catch verify events after all processing is done. This technique
305:             * is used so that some keys will have their native behaviour happen first.
306:             * 
307:             * @since 3.1
308:             */
309:            private final OutOfOrderVerifyListener outOfOrderVerifyListener = new OutOfOrderVerifyListener(
310:                    outOfOrderListener);
311:
312:            /**
313:             * The time at which the last timer was started. This is used to judge if a
314:             * sufficient amount of time has elapsed. This is simply the output of
315:             * <code>System.currentTimeMillis()</code>.
316:             */
317:            private long startTime = Long.MAX_VALUE;
318:
319:            /**
320:             * The mode is the current state of the key binding architecture. In the
321:             * case of multi-stroke key bindings, this can be a partially complete key
322:             * binding.
323:             */
324:            private final KeyBindingState state;
325:
326:            /**
327:             * The window listener responsible for maintaining internal state as the
328:             * focus moves between windows on the desktop.
329:             */
330:            private final IWindowListener windowListener = new IWindowListener() {
331:
332:                /*
333:                 * (non-Javadoc)
334:                 * 
335:                 * @see org.eclipse.ui.IWindowListener#windowActivated(org.eclipse.ui.IWorkbenchWindow)
336:                 */
337:                public void windowActivated(IWorkbenchWindow window) {
338:                    checkActiveWindow(window);
339:                }
340:
341:                /*
342:                 * (non-Javadoc)
343:                 * 
344:                 * @see org.eclipse.ui.IWindowListener#windowClosed(org.eclipse.ui.IWorkbenchWindow)
345:                 */
346:                public void windowClosed(IWorkbenchWindow window) {
347:                    // Do nothing.
348:                }
349:
350:                /*
351:                 * (non-Javadoc)
352:                 * 
353:                 * @see org.eclipse.ui.IWindowListener#windowDeactivated(org.eclipse.ui.IWorkbenchWindow)
354:                 */
355:                public void windowDeactivated(IWorkbenchWindow window) {
356:                    // Do nothing
357:                }
358:
359:                /*
360:                 * (non-Javadoc)
361:                 * 
362:                 * @see org.eclipse.ui.IWindowListener#windowOpened(org.eclipse.ui.IWorkbenchWindow)
363:                 */
364:                public void windowOpened(IWorkbenchWindow window) {
365:                    // Do nothing.
366:                }
367:            };
368:
369:            /**
370:             * The workbench on which this keyboard interface should act.
371:             */
372:            private final IWorkbench workbench;
373:
374:            /**
375:             * Constructs a new instance of <code>WorkbenchKeyboard</code> associated
376:             * with a particular workbench.
377:             * 
378:             * @param associatedWorkbench
379:             *            The workbench with which this keyboard interface should work;
380:             *            must not be <code>null</code>.
381:             * @since 3.1
382:             */
383:            public WorkbenchKeyboard(Workbench associatedWorkbench) {
384:                workbench = associatedWorkbench;
385:                state = new KeyBindingState(associatedWorkbench);
386:                workbench.addWindowListener(windowListener);
387:            }
388:
389:            /**
390:             * Verifies that the active workbench window is the same as the workbench
391:             * window associated with the state. This is used to verify that the state
392:             * is properly reset as focus changes. When they are not the same, the state
393:             * is reset and associated with the newly activated window.
394:             * 
395:             * @param window
396:             *            The activated window; must not be <code>null</code>.
397:             */
398:            private void checkActiveWindow(IWorkbenchWindow window) {
399:                if (!window.equals(state.getAssociatedWindow())) {
400:                    resetState(true);
401:                    state.setAssociatedWindow(window);
402:                }
403:            }
404:
405:            /**
406:             * Closes the multi-stroke key binding assistant shell, if it exists and
407:             * isn't already disposed.
408:             */
409:            private void closeMultiKeyAssistShell() {
410:                if (keyAssistDialog != null) {
411:                    final Shell shell = keyAssistDialog.getShell();
412:                    if ((shell != null) && (!shell.isDisposed())
413:                            && (shell.isVisible())) {
414:                        keyAssistDialog.close(true);
415:                    }
416:                }
417:            }
418:
419:            /**
420:             * Performs the actual execution of the command by looking up the current
421:             * handler from the command manager. If there is a handler and it is
422:             * enabled, then it tries the actual execution. Execution failures are
423:             * logged. When this method completes, the key binding state is reset.
424:             * 
425:             * @param binding
426:             *            The binding that should be executed; should not be
427:             *            <code>null</code>.
428:             * @param trigger
429:             *            The triggering event; may be <code>null</code>.
430:             * @return <code>true</code> if there was a handler; <code>false</code>
431:             *         otherwise.
432:             * @throws CommandException
433:             *             if the handler does not complete execution for some reason.
434:             *             It is up to the caller of this method to decide whether to
435:             *             log the message, display a dialog, or ignore this exception
436:             *             entirely.
437:             */
438:            final boolean executeCommand(final Binding binding,
439:                    final Event trigger) throws CommandException {
440:                final ParameterizedCommand parameterizedCommand = binding
441:                        .getParameterizedCommand();
442:
443:                if (DEBUG) {
444:                    Tracing.printTrace("KEYS", //$NON-NLS-1$
445:                            "WorkbenchKeyboard.executeCommand(commandId = '" //$NON-NLS-1$
446:                                    + parameterizedCommand.getId()
447:                                    + "', parameters = " //$NON-NLS-1$
448:                                    + parameterizedCommand.getParameterMap()
449:                                    + ')');
450:                }
451:
452:                // Reset the key binding state (close window, clear status line, etc.)
453:                resetState(false);
454:
455:                // Dispatch to the handler.
456:                final Command command = parameterizedCommand.getCommand();
457:                final boolean commandDefined = command.isDefined();
458:                final boolean commandHandled = command.isHandled();
459:                final boolean commandEnabled = command.isEnabled();
460:
461:                if (DEBUG && DEBUG_VERBOSE) {
462:                    if (!commandDefined) {
463:                        Tracing.printTrace("KEYS", "    not defined"); //$NON-NLS-1$ //$NON-NLS-2$
464:                    } else if (!commandHandled) {
465:                        Tracing.printTrace("KEYS", "    not handled"); //$NON-NLS-1$ //$NON-NLS-2$
466:                    } else if (!commandEnabled) {
467:                        Tracing.printTrace("KEYS", "    not enabled"); //$NON-NLS-1$ //$NON-NLS-2$
468:                    }
469:                }
470:
471:                try {
472:                    final IHandlerService handlerService = (IHandlerService) workbench
473:                            .getService(IHandlerService.class);
474:                    handlerService
475:                            .executeCommand(parameterizedCommand, trigger);
476:                } catch (final NotDefinedException e) {
477:                    // The command is not defined. Forwarded to the IExecutionListener.
478:                } catch (final NotEnabledException e) {
479:                    // The command is not enabled. Forwarded to the IExecutionListener.
480:                } catch (final NotHandledException e) {
481:                    // There is no handler. Forwarded to the IExecutionListener.
482:                }
483:
484:                /*
485:                 * Now that the command has executed (and had the opportunity to use the
486:                 * remembered state of the dialog), it is safe to delete that
487:                 * information.
488:                 */
489:                if (keyAssistDialog != null) {
490:                    keyAssistDialog.clearRememberedState();
491:                }
492:
493:                return (commandDefined && commandHandled);
494:            }
495:
496:            /**
497:             * <p>
498:             * Launches the command matching a the typed key. This filter an incoming
499:             * <code>SWT.KeyDown</code> or <code>SWT.Traverse</code> event at the
500:             * level of the display (i.e., before it reaches the widgets). It does not
501:             * allow processing in a dialog or if the key strokes does not contain a
502:             * natural key.
503:             * </p>
504:             * <p>
505:             * Some key strokes (defined as a property) are declared as out-of-order
506:             * keys. This means that they are processed by the widget <em>first</em>.
507:             * Only if the other widget listeners do no useful work does it try to
508:             * process key bindings. For example, "ESC" can cancel the current widget
509:             * action, if there is one, without triggering key bindings.
510:             * </p>
511:             * 
512:             * @param event
513:             *            The incoming event; must not be <code>null</code>.
514:             */
515:            private void filterKeySequenceBindings(Event event) {
516:                /*
517:                 * Only process key strokes containing natural keys to trigger key
518:                 * bindings.
519:                 */
520:                if ((event.keyCode & SWT.MODIFIER_MASK) != 0) {
521:                    return;
522:                }
523:
524:                // Allow special key out-of-order processing.
525:                List keyStrokes = generatePossibleKeyStrokes(event);
526:                if (isOutOfOrderKey(keyStrokes)) {
527:                    Widget widget = event.widget;
528:                    if ((event.character == SWT.DEL)
529:                            && ((event.stateMask & SWT.MODIFIER_MASK) == 0)
530:                            && ((widget instanceof  Text) || (widget instanceof  Combo))) {
531:                        /*
532:                         * KLUDGE. Bug 54654. The text widget relies on no listener
533:                         * doing any work before dispatching the native delete event.
534:                         * This does not work, as we are restricted to listeners.
535:                         * However, it can be said that pressing a delete key in a text
536:                         * widget will never use key bindings. This can be shown be
537:                         * considering how the event dispatching is expected to work in
538:                         * a text widget. So, we should do nothing ... ever.
539:                         */
540:                        return;
541:
542:                    } else if (widget instanceof  StyledText) {
543:
544:                        if (event.type == SWT.KeyDown) {
545:                            /*
546:                             * KLUDGE. Some people try to do useful work in verify
547:                             * listeners. The way verify listeners work in SWT, we need
548:                             * to verify the key as well; otherwise, we can't detect
549:                             * that useful work has been done.
550:                             */
551:                            if (!outOfOrderVerifyListener.isActive(event.time)) {
552:                                ((StyledText) widget)
553:                                        .addVerifyKeyListener(outOfOrderVerifyListener);
554:                                outOfOrderVerifyListener.setActive(event.time);
555:                            }
556:                        }
557:
558:                    } else {
559:                        if (!outOfOrderListener.isActive(event.time)) {
560:                            widget.addListener(SWT.KeyDown, outOfOrderListener);
561:                            outOfOrderListener.setActive(event.time);
562:                        }
563:
564:                    }
565:
566:                    /*
567:                     * Otherwise, we count on a key down arriving eventually. Expecting
568:                     * out of order handling on Ctrl+Tab, for example, is a bad idea
569:                     * (stick to keys that are not window traversal keys).
570:                     */
571:
572:                } else {
573:                    processKeyEvent(keyStrokes, event);
574:
575:                }
576:            }
577:
578:            /**
579:             * An accessor for the filter that processes key down and traverse events on
580:             * the display.
581:             * 
582:             * @return The global key down and traverse filter; never <code>null</code>.
583:             */
584:            public KeyDownFilter getKeyDownFilter() {
585:                return keyDownFilter;
586:            }
587:
588:            /**
589:             * Determines whether the key sequence is a perfect match for any command.
590:             * If there is a match, then the corresponding command identifier is
591:             * returned.
592:             * 
593:             * @param keySequence
594:             *            The key sequence to check for a match; must never be
595:             *            <code>null</code>.
596:             * @return The binding for the perfectly matching command; <code>null</code>
597:             *         if no command matches.
598:             */
599:            private Binding getPerfectMatch(KeySequence keySequence) {
600:                if (bindingService == null) {
601:                    bindingService = (IBindingService) workbench
602:                            .getService(IBindingService.class);
603:                }
604:                return bindingService.getPerfectMatch(keySequence);
605:            }
606:
607:            final KeySequence getBuffer() {
608:                return state.getCurrentSequence();
609:            }
610:
611:            /**
612:             * Changes the key binding state to the given value. This should be an
613:             * incremental change, but there are no checks to guarantee this is so. It
614:             * also sets up a <code>Shell</code> to be displayed after one second has
615:             * elapsed. This shell will show the user the possible completions for what
616:             * they have typed.
617:             * 
618:             * @param sequence
619:             *            The new key sequence for the state; should not be
620:             *            <code>null</code>.
621:             */
622:            private void incrementState(KeySequence sequence) {
623:                // Record the starting time.
624:                startTime = System.currentTimeMillis();
625:                final long myStartTime = startTime;
626:
627:                // Update the state.
628:                state.setCurrentSequence(sequence);
629:                state.setAssociatedWindow(workbench.getActiveWorkbenchWindow());
630:
631:                // After some time, open a shell displaying the possible completions.
632:                final Display display = workbench.getDisplay();
633:                display.timerExec(DELAY, new Runnable() {
634:                    public void run() {
635:                        if ((System.currentTimeMillis() > (myStartTime - DELAY))
636:                                && (startTime == myStartTime)) {
637:                            openMultiKeyAssistShell();
638:                        }
639:                    }
640:                });
641:            }
642:
643:            /**
644:             * Determines whether the key sequence partially matches on of the active
645:             * key bindings.
646:             * 
647:             * @param keySequence
648:             *            The key sequence to check for a partial match; must never be
649:             *            <code>null</code>.
650:             * @return <code>true</code> if there is a partial match;
651:             *         <code>false</code> otherwise.
652:             */
653:            private boolean isPartialMatch(KeySequence keySequence) {
654:                if (bindingService == null) {
655:                    bindingService = (IBindingService) workbench
656:                            .getService(IBindingService.class);
657:                }
658:                return bindingService.isPartialMatch(keySequence);
659:            }
660:
661:            /**
662:             * Determines whether the key sequence perfectly matches on of the active
663:             * key bindings.
664:             * 
665:             * @param keySequence
666:             *            The key sequence to check for a perfect match; must never be
667:             *            <code>null</code>.
668:             * @return <code>true</code> if there is a perfect match;
669:             *         <code>false</code> otherwise.
670:             */
671:            private boolean isPerfectMatch(KeySequence keySequence) {
672:                if (bindingService == null) {
673:                    bindingService = (IBindingService) workbench
674:                            .getService(IBindingService.class);
675:                }
676:                return bindingService.isPerfectMatch(keySequence);
677:            }
678:
679:            /**
680:             * Logs the given exception, and opens a dialog explaining the failure.
681:             * 
682:             * @param e
683:             *            The exception to log; must not be <code>null</code>.
684:             * @param command
685:             *            The parameterized command for the binding to execute; may be
686:             *            <code>null</code>.
687:             */
688:            final void logException(final CommandException e,
689:                    final ParameterizedCommand command) {
690:                Throwable nestedException = e.getCause();
691:                Throwable exception = (nestedException == null) ? e
692:                        : nestedException;
693:
694:                // If we can, include the command name in the exception.
695:                String message = null;
696:                if (command != null) {
697:                    try {
698:                        final String name = command.getCommand().getName();
699:                        message = MessageFormat.format(Util.translateString(
700:                                RESOURCE_BUNDLE,
701:                                "ExecutionError.MessageCommandName"), //$NON-NLS-1$
702:                                new Object[] { name });
703:                    } catch (final NotDefinedException nde) {
704:                        // Fall through (message == null)
705:                    }
706:                }
707:                if (message == null) {
708:                    message = Util.translateString(RESOURCE_BUNDLE,
709:                            "ExecutionError.Message"); //$NON-NLS-1$
710:                }
711:
712:                String exceptionMessage = exception.getMessage();
713:                if (exceptionMessage == null) {
714:                    exceptionMessage = exception.getClass().getName();
715:                }
716:                IStatus status = new Status(IStatus.ERROR,
717:                        WorkbenchPlugin.PI_WORKBENCH, 0, exceptionMessage,
718:                        exception);
719:                WorkbenchPlugin.log(message, status);
720:                StatusUtil.handleStatus(message, exception, StatusManager.SHOW);
721:            }
722:
723:            /**
724:             * Opens a <code>KeyAssistDialog</code> to assist the user in completing a
725:             * multi-stroke key binding. This method lazily creates a
726:             * <code>keyAssistDialog</code> and shares it between executions.
727:             */
728:            public final void openMultiKeyAssistShell() {
729:                if (keyAssistDialog == null) {
730:                    keyAssistDialog = new KeyAssistDialog(workbench, this ,
731:                            state);
732:                }
733:                if (keyAssistDialog.getShell() == null) {
734:                    keyAssistDialog.setParentShell(Util.getShellToParentOn());
735:                }
736:                keyAssistDialog.open();
737:            }
738:
739:            /**
740:             * Opens the key assist dialog to offer the user the choice of a binding to
741:             * pick from the collection of bindings.
742:             * 
743:             * @param bindings
744:             *            a collection of Binding objects
745:             * @since 3.3
746:             */
747:            public final void openKeyAssistShell(final Collection bindings) {
748:                if (keyAssistDialog == null) {
749:                    keyAssistDialog = new KeyAssistDialog(workbench,
750:                            WorkbenchKeyboard.this , state);
751:                }
752:                if (keyAssistDialog.getShell() == null) {
753:                    keyAssistDialog.setParentShell(Util.getShellToParentOn());
754:                }
755:                keyAssistDialog.open(bindings);
756:            }
757:
758:            /**
759:             * Processes a key press with respect to the key binding architecture. This
760:             * updates the mode of the command manager, and runs the current handler for
761:             * the command that matches the key sequence, if any.
762:             * 
763:             * @param potentialKeyStrokes
764:             *            The key strokes that could potentially match, in the order of
765:             *            priority; must not be <code>null</code>.
766:             * @param event
767:             *            The event; may be <code>null</code>.
768:             * @return <code>true</code> if a command is executed; <code>false</code>
769:             *         otherwise.
770:             */
771:            public boolean press(List potentialKeyStrokes, Event event) {
772:                if (DEBUG && DEBUG_VERBOSE) {
773:                    Tracing.printTrace("KEYS", //$NON-NLS-1$
774:                            "WorkbenchKeyboard.press(potentialKeyStrokes = " //$NON-NLS-1$
775:                                    + potentialKeyStrokes + ')');
776:                }
777:
778:                /*
779:                 * KLUDGE. This works around a couple of specific problems in how GTK+
780:                 * works. The first problem is the ordering of key press events with
781:                 * respect to shell activation events. If on the event thread a dialog
782:                 * is about to open, and the user presses a key, the key press event
783:                 * will arrive before the shell activation event. From the perspective
784:                 * of Eclipse, this means that things like two "Open Type" dialogs can
785:                 * appear if "Ctrl+Shift+T" is pressed twice rapidly. For more
786:                 * information, please see Bug 95792. The second problem is simply a bug
787:                 * in GTK+, for which an incomplete workaround currently exists in SWT.
788:                 * This makes shell activation events unreliable. Please see Bug 56231
789:                 * and Bug 95222 for more information.
790:                 */
791:                if ("gtk".equals(SWT.getPlatform())) { //$NON-NLS-1$
792:                    final Widget widget = event.widget;
793:
794:                    // Update the contexts.
795:                    final ContextService contextService = (ContextService) workbench
796:                            .getService(IContextService.class);
797:                    if ((widget instanceof  Control) && (!widget.isDisposed())) {
798:                        final Shell shell = ((Control) widget).getShell();
799:                        contextService.updateShellKludge(shell);
800:                    } else {
801:                        contextService.updateShellKludge();
802:                    }
803:
804:                    // Update the handlers.
805:                    final HandlerService handlerService = (HandlerService) workbench
806:                            .getService(IHandlerService.class);
807:                    if ((widget instanceof  Control) && (!widget.isDisposed())) {
808:                        final Shell shell = ((Control) widget).getShell();
809:                        handlerService.updateShellKludge(shell);
810:                    } else {
811:                        handlerService.updateShellKludge();
812:                    }
813:                }
814:
815:                KeySequence errorSequence = null;
816:                Collection errorMatch = null;
817:
818:                KeySequence sequenceBeforeKeyStroke = state
819:                        .getCurrentSequence();
820:                for (Iterator iterator = potentialKeyStrokes.iterator(); iterator
821:                        .hasNext();) {
822:                    KeySequence sequenceAfterKeyStroke = KeySequence
823:                            .getInstance(sequenceBeforeKeyStroke,
824:                                    (KeyStroke) iterator.next());
825:                    if (isPartialMatch(sequenceAfterKeyStroke)) {
826:                        incrementState(sequenceAfterKeyStroke);
827:                        return true;
828:
829:                    } else if (isPerfectMatch(sequenceAfterKeyStroke)) {
830:                        final Binding binding = getPerfectMatch(sequenceAfterKeyStroke);
831:                        try {
832:                            return executeCommand(binding, event)
833:                                    || !sequenceBeforeKeyStroke.isEmpty();
834:                        } catch (final CommandException e) {
835:                            logException(e, binding.getParameterizedCommand());
836:                            return true;
837:                        }
838:
839:                    } else if ((keyAssistDialog != null)
840:                            && (keyAssistDialog.getShell() != null)
841:                            && ((event.keyCode == SWT.ARROW_DOWN)
842:                                    || (event.keyCode == SWT.ARROW_UP)
843:                                    || (event.keyCode == SWT.ARROW_LEFT)
844:                                    || (event.keyCode == SWT.ARROW_RIGHT)
845:                                    || (event.keyCode == SWT.CR)
846:                                    || (event.keyCode == SWT.PAGE_UP) || (event.keyCode == SWT.PAGE_DOWN))) {
847:                        // We don't want to swallow keyboard navigation keys.
848:                        return false;
849:
850:                    } else {
851:                        Collection match = (InternalPolicy.currentConflicts == null ? null
852:                                : (Collection) InternalPolicy.currentConflicts
853:                                        .get(sequenceAfterKeyStroke));
854:                        if (match != null) {
855:                            errorSequence = sequenceAfterKeyStroke;
856:                            errorMatch = match;
857:                        }
858:                    }
859:                }
860:
861:                resetState(true);
862:                if (sequenceBeforeKeyStroke.isEmpty() && errorSequence != null) {
863:                    openKeyAssistShell(errorMatch);
864:                }
865:                return !sequenceBeforeKeyStroke.isEmpty();
866:            }
867:
868:            /**
869:             * <p>
870:             * Actually performs the processing of the key event by interacting with the
871:             * <code>ICommandManager</code>. If work is carried out, then the event
872:             * is stopped here (i.e., <code>event.doit = false</code>). It does not
873:             * do any processing if there are no matching key strokes.
874:             * </p>
875:             * <p>
876:             * If the active <code>Shell</code> is not the same as the one to which
877:             * the state is associated, then a reset occurs.
878:             * </p>
879:             * 
880:             * @param keyStrokes
881:             *            The set of all possible matching key strokes; must not be
882:             *            <code>null</code>.
883:             * @param event
884:             *            The event to process; must not be <code>null</code>.
885:             */
886:            void processKeyEvent(List keyStrokes, Event event) {
887:                // Dispatch the keyboard shortcut, if any.
888:                boolean eatKey = false;
889:                if (!keyStrokes.isEmpty()) {
890:                    eatKey = press(keyStrokes, event);
891:                }
892:
893:                if (eatKey) {
894:                    switch (event.type) {
895:                    case SWT.KeyDown:
896:                        event.doit = false;
897:                        break;
898:                    case SWT.Traverse:
899:                        event.detail = SWT.TRAVERSE_NONE;
900:                        event.doit = true;
901:                        break;
902:                    default:
903:                    }
904:                    event.type = SWT.NONE;
905:                }
906:            }
907:
908:            /**
909:             * Resets the state, and cancels any running timers. If there is a
910:             * <code>Shell</code> currently open, then it closes it.
911:             * 
912:             * @param clearRememberedState
913:             *            Whether the remembered state (dialog bounds) of the key assist
914:             *            should be forgotten immediately as well.
915:             */
916:            private final void resetState(final boolean clearRememberedState) {
917:                startTime = Long.MAX_VALUE;
918:                state.reset();
919:                closeMultiKeyAssistShell();
920:                if ((keyAssistDialog != null) && clearRememberedState) {
921:                    keyAssistDialog.clearRememberedState();
922:                }
923:            }
924:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.