@Override
 public void propertyChange(PropertyChangeEvent event) {
   boolean internal = myInternal;
   myInternal = true;
   Object value = event.getNewValue();
   if (ShortcutFilteringPanel.this == event.getSource()) {
     if (value instanceof KeyboardShortcut) {
       KeyboardShortcut shortcut = (KeyboardShortcut) value;
       myMousePanel.setShortcut(null);
       myKeyboardPanel.setShortcut(shortcut);
       if (null != shortcut.getSecondKeyStroke()) {
         myKeyboardPanel.mySecondStrokeEnable.setSelected(true);
       }
     } else {
       MouseShortcut shortcut =
           value instanceof MouseShortcut ? (MouseShortcut) value : null;
       String text = shortcut == null ? null : KeymapUtil.getMouseShortcutText(shortcut);
       myMousePanel.setShortcut(shortcut);
       myKeyboardPanel.setShortcut(null);
       myKeyboardPanel.myFirstStroke.setText(text);
       myKeyboardPanel.mySecondStroke.setText(null);
       myKeyboardPanel.mySecondStroke.setEnabled(false);
     }
   } else if (value instanceof Shortcut) {
     setShortcut((Shortcut) value);
   } else if (!internal) {
     setShortcut(null);
   }
   myInternal = internal;
 }
 @Nullable
 public static KeyStroke getKeyStroke(@NotNull final ShortcutSet shortcutSet) {
   final Shortcut[] shortcuts = shortcutSet.getShortcuts();
   if (shortcuts.length == 0 || !(shortcuts[0] instanceof KeyboardShortcut)) return null;
   final KeyboardShortcut shortcut = (KeyboardShortcut) shortcuts[0];
   if (shortcut.getSecondKeyStroke() != null) {
     return null;
   }
   return shortcut.getFirstKeyStroke();
 }
 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 int getMnemonicCharIndex(String text) {
    final int mnemonicIndex = myPresentation.getDisplayedMnemonicIndex();
    if (mnemonicIndex != -1) {
      return mnemonicIndex;
    }
    final ShortcutSet shortcutSet = myAction.getShortcutSet();
    final Shortcut[] shortcuts = shortcutSet.getShortcuts();
    for (Shortcut shortcut : shortcuts) {
      if (!(shortcut instanceof KeyboardShortcut)) continue;

      KeyboardShortcut keyboardShortcut = (KeyboardShortcut) shortcut;
      if (keyboardShortcut.getSecondKeyStroke()
          == null) { // we are interested only in "mnemonic-like" shortcuts
        final KeyStroke keyStroke = keyboardShortcut.getFirstKeyStroke();
        final int modifiers = keyStroke.getModifiers();
        if (BitUtil.isSet(modifiers, InputEvent.ALT_MASK)) {
          return (keyStroke.getKeyChar() != KeyEvent.CHAR_UNDEFINED)
              ? text.indexOf(keyStroke.getKeyChar())
              : text.indexOf(KeyEvent.getKeyText(keyStroke.getKeyCode()));
        }
      }
    }
    return -1;
  }
  public static String getShortcutText(@NotNull Shortcut shortcut) {
    String s = "";

    if (shortcut instanceof KeyboardShortcut) {
      KeyboardShortcut keyboardShortcut = (KeyboardShortcut) shortcut;

      String acceleratorText = getKeystrokeText(keyboardShortcut.getFirstKeyStroke());
      if (!acceleratorText.isEmpty()) {
        s = acceleratorText;
      }

      acceleratorText = getKeystrokeText(keyboardShortcut.getSecondKeyStroke());
      if (!acceleratorText.isEmpty()) {
        s += ", " + acceleratorText;
      }
    } else if (shortcut instanceof MouseShortcut) {
      MouseShortcut mouseShortcut = (MouseShortcut) shortcut;
      s =
          getMouseShortcutText(
              mouseShortcut.getButton(),
              mouseShortcut.getModifiers(),
              mouseShortcut.getClickCount());
    } else if (shortcut instanceof KeyboardModifierGestureShortcut) {
      final KeyboardModifierGestureShortcut gestureShortcut =
          (KeyboardModifierGestureShortcut) shortcut;
      s =
          gestureShortcut.getType() == KeyboardGestureAction.ModifierType.dblClick
              ? "Press, release and hold "
              : "Hold ";
      s += getKeystrokeText(gestureShortcut.getStroke());
    } else {
      throw new IllegalArgumentException(
          "unknown shortcut class: " + shortcut.getClass().getCanonicalName());
    }
    return s;
  }
  protected ActionCallback _execute(final PlaybackContext context) {
    final String actionName = getText().substring(PREFIX.length()).trim();

    final ActionManager am = ActionManager.getInstance();
    final AnAction targetAction = am.getAction(actionName);
    if (targetAction == null) {
      dumpError(context, "Unknown action: " + actionName);
      return new ActionCallback.Rejected();
    }

    if (!context.isUseDirectActionCall()) {
      final Shortcut[] sc = KeymapManager.getInstance().getActiveKeymap().getShortcuts(actionName);
      KeyStroke stroke = null;
      for (Shortcut each : sc) {
        if (each instanceof KeyboardShortcut) {
          final KeyboardShortcut ks = (KeyboardShortcut) each;
          final KeyStroke first = ks.getFirstKeyStroke();
          final KeyStroke second = ks.getSecondKeyStroke();
          if (first != null && second == null) {
            stroke = KeyStroke.getKeyStroke(first.getKeyCode(), first.getModifiers(), false);
            break;
          }
        }
      }

      if (stroke != null) {
        final ActionCallback result =
            new TimedOutCallback(
                Registry.intValue("actionSystem.commandProcessingTimeout"),
                "Timed out calling action id=" + actionName,
                new Throwable(),
                true) {
              @Override
              protected void dumpError() {
                context.error(getMessage(), getLine());
              }
            };
        context.message("Invoking action via shortcut: " + stroke.toString(), getLine());

        final KeyStroke finalStroke = stroke;

        IdeFocusManager.getGlobalInstance()
            .doWhenFocusSettlesDown(
                new Runnable() {
                  @Override
                  public void run() {
                    final Ref<AnActionListener> listener = new Ref<AnActionListener>();
                    listener.set(
                        new AnActionListener.Adapter() {

                          @Override
                          public void beforeActionPerformed(
                              final AnAction action, DataContext dataContext, AnActionEvent event) {
                            SwingUtilities.invokeLater(
                                new Runnable() {
                                  @Override
                                  public void run() {
                                    if (context.isDisposed()) {
                                      am.removeAnActionListener(listener.get());
                                      return;
                                    }

                                    if (targetAction.equals(action)) {
                                      context.message(
                                          "Performed action: " + actionName,
                                          context.getCurrentLine());
                                      am.removeAnActionListener(listener.get());
                                      result.setDone();
                                    }
                                  }
                                });
                          }
                        });
                    am.addAnActionListener(listener.get());

                    context.runPooledThread(
                        new Runnable() {
                          @Override
                          public void run() {
                            type(context.getRobot(), finalStroke);
                          }
                        });
                  }
                });

        return result;
      }
    }

    final InputEvent input = getInputEvent(actionName);

    final ActionCallback result = new ActionCallback();

    context.getRobot().delay(Registry.intValue("actionSystem.playback.delay"));
    SwingUtilities.invokeLater(
        new Runnable() {
          public void run() {
            am.tryToExecute(targetAction, input, null, null, false)
                .doWhenProcessed(result.createSetDoneRunnable());
          }
        });

    return result;
  }
  /**
   * 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;
  }