1 11 package org.eclipse.ui.internal.keys; 12 13 import java.util.ArrayList ; 14 import java.util.Collection ; 15 import java.util.Iterator ; 16 import java.util.List ; 17 import java.util.ResourceBundle ; 18 19 import org.eclipse.core.commands.Command; 20 import org.eclipse.core.commands.NotEnabledException; 21 import org.eclipse.core.commands.NotHandledException; 22 import org.eclipse.core.commands.ParameterizedCommand; 23 import org.eclipse.core.commands.common.CommandException; 24 import org.eclipse.core.commands.common.NotDefinedException; 25 import org.eclipse.core.commands.util.Tracing; 26 import org.eclipse.core.runtime.IStatus; 27 import org.eclipse.core.runtime.Status; 28 import org.eclipse.jface.bindings.Binding; 29 import org.eclipse.jface.bindings.keys.KeySequence; 30 import org.eclipse.jface.bindings.keys.KeyStroke; 31 import org.eclipse.jface.bindings.keys.ParseException; 32 import org.eclipse.jface.bindings.keys.SWTKeySupport; 33 import org.eclipse.jface.internal.InternalPolicy; 34 import org.eclipse.swt.SWT; 35 import org.eclipse.swt.custom.StyledText; 36 import org.eclipse.swt.widgets.Combo; 37 import org.eclipse.swt.widgets.Control; 38 import org.eclipse.swt.widgets.Display; 39 import org.eclipse.swt.widgets.Event; 40 import org.eclipse.swt.widgets.Listener; 41 import org.eclipse.swt.widgets.Shell; 42 import org.eclipse.swt.widgets.Text; 43 import org.eclipse.swt.widgets.Widget; 44 import org.eclipse.ui.IWindowListener; 45 import org.eclipse.ui.IWorkbench; 46 import org.eclipse.ui.IWorkbenchWindow; 47 import org.eclipse.ui.contexts.IContextService; 48 import org.eclipse.ui.handlers.IHandlerService; 49 import org.eclipse.ui.internal.Workbench; 50 import org.eclipse.ui.internal.WorkbenchPlugin; 51 import org.eclipse.ui.internal.contexts.ContextService; 52 import org.eclipse.ui.internal.handlers.HandlerService; 53 import org.eclipse.ui.internal.misc.Policy; 54 import org.eclipse.ui.internal.misc.StatusUtil; 55 import org.eclipse.ui.internal.util.Util; 56 import org.eclipse.ui.keys.IBindingService; 57 import org.eclipse.ui.statushandlers.StatusManager; 58 59 import com.ibm.icu.text.MessageFormat; 60 61 77 public final class WorkbenchKeyboard { 78 79 86 public final class KeyDownFilter implements Listener { 87 88 91 private transient boolean enabled = true; 92 93 99 public final void handleEvent(final Event event) { 100 if (!enabled) { 101 return; 102 } 103 104 if (DEBUG && DEBUG_VERBOSE) { 105 final StringBuffer buffer = new StringBuffer ( 106 "Listener.handleEvent(type = "); switch (event.type) { 108 case SWT.KeyDown: 109 buffer.append("KeyDown"); break; 111 case SWT.Traverse: 112 buffer.append("Traverse"); break; 114 default: 115 buffer.append(event.type); 116 } 117 buffer.append(", stateMask = 0x" + Integer.toHexString(event.stateMask) 119 + ", keyCode = 0x" + Integer.toHexString(event.keyCode) + ", time = " + event.time + ", character = 0x" + Integer.toHexString(event.character) + ")"); Tracing.printTrace("KEYS", buffer.toString()); } 125 126 filterKeySequenceBindings(event); 127 } 128 129 134 public final boolean isEnabled() { 135 return enabled; 136 } 137 138 144 public final void setEnabled(final boolean enabled) { 145 this.enabled = enabled; 146 } 147 } 148 149 153 private static final boolean DEBUG = Policy.DEBUG_KEY_BINDINGS; 154 155 159 private static final boolean DEBUG_VERBOSE = Policy.DEBUG_KEY_BINDINGS_VERBOSE; 160 161 165 private static final int DELAY = 1000; 166 167 168 static KeySequence outOfOrderKeys; 169 170 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"); } 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."; WorkbenchPlugin.log(message, new Status(IStatus.ERROR, 184 WorkbenchPlugin.PI_WORKBENCH, 0, message, e)); 185 } 186 } 187 188 198 public static List generatePossibleKeyStrokes(Event event) { 199 final List keyStrokes = new ArrayList (3); 200 201 205 if ((event.stateMask == 0) && (event.keyCode == 0) 206 && (event.character == 0)) { 207 return keyStrokes; 208 } 209 210 final int firstAccelerator = SWTKeySupport 212 .convertEventToUnmodifiedAccelerator(event); 213 keyStrokes.add(SWTKeySupport 214 .convertAcceleratorToKeyStroke(firstAccelerator)); 215 216 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 258 private static boolean isOutOfOrderKey(List keyStrokes) { 259 final KeyStroke[] outOfOrderKeyStrokes = outOfOrderKeys.getKeyStrokes(); 261 final int outOfOrderKeyStrokesLength = outOfOrderKeyStrokes.length; 262 for (int i = 0; i < outOfOrderKeyStrokesLength; i++) { 263 if (keyStrokes.contains(outOfOrderKeyStrokes[i])) { 264 return true; 265 } 266 } 267 return false; 268 } 269 270 274 private IBindingService bindingService = null; 275 276 282 private KeyAssistDialog keyAssistDialog = null; 283 284 287 private final KeyDownFilter keyDownFilter = new KeyDownFilter(); 288 289 297 private final OutOfOrderListener outOfOrderListener = new OutOfOrderListener( 298 this); 299 300 308 private final OutOfOrderVerifyListener outOfOrderVerifyListener = new OutOfOrderVerifyListener( 309 outOfOrderListener); 310 311 316 private long startTime = Long.MAX_VALUE; 317 318 323 private final KeyBindingState state; 324 325 329 private final IWindowListener windowListener = new IWindowListener() { 330 331 336 public void windowActivated(IWorkbenchWindow window) { 337 checkActiveWindow(window); 338 } 339 340 345 public void windowClosed(IWorkbenchWindow window) { 346 } 348 349 354 public void windowDeactivated(IWorkbenchWindow window) { 355 } 357 358 363 public void windowOpened(IWorkbenchWindow window) { 364 } 366 }; 367 368 371 private final IWorkbench workbench; 372 373 382 public WorkbenchKeyboard(Workbench associatedWorkbench) { 383 workbench = associatedWorkbench; 384 state = new KeyBindingState(associatedWorkbench); 385 workbench.addWindowListener(windowListener); 386 } 387 388 397 private void checkActiveWindow(IWorkbenchWindow window) { 398 if (!window.equals(state.getAssociatedWindow())) { 399 resetState(true); 400 state.setAssociatedWindow(window); 401 } 402 } 403 404 408 private void closeMultiKeyAssistShell() { 409 if (keyAssistDialog != null) { 410 final Shell shell = keyAssistDialog.getShell(); 411 if ((shell != null) && (!shell.isDisposed()) && (shell.isVisible())) { 412 keyAssistDialog.close(true); 413 } 414 } 415 } 416 417 436 final boolean executeCommand(final Binding binding, final Event trigger) 437 throws CommandException { 438 final ParameterizedCommand parameterizedCommand = binding 439 .getParameterizedCommand(); 440 441 if (DEBUG) { 442 Tracing.printTrace("KEYS", "WorkbenchKeyboard.executeCommand(commandId = '" + parameterizedCommand.getId() + "', parameters = " + parameterizedCommand.getParameterMap() + ')'); 446 } 447 448 resetState(false); 450 451 final Command command = parameterizedCommand.getCommand(); 453 final boolean commandDefined = command.isDefined(); 454 final boolean commandHandled = command.isHandled(); 455 final boolean commandEnabled = command.isEnabled(); 456 457 if (DEBUG && DEBUG_VERBOSE) { 458 if (!commandDefined) { 459 Tracing.printTrace("KEYS", " not defined"); } else if (!commandHandled) { 461 Tracing.printTrace("KEYS", " not handled"); } else if (!commandEnabled) { 463 Tracing.printTrace("KEYS", " not enabled"); } 465 } 466 467 try { 468 final IHandlerService handlerService = (IHandlerService) workbench 469 .getService(IHandlerService.class); 470 handlerService.executeCommand(parameterizedCommand, trigger); 471 } catch (final NotDefinedException e) { 472 } catch (final NotEnabledException e) { 474 } catch (final NotHandledException e) { 476 } 478 479 484 if (keyAssistDialog != null) { 485 keyAssistDialog.clearRememberedState(); 486 } 487 488 return (commandDefined && commandHandled); 489 } 490 491 510 private void filterKeySequenceBindings(Event event) { 511 515 if ((event.keyCode & SWT.MODIFIER_MASK) != 0) { 516 return; 517 } 518 519 List keyStrokes = generatePossibleKeyStrokes(event); 521 if (isOutOfOrderKey(keyStrokes)) { 522 Widget widget = event.widget; 523 if ((event.character == SWT.DEL) 524 && ((event.stateMask & SWT.MODIFIER_MASK) == 0) 525 && ((widget instanceof Text) || (widget instanceof Combo))) { 526 535 return; 536 537 } else if (widget instanceof StyledText) { 538 539 if (event.type == SWT.KeyDown) { 540 546 if (!outOfOrderVerifyListener.isActive(event.time)) { 547 ((StyledText) widget) 548 .addVerifyKeyListener(outOfOrderVerifyListener); 549 outOfOrderVerifyListener.setActive(event.time); 550 } 551 } 552 553 } else { 554 if (!outOfOrderListener.isActive(event.time)) { 555 widget.addListener(SWT.KeyDown, outOfOrderListener); 556 outOfOrderListener.setActive(event.time); 557 } 558 559 } 560 561 566 567 } else { 568 processKeyEvent(keyStrokes, event); 569 570 } 571 } 572 573 579 public KeyDownFilter getKeyDownFilter() { 580 return keyDownFilter; 581 } 582 583 594 private Binding getPerfectMatch(KeySequence keySequence) { 595 if (bindingService == null) { 596 bindingService = (IBindingService) workbench 597 .getService(IBindingService.class); 598 } 599 return bindingService.getPerfectMatch(keySequence); 600 } 601 602 final KeySequence getBuffer() { 603 return state.getCurrentSequence(); 604 } 605 606 617 private void incrementState(KeySequence sequence) { 618 startTime = System.currentTimeMillis(); 620 final long myStartTime = startTime; 621 622 state.setCurrentSequence(sequence); 624 state.setAssociatedWindow(workbench.getActiveWorkbenchWindow()); 625 626 final Display display = workbench.getDisplay(); 628 display.timerExec(DELAY, new Runnable () { 629 public void run() { 630 if ((System.currentTimeMillis() > (myStartTime - DELAY)) 631 && (startTime == myStartTime)) { 632 openMultiKeyAssistShell(); 633 } 634 } 635 }); 636 } 637 638 648 private boolean isPartialMatch(KeySequence keySequence) { 649 if (bindingService == null) { 650 bindingService = (IBindingService) workbench 651 .getService(IBindingService.class); 652 } 653 return bindingService.isPartialMatch(keySequence); 654 } 655 656 666 private boolean isPerfectMatch(KeySequence keySequence) { 667 if (bindingService == null) { 668 bindingService = (IBindingService) workbench 669 .getService(IBindingService.class); 670 } 671 return bindingService.isPerfectMatch(keySequence); 672 } 673 674 683 final void logException(final CommandException e, 684 final ParameterizedCommand command) { 685 Throwable nestedException = e.getCause(); 686 Throwable exception = (nestedException == null) ? e : nestedException; 687 688 String message = null; 690 if (command != null) { 691 try { 692 final String name = command.getCommand().getName(); 693 message = MessageFormat.format(Util.translateString( 694 RESOURCE_BUNDLE, "ExecutionError.MessageCommandName"), new Object [] { name }); 696 } catch (final NotDefinedException nde) { 697 } 699 } 700 if (message == null) { 701 message = Util.translateString(RESOURCE_BUNDLE, 702 "ExecutionError.Message"); } 704 705 String exceptionMessage = exception.getMessage(); 706 if (exceptionMessage == null) { 707 exceptionMessage = exception.getClass().getName(); 708 } 709 IStatus status = new Status(IStatus.ERROR, 710 WorkbenchPlugin.PI_WORKBENCH, 0, exceptionMessage, exception); 711 WorkbenchPlugin.log(message, status); 712 StatusUtil.handleStatus(message, exception, StatusManager.SHOW); 713 } 714 715 720 public final void openMultiKeyAssistShell() { 721 if (keyAssistDialog == null) { 722 keyAssistDialog = new KeyAssistDialog(workbench, this, state); 723 } 724 if (keyAssistDialog.getShell() == null) { 725 keyAssistDialog.setParentShell(Util.getShellToParentOn()); 726 } 727 keyAssistDialog.open(); 728 } 729 730 738 public final void openKeyAssistShell(final Collection bindings) { 739 if (keyAssistDialog == null) { 740 keyAssistDialog = new KeyAssistDialog(workbench, 741 WorkbenchKeyboard.this, state); 742 } 743 if (keyAssistDialog.getShell() == null) { 744 keyAssistDialog.setParentShell(Util.getShellToParentOn()); 745 } 746 keyAssistDialog.open(bindings); 747 } 748 749 762 public boolean press(List potentialKeyStrokes, Event event) { 763 if (DEBUG && DEBUG_VERBOSE) { 764 Tracing.printTrace("KEYS", "WorkbenchKeyboard.press(potentialKeyStrokes = " + potentialKeyStrokes + ')'); 767 } 768 769 782 if ("gtk".equals(SWT.getPlatform())) { final Widget widget = event.widget; 784 785 final ContextService contextService = (ContextService) workbench 787 .getService(IContextService.class); 788 if ((widget instanceof Control) && (!widget.isDisposed())) { 789 final Shell shell = ((Control) widget).getShell(); 790 contextService.updateShellKludge(shell); 791 } else { 792 contextService.updateShellKludge(); 793 } 794 795 final HandlerService handlerService = (HandlerService) workbench 797 .getService(IHandlerService.class); 798 if ((widget instanceof Control) && (!widget.isDisposed())) { 799 final Shell shell = ((Control) widget).getShell(); 800 handlerService.updateShellKludge(shell); 801 } else { 802 handlerService.updateShellKludge(); 803 } 804 } 805 806 KeySequence errorSequence = null; 807 Collection errorMatch = null; 808 809 KeySequence sequenceBeforeKeyStroke = state.getCurrentSequence(); 810 for (Iterator iterator = potentialKeyStrokes.iterator(); iterator 811 .hasNext();) { 812 KeySequence sequenceAfterKeyStroke = KeySequence.getInstance( 813 sequenceBeforeKeyStroke, (KeyStroke) iterator.next()); 814 if (isPartialMatch(sequenceAfterKeyStroke)) { 815 incrementState(sequenceAfterKeyStroke); 816 return true; 817 818 } else if (isPerfectMatch(sequenceAfterKeyStroke)) { 819 final Binding binding = getPerfectMatch(sequenceAfterKeyStroke); 820 try { 821 return executeCommand(binding, event) 822 || !sequenceBeforeKeyStroke.isEmpty(); 823 } catch (final CommandException e) { 824 logException(e, binding.getParameterizedCommand()); 825 return true; 826 } 827 828 } else if ((keyAssistDialog != null) 829 && (keyAssistDialog.getShell() != null) 830 && ((event.keyCode == SWT.ARROW_DOWN) 831 || (event.keyCode == SWT.ARROW_UP) 832 || (event.keyCode == SWT.ARROW_LEFT) 833 || (event.keyCode == SWT.ARROW_RIGHT) 834 || (event.keyCode == SWT.CR) 835 || (event.keyCode == SWT.PAGE_UP) || (event.keyCode == SWT.PAGE_DOWN))) { 836 return false; 838 839 } else { 840 Collection match = (InternalPolicy.currentConflicts == null ? null 841 : (Collection ) InternalPolicy.currentConflicts 842 .get(sequenceAfterKeyStroke)); 843 if (match != null) { 844 errorSequence = sequenceAfterKeyStroke; 845 errorMatch = match; 846 } 847 } 848 } 849 850 resetState(true); 851 if (sequenceBeforeKeyStroke.isEmpty() && errorSequence != null) { 852 openKeyAssistShell(errorMatch); 853 } 854 return !sequenceBeforeKeyStroke.isEmpty(); 855 } 856 857 875 void processKeyEvent(List keyStrokes, Event event) { 876 boolean eatKey = false; 878 if (!keyStrokes.isEmpty()) { 879 eatKey = press(keyStrokes, event); 880 } 881 882 if (eatKey) { 883 switch (event.type) { 884 case SWT.KeyDown: 885 event.doit = false; 886 break; 887 case SWT.Traverse: 888 event.detail = SWT.TRAVERSE_NONE; 889 event.doit = true; 890 break; 891 default: 892 } 893 event.type = SWT.NONE; 894 } 895 } 896 897 905 private final void resetState(final boolean clearRememberedState) { 906 startTime = Long.MAX_VALUE; 907 state.reset(); 908 closeMultiKeyAssistShell(); 909 if ((keyAssistDialog != null) && clearRememberedState) { 910 keyAssistDialog.clearRememberedState(); 911 } 912 } 913 } 914 | Popular Tags |