public boolean processAction(final InputEvent e, ActionProcessor processor) {
    ActionManagerEx actionManager = ActionManagerEx.getInstanceEx();
    final Project project = PlatformDataKeys.PROJECT.getData(myContext.getDataContext());
    final boolean dumb = project != null && DumbService.getInstance(project).isDumb();
    List<AnActionEvent> nonDumbAwareAction = new ArrayList<AnActionEvent>();
    for (final AnAction action : myContext.getActions()) {
      final Presentation presentation = myPresentationFactory.getPresentation(action);

      // Mouse modifiers are 0 because they have no any sense when action is invoked via keyboard
      final AnActionEvent actionEvent =
          processor.createEvent(
              e,
              myContext.getDataContext(),
              ActionPlaces.MAIN_MENU,
              presentation,
              ActionManager.getInstance());

      ActionUtil.performDumbAwareUpdate(action, actionEvent, true);

      if (dumb && !action.isDumbAware()) {
        if (Boolean.FALSE.equals(
            presentation.getClientProperty(ActionUtil.WOULD_BE_ENABLED_IF_NOT_DUMB_MODE))) {
          continue;
        }

        nonDumbAwareAction.add(actionEvent);
        continue;
      }

      if (!presentation.isEnabled()) {
        continue;
      }

      processor.onUpdatePassed(e, action, actionEvent);

      ((DataManagerImpl.MyDataContext) myContext.getDataContext())
          .setEventCount(IdeEventQueue.getInstance().getEventCount(), this);
      actionManager.fireBeforeActionPerformed(action, actionEvent.getDataContext(), actionEvent);
      Component component =
          PlatformDataKeys.CONTEXT_COMPONENT.getData(actionEvent.getDataContext());
      if (component != null && !component.isShowing()) {
        return true;
      }

      processor.performAction(e, action, actionEvent);
      actionManager.fireAfterActionPerformed(action, actionEvent.getDataContext(), actionEvent);
      return true;
    }

    if (!nonDumbAwareAction.isEmpty()) {
      showDumbModeWarningLaterIfNobodyConsumesEvent(
          e, nonDumbAwareAction.toArray(new AnActionEvent[nonDumbAwareAction.size()]));
    }

    return false;
  }
 private boolean inWaitForSecondStrokeState() {
   // a key pressed means that the user starts to enter the second stroke...
   if (KeyEvent.KEY_PRESSED == myContext.getInputEvent().getID()) {
     setState(KeyState.STATE_SECOND_STROKE_IN_PROGRESS);
     return inSecondStrokeInProgressState();
   }
   // looks like RELEASEs (from the first stroke) go here...  skip them
   return true;
 }
예제 #3
0
 @Override
 public void run() {
   if (myState == KeyState.STATE_WAIT_FOR_SECOND_KEYSTROKE) {
     resetState();
     final DataContext dataContext = myContext.getDataContext();
     StatusBar.Info.set(
         null, dataContext == null ? null : CommonDataKeys.PROJECT.getData(dataContext));
   }
 }
 public void run() {
   if (myState == KeyState.STATE_WAIT_FOR_SECOND_KEYSTROKE) {
     resetState();
     if (myContext != null) {
       final DataContext dataContext = myContext.getDataContext();
       StatusBar.Info.set(
           null, dataContext == null ? null : PlatformDataKeys.PROJECT.getData(dataContext));
     }
   }
 }
  /** @return true if action is added and has second stroke */
  private boolean addAction(AnAction action, Shortcut sc) {
    boolean hasSecondStroke = false;

    Shortcut[] shortcuts = action.getShortcutSet().getShortcuts();
    for (Shortcut each : shortcuts) {
      if (!each.isKeyboard()) continue;

      if (each.startsWith(sc)) {
        if (!myContext.getActions().contains(action)) {
          myContext.getActions().add(action);
        }

        if (each instanceof KeyboardShortcut) {
          hasSecondStroke |= ((KeyboardShortcut) each).getSecondKeyStroke() != null;
        }
      }
    }

    return hasSecondStroke;
  }
 private ArrayList<Pair<AnAction, KeyStroke>> getSecondKeystrokeActions() {
   ArrayList<Pair<AnAction, KeyStroke>> secondKeyStrokes =
       new ArrayList<Pair<AnAction, KeyStroke>>();
   for (AnAction action : myContext.getActions()) {
     Shortcut[] shortcuts = action.getShortcutSet().getShortcuts();
     for (Shortcut shortcut : shortcuts) {
       if (shortcut instanceof KeyboardShortcut) {
         KeyboardShortcut keyShortcut = (KeyboardShortcut) shortcut;
         if (keyShortcut.getFirstKeyStroke().equals(myFirstKeyStroke)) {
           secondKeyStrokes.add(
               new Pair<AnAction, KeyStroke>(action, keyShortcut.getSecondKeyStroke()));
         }
       }
     }
   }
   return secondKeyStrokes;
 }
  private boolean inProcessedState() {
    KeyEvent e = myContext.getInputEvent();

    // ignore typed events which come after processed pressed event
    if (KeyEvent.KEY_TYPED == e.getID() && isPressedWasProcessed()) {
      return true;
    }
    if (KeyEvent.KEY_RELEASED == e.getID()
        && KeyEvent.VK_ALT == e.getKeyCode()
        && isPressedWasProcessed()) {
      // see IDEADEV-8615
      return true;
    }
    setState(KeyState.STATE_INIT);
    setPressedWasProcessed(false);
    return inInitState();
  }
  private boolean inSecondStrokeInProgressState() {
    KeyEvent e = myContext.getInputEvent();

    // when any key is released, we stop waiting for the second stroke
    if (KeyEvent.KEY_RELEASED == e.getID()) {
      myFirstKeyStroke = null;
      setState(KeyState.STATE_INIT);
      Project project = PlatformDataKeys.PROJECT.getData(myContext.getDataContext());
      StatusBar.Info.set(null, project);
      return false;
    }

    KeyStroke originalKeyStroke = KeyStroke.getKeyStrokeForEvent(e);
    KeyStroke keyStroke = getKeyStrokeWithoutMouseModifiers(originalKeyStroke);

    updateCurrentContext(
        myContext.getFoundComponent(),
        new KeyboardShortcut(myFirstKeyStroke, keyStroke),
        myContext.isModalContext());

    // consume the wrong second stroke and keep on waiting
    if (myContext.getActions().isEmpty()) {
      return true;
    }

    // finally user had managed to enter the second keystroke, so let it be processed
    Project project = PlatformDataKeys.PROJECT.getData(myContext.getDataContext());
    StatusBarEx statusBar = (StatusBarEx) WindowManager.getInstance().getStatusBar(project);
    if (processAction(e, myActionProcessor)) {
      if (statusBar != null) {
        statusBar.setInfo(null);
      }
      return true;
    } else {
      return false;
    }
  }
  /**
   * This method fills <code>myActions</code> list.
   *
   * @return true if there is a shortcut with second stroke found.
   */
  public KeyProcessorContext updateCurrentContext(
      Component component, Shortcut sc, boolean isModalContext) {
    myContext.setFoundComponent(null);
    myContext.getActions().clear();

    if (isControlEnterOnDialog(component, sc)) return myContext;

    boolean hasSecondStroke = false;

    // here we try to find "local" shortcuts

    for (; component != null; component = component.getParent()) {
      if (!(component instanceof JComponent)) {
        continue;
      }
      ArrayList listOfActions =
          (ArrayList) ((JComponent) component).getClientProperty(AnAction.ourClientProperty);
      if (listOfActions == null) {
        continue;
      }
      for (Object listOfAction : listOfActions) {
        if (!(listOfAction instanceof AnAction)) {
          continue;
        }
        AnAction action = (AnAction) listOfAction;
        hasSecondStroke |= addAction(action, sc);
      }
      // once we've found a proper local shortcut(s), we continue with non-local shortcuts
      if (!myContext.getActions().isEmpty()) {
        myContext.setFoundComponent((JComponent) component);
        break;
      }
    }

    // search in main keymap

    Keymap keymap = KeymapManager.getInstance().getActiveKeymap();
    String[] actionIds = keymap.getActionIds(sc);

    ActionManager actionManager = ActionManager.getInstance();
    for (String actionId : actionIds) {
      AnAction action = actionManager.getAction(actionId);
      if (action != null) {
        if (isModalContext && !action.isEnabledInModalContext()) {
          continue;
        }
        hasSecondStroke |= addAction(action, sc);
      }
    }

    if (!hasSecondStroke && sc instanceof KeyboardShortcut) {
      // little trick to invoke action which second stroke is a key w/o modifiers, but user still
      // holds the modifier key(s) of the first stroke

      final KeyboardShortcut keyboardShortcut = (KeyboardShortcut) sc;
      final KeyStroke firstKeyStroke = keyboardShortcut.getFirstKeyStroke();
      final KeyStroke secondKeyStroke = keyboardShortcut.getSecondKeyStroke();

      if (secondKeyStroke != null
          && secondKeyStroke.getModifiers() != 0
          && firstKeyStroke.getModifiers() != 0) {
        final KeyboardShortcut altShortCut =
            new KeyboardShortcut(
                firstKeyStroke, KeyStroke.getKeyStroke(secondKeyStroke.getKeyCode(), 0));
        final String[] additionalActions = keymap.getActionIds(altShortCut);

        for (final String actionId : additionalActions) {
          AnAction action = actionManager.getAction(actionId);
          if (action != null) {
            if (isModalContext && !action.isEnabledInModalContext()) {
              continue;
            }
            hasSecondStroke |= addAction(action, altShortCut);
          }
        }
      }
    }

    myContext.setHasSecondStroke(hasSecondStroke);

    Comparator<? super AnAction> comparator =
        PlatformDataKeys.ACTIONS_SORTER.getData(myContext.getDataContext());
    if (comparator != null) {
      Collections.sort(myContext.getActions(), comparator);
    }

    return myContext;
  }
  private boolean inInitState() {
    Component focusOwner = myContext.getFocusOwner();
    boolean isModalContext = myContext.isModalContext();
    DataContext dataContext = myContext.getDataContext();
    KeyEvent e = myContext.getInputEvent();

    // http://www.jetbrains.net/jira/browse/IDEADEV-12372
    if (myLeftCtrlPressed
        && myRightAltPressed
        && focusOwner != null
        && e.getModifiers() == (InputEvent.CTRL_MASK | InputEvent.ALT_MASK)) {
      if (Registry.is("actionSystem.force.alt.gr")) {
        return false;
      }
      final InputContext inputContext = focusOwner.getInputContext();
      if (inputContext != null) {
        Locale locale = inputContext.getLocale();
        if (locale != null) {
          @NonNls final String language = locale.getLanguage();
          if (ALT_GR_LAYOUTS.contains(language)) {
            // don't search for shortcuts
            return false;
          }
        }
      }
    }

    KeyStroke originalKeyStroke = KeyStroke.getKeyStrokeForEvent(e);
    KeyStroke keyStroke = getKeyStrokeWithoutMouseModifiers(originalKeyStroke);

    if (myKeyGestureProcessor.processInitState()) {
      return true;
    }

    if (SystemInfo.isMac) {
      boolean keyTyped = e.getID() == KeyEvent.KEY_TYPED;
      boolean hasMnemonicsInWindow =
          e.getID() == KeyEvent.KEY_PRESSED && hasMnemonicInWindow(focusOwner, e.getKeyCode())
              || keyTyped && hasMnemonicInWindow(focusOwner, e.getKeyChar());
      boolean imEnabled = IdeEventQueue.getInstance().isInputMethodEnabled();

      if (e.getModifiersEx() == InputEvent.ALT_DOWN_MASK
          && (hasMnemonicsInWindow || (!imEnabled && keyTyped))) {
        setPressedWasProcessed(true);
        setState(KeyState.STATE_PROCESSED);
        return false;
      }
    }

    updateCurrentContext(focusOwner, new KeyboardShortcut(keyStroke, null), isModalContext);
    if (myContext.getActions().isEmpty()) {
      // there's nothing mapped for this stroke
      return false;
    }

    if (myContext.isHasSecondStroke()) {
      myFirstKeyStroke = keyStroke;
      final ArrayList<Pair<AnAction, KeyStroke>> secondKeyStrokes = getSecondKeystrokeActions();

      final Project project = PlatformDataKeys.PROJECT.getData(dataContext);
      StringBuilder message = new StringBuilder();
      message.append(KeyMapBundle.message("prefix.key.pressed.message"));
      message.append(' ');
      for (int i = 0; i < secondKeyStrokes.size(); i++) {
        Pair<AnAction, KeyStroke> pair = secondKeyStrokes.get(i);
        if (i > 0) message.append(", ");
        message.append(pair.getFirst().getTemplatePresentation().getText());
        message.append(" (");
        message.append(KeymapUtil.getKeystrokeText(pair.getSecond()));
        message.append(")");
      }

      StatusBar.Info.set(message.toString(), project);

      mySecondStrokeTimeout.cancelAllRequests();
      mySecondStrokeTimeout.addRequest(
          mySecondStrokeTimeoutRunnable, Registry.intValue("actionSystem.secondKeystrokeTimeout"));

      if (Registry.is("actionSystem.secondKeystrokeAutoPopupEnabled")) {
        mySecondKeystrokePopupTimeout.cancelAllRequests();
        if (secondKeyStrokes.size() > 1) {
          final DataContext oldContext = myContext.getDataContext();
          mySecondKeystrokePopupTimeout.addRequest(
              new Runnable() {
                @Override
                public void run() {
                  if (myState == KeyState.STATE_WAIT_FOR_SECOND_KEYSTROKE) {
                    StatusBar.Info.set(null, PlatformDataKeys.PROJECT.getData(oldContext));
                    new SecondaryKeystrokePopup(myFirstKeyStroke, secondKeyStrokes, oldContext)
                        .showInBestPositionFor(oldContext);
                  }
                }
              },
              Registry.intValue("actionSystem.secondKeystrokePopupTimeout"));
        }
      }

      setState(KeyState.STATE_WAIT_FOR_SECOND_KEYSTROKE);
      return true;
    } else {
      return processAction(e, myActionProcessor);
    }
  }
  /**
   * @return <code>true</code> if and only if the passed event is already dispatched by the <code>
   *     IdeKeyEventDispatcher</code> and there is no need for any other processing of the event.
   */
  public boolean dispatchKeyEvent(final KeyEvent e) {
    if (myDisposed) return false;

    if (e.isConsumed()) {
      return false;
    }

    // http://www.jetbrains.net/jira/browse/IDEADEV-12372
    if (e.getKeyCode() == KeyEvent.VK_CONTROL) {
      if (e.getID() == KeyEvent.KEY_PRESSED) {
        myLeftCtrlPressed = e.getKeyLocation() == KeyEvent.KEY_LOCATION_LEFT;
      } else if (e.getID() == KeyEvent.KEY_RELEASED) {
        myLeftCtrlPressed = false;
      }
    } else if (e.getKeyCode() == KeyEvent.VK_ALT) {
      if (e.getID() == KeyEvent.KEY_PRESSED) {
        myRightAltPressed = e.getKeyLocation() == KeyEvent.KEY_LOCATION_RIGHT;
      } else if (e.getID() == KeyEvent.KEY_RELEASED) {
        myRightAltPressed = false;
      }
    }

    KeyboardFocusManager focusManager = KeyboardFocusManager.getCurrentKeyboardFocusManager();
    Component focusOwner = focusManager.getFocusOwner();

    // shortcuts should not work in shortcut setup fields
    if (focusOwner instanceof ShortcutTextField) {
      return false;
    }
    if (focusOwner instanceof JTextComponent && ((JTextComponent) focusOwner).isEditable()) {
      if (e.getKeyChar() != KeyEvent.CHAR_UNDEFINED && e.getKeyChar() != KeyEvent.VK_ESCAPE) {
        MacUIUtil.hideCursor();
      }
    }

    MenuSelectionManager menuSelectionManager = MenuSelectionManager.defaultManager();
    MenuElement[] selectedPath = menuSelectionManager.getSelectedPath();
    if (selectedPath.length > 0) {
      if (!(selectedPath[0] instanceof ComboPopup)) {
        // The following couple of lines of code is a PATCH!!!
        // It is needed to ignore ENTER KEY_TYPED events which sometimes can reach editor when an
        // action
        // is invoked from main menu via Enter key.
        setState(KeyState.STATE_PROCESSED);
        setPressedWasProcessed(true);
        return false;
      }
    }

    // Keymap shortcuts (i.e. not local shortcuts) should work only in:
    // - main frame
    // - floating focusedWindow
    // - when there's an editor in contexts
    Window focusedWindow = focusManager.getFocusedWindow();
    boolean isModalContext = focusedWindow != null && isModalContext(focusedWindow);

    final DataManager dataManager = DataManager.getInstance();
    if (dataManager == null) return false;

    DataContext dataContext = dataManager.getDataContext();

    myContext.setDataContext(dataContext);
    myContext.setFocusOwner(focusOwner);
    myContext.setModalContext(isModalContext);
    myContext.setInputEvent(e);

    try {
      if (getState() == KeyState.STATE_INIT) {
        return inInitState();
      } else if (getState() == KeyState.STATE_PROCESSED) {
        return inProcessedState();
      } else if (getState() == KeyState.STATE_WAIT_FOR_SECOND_KEYSTROKE) {
        return inWaitForSecondStrokeState();
      } else if (getState() == KeyState.STATE_SECOND_STROKE_IN_PROGRESS) {
        return inSecondStrokeInProgressState();
      } else if (getState() == KeyState.STATE_KEY_GESTURE_PROCESSOR) {
        return myKeyGestureProcessor.process();
      } else {
        throw new IllegalStateException("state = " + getState());
      }
    } finally {
      myContext.clear();
    }
  }