diff --git a/platform/lang-impl/src/com/intellij/ui/debugger/extensions/PlaybackDebugger.java b/platform/lang-impl/src/com/intellij/ui/debugger/extensions/PlaybackDebugger.java index 3db7c1b32ac9..d65542991113 100644 --- a/platform/lang-impl/src/com/intellij/ui/debugger/extensions/PlaybackDebugger.java +++ b/platform/lang-impl/src/com/intellij/ui/debugger/extensions/PlaybackDebugger.java @@ -439,7 +439,7 @@ public class PlaybackDebugger implements UiDebuggerExtension, PlaybackRunner.Sta myMessage.clear(); addInfo("Waiting for IDE frame activation", -1); - myRunner = new PlaybackRunner(myDocument.getText(), this); + myRunner = new PlaybackRunner(myDocument.getText(), this, false); new Thread() { diff --git a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java index 50d1e14f9a68..5c32ff9a9bff 100644 --- a/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java +++ b/platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java @@ -29,6 +29,7 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher; import com.intellij.openapi.keymap.impl.IdeMouseEventDispatcher; import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.SystemInfo; @@ -48,10 +49,7 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; /** @@ -83,7 +81,7 @@ public class IdeEventQueue extends EventQueue { private final Map myListener2Request = new HashMap(); // IdleListener -> MyFireIdleRequest - private final IdeKeyEventDispatcher myKeyEventDispatcher = new IdeKeyEventDispatcher(); + private final IdeKeyEventDispatcher myKeyEventDispatcher = new IdeKeyEventDispatcher(this); private final IdeMouseEventDispatcher myMouseEventDispatcher = new IdeMouseEventDispatcher(); @@ -130,6 +128,9 @@ public class IdeEventQueue extends EventQueue { private final Set myDispatchers = new LinkedHashSet(); private final Set myPostprocessors = new LinkedHashSet(); + private Set myReady = new HashSet(); + private boolean myKeyboardBusy; + private static class IdeEventQueueHolder { private static final IdeEventQueue INSTANCE = new IdeEventQueue(); } @@ -379,6 +380,10 @@ public class IdeEventQueue extends EventQueue { each.dispatch(e); } + if (e instanceof KeyEvent) { + maybeReady(); + } + if (DEBUG) { final long processTime = System.currentTimeMillis() - t; if (processTime > 100) { @@ -427,6 +432,10 @@ public class IdeEventQueue extends EventQueue { enterSuspendModeIfNeeded(e); } + if (e instanceof KeyEvent) { + myKeyboardBusy = e.getID() != KeyEvent.KEY_RELEASED || ((KeyEvent)e).getModifiers() != 0; + } + if (typeAheadDispatchToFocusManager(e)) return; if (e instanceof WindowEvent) { @@ -735,4 +744,37 @@ public class IdeEventQueue extends EventQueue { public boolean hasFocusEventsPending() { return peekEvent(FocusEvent.FOCUS_GAINED) != null || peekEvent(FocusEvent.FOCUS_LOST) != null; } + + private boolean isReady() { + return !myKeyboardBusy && myKeyEventDispatcher.isReady(); + } + + public void maybeReady() { + flushReady(); + } + + private void flushReady() { + if (myReady.size() == 0 || !isReady()) return; + + Runnable[] ready = myReady.toArray(new Runnable[myReady.size()]); + myReady.clear(); + + for (Runnable each : ready) { + each.run(); + } + } + + public void doWhenReady(final Runnable runnable) { + if (EventQueue.isDispatchThread()) { + myReady.add(runnable); + maybeReady(); + } else { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + myReady.add(runnable); + maybeReady(); + } + }); + } + } } diff --git a/platform/platform-impl/src/com/intellij/ide/actionMacro/ActionMacroManager.java b/platform/platform-impl/src/com/intellij/ide/actionMacro/ActionMacroManager.java index 04f7311e7feb..b32bea00fd66 100644 --- a/platform/platform-impl/src/com/intellij/ide/actionMacro/ActionMacroManager.java +++ b/platform/platform-impl/src/com/intellij/ide/actionMacro/ActionMacroManager.java @@ -30,6 +30,7 @@ import com.intellij.openapi.ui.playback.PlaybackRunner; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.NamedJDOMExternalizable; import com.intellij.openapi.util.WriteExternalException; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.wm.IdeFrame; import com.intellij.openapi.wm.WindowManager; import org.jdom.Element; @@ -212,7 +213,7 @@ public class ActionMacroManager implements ExportableApplicationComponent, Named public void messageEdt(String text, int curentLine) { frame.getStatusBar().setInfo("Line " + curentLine + ": " + text); } - }); + }, Registry.is("actionSystem.playback.useDirectActionCall")); myIsPlaying = true; diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java index 24ba340120a0..fd1910851573 100644 --- a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java +++ b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java @@ -93,8 +93,10 @@ public final class IdeKeyEventDispatcher implements Disposable { private final KeyboardGestureProcessor myKeyGestureProcessor = new KeyboardGestureProcessor(this); private final KeyProcessorContext myContext = new KeyProcessorContext(); + private IdeEventQueue myQueue; - public IdeKeyEventDispatcher(){ + public IdeKeyEventDispatcher(IdeEventQueue queue){ + myQueue = queue; Application parent = ApplicationManager.getApplication(); // Application is null on early start when e.g. license dialog is shown if (parent != null) Disposer.register(parent, this); } @@ -348,7 +350,9 @@ public final class IdeKeyEventDispatcher implements Disposable { KeyStroke originalKeyStroke=KeyStroke.getKeyStrokeForEvent(e); KeyStroke keyStroke=getKeyStrokeWithoutMouseModifiers(originalKeyStroke); - if (myKeyGestureProcessor.processInitState()) return true; + if (myKeyGestureProcessor.processInitState()) { + return true; + } if (SystemInfo.isMac) { if (e.getModifiersEx() == InputEvent.ALT_DOWN_MASK && @@ -620,6 +624,9 @@ public final class IdeKeyEventDispatcher implements Disposable { public void setState(final int state) { myState = state; + if (myQueue != null) { + myQueue.maybeReady(); + } } public void resetState() { @@ -634,4 +641,8 @@ public final class IdeKeyEventDispatcher implements Disposable { public void setPressedWasProcessed(boolean pressedWasProcessed) { myPressedWasProcessed = pressedWasProcessed; } + + public boolean isReady() { + return myState == STATE_INIT; + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/playback/PlaybackCommand.java b/platform/platform-impl/src/com/intellij/openapi/ui/playback/PlaybackCommand.java index 13497ee91502..76b3f4b82711 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/playback/PlaybackCommand.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/playback/PlaybackCommand.java @@ -20,6 +20,6 @@ import com.intellij.openapi.util.ActionCallback; import java.awt.*; public interface PlaybackCommand { - ActionCallback execute(PlaybackRunner.StatusCallback cb, Robot robot); + ActionCallback execute(PlaybackRunner.StatusCallback cb, Robot robot, boolean useDirectActionCall); boolean canGoFurther(); } \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/playback/PlaybackRunner.java b/platform/platform-impl/src/com/intellij/openapi/ui/playback/PlaybackRunner.java index 7ccee038d6d3..0027308deb4e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/playback/PlaybackRunner.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/playback/PlaybackRunner.java @@ -15,6 +15,7 @@ */ package com.intellij.openapi.ui.playback; +import com.intellij.ide.IdeEventQueue; import com.intellij.openapi.ui.playback.commands.AssertFocused; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.ui.playback.commands.*; @@ -39,10 +40,12 @@ public class PlaybackRunner { private ArrayList myCommands = new ArrayList(); private ActionCallback myActionCallback; private boolean myStopRequested; + private boolean myUseDirectActionCall; - public PlaybackRunner(String script, StatusCallback callback) { + public PlaybackRunner(String script, StatusCallback callback, final boolean useDirectActionCall) { myScript = script; myCallback = callback; + myUseDirectActionCall = useDirectActionCall; } public ActionCallback run() { @@ -58,7 +61,15 @@ public class PlaybackRunner { new Thread() { @Override public void run() { - executeFrom(0); + if (myUseDirectActionCall) { + executeFrom(0); + } else { + IdeEventQueue.getInstance().doWhenReady(new Runnable() { + public void run() { + executeFrom(0); + } + }); + } } }.start(); @@ -78,7 +89,7 @@ public class PlaybackRunner { myActionCallback.setRejected(); return; } - cmd.execute(myCallback, myRobot).doWhenDone(new Runnable() { + cmd.execute(myCallback, myRobot, myUseDirectActionCall).doWhenDone(new Runnable() { public void run() { if (cmd.canGoFurther()) { executeFrom(cmdIndex + 1); @@ -226,7 +237,7 @@ public class PlaybackRunner { public void message(String text, int currentLine) { System.out.println(currentLine + " " + text); } - }).run(); + }, false).run(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/AbstractCommand.java b/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/AbstractCommand.java index 30811b7fab8d..628d822d1a01 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/AbstractCommand.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/AbstractCommand.java @@ -45,10 +45,10 @@ public abstract class AbstractCommand implements PlaybackCommand { return true; } - public final ActionCallback execute(PlaybackRunner.StatusCallback cb, Robot robot) { + public final ActionCallback execute(PlaybackRunner.StatusCallback cb, Robot robot, boolean useDirectActionCall) { try { dumpCommand(cb); - return _execute(cb, robot); + return _execute(cb, robot, useDirectActionCall); } catch (Exception e) { cb.error(e.getMessage(), getLine()); @@ -56,7 +56,7 @@ public abstract class AbstractCommand implements PlaybackCommand { } } - protected abstract ActionCallback _execute(PlaybackRunner.StatusCallback cb, Robot robot); + protected abstract ActionCallback _execute(PlaybackRunner.StatusCallback cb, Robot robot, boolean directActionCall); public void dumpCommand(final PlaybackRunner.StatusCallback cb) { cb.message(getText(), getLine()); diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/ActionCommand.java b/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/ActionCommand.java index aea325d0813d..a30b23929901 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/ActionCommand.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/ActionCommand.java @@ -38,7 +38,7 @@ public class ActionCommand extends TypeCommand { super(text, line); } - protected ActionCallback _execute(PlaybackRunner.StatusCallback cb, Robot robot) { + protected ActionCallback _execute(PlaybackRunner.StatusCallback cb, Robot robot, boolean directActionCall) { final String actionName = getText().substring(PREFIX.length()).trim(); final AnAction action = ActionManager.getInstance().getAction(actionName); @@ -48,23 +48,26 @@ public class ActionCommand extends TypeCommand { } - 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); + if (!directActionCall) { + 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) { - cb.message("Invoking action via shortcut: " + stroke.toString(), getLine()); - type(robot, stroke); - return new ActionCallback.Done(); + if (stroke != null) { + cb.message("Invoking action via shortcut: " + stroke.toString(), getLine()); + type(robot, stroke); + return new ActionCallback.Done(); + } } final InputEvent input = getInputEvent(actionName); diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/AlphaNumericTypeCommand.java b/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/AlphaNumericTypeCommand.java index f54fdbad245b..c0a034f9a1c2 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/AlphaNumericTypeCommand.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/AlphaNumericTypeCommand.java @@ -27,7 +27,7 @@ public class AlphaNumericTypeCommand extends TypeCommand { super(text, line); } - public ActionCallback _execute(PlaybackRunner.StatusCallback cb, Robot robot) { + public ActionCallback _execute(PlaybackRunner.StatusCallback cb, Robot robot, boolean directActionCall) { final String text = getText(); for (int i = 0; i < text.length(); i++) { final char each = text.charAt(i); diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/AssertFocused.java b/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/AssertFocused.java index 37224d5c8ff3..f146d4a4dc77 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/AssertFocused.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/AssertFocused.java @@ -31,7 +31,7 @@ public class AssertFocused extends AbstractCommand { super(text, line); } - protected ActionCallback _execute(final PlaybackRunner.StatusCallback cb, Robot robot) { + protected ActionCallback _execute(final PlaybackRunner.StatusCallback cb, Robot robot, boolean directActionCall) { final ActionCallback result = new ActionCallback(); String text = getText().substring(PREFIX.length()).trim(); diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/DelayCommand.java b/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/DelayCommand.java index 88263634bbb6..101fb358d73c 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/DelayCommand.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/DelayCommand.java @@ -27,7 +27,7 @@ public class DelayCommand extends AbstractCommand { super(text, line); } - public ActionCallback _execute(PlaybackRunner.StatusCallback cb, Robot robot) { + public ActionCallback _execute(PlaybackRunner.StatusCallback cb, Robot robot, boolean directActionCall) { final String s = getText().substring(PREFIX.length()).trim(); try { diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/EmptyCommand.java b/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/EmptyCommand.java index a43e6b18bd35..870bfea344bb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/EmptyCommand.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/EmptyCommand.java @@ -25,7 +25,7 @@ public class EmptyCommand extends AbstractCommand { super("", line); } - public ActionCallback _execute(PlaybackRunner.StatusCallback cb, Robot robot) { + public ActionCallback _execute(PlaybackRunner.StatusCallback cb, Robot robot, boolean directActionCall) { return new ActionCallback.Done(); } } \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/ErrorCommand.java b/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/ErrorCommand.java index 13f4ee9329f4..41e04f88ba69 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/ErrorCommand.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/ErrorCommand.java @@ -26,7 +26,7 @@ public class ErrorCommand extends AbstractCommand { super(text, line); } - public ActionCallback _execute(PlaybackRunner.StatusCallback cb, Robot robot) { + public ActionCallback _execute(PlaybackRunner.StatusCallback cb, Robot robot, boolean directActionCall) { dumpError(cb, getText()); return new ActionCallback.Rejected(); } diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/KeyShortcutCommand.java b/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/KeyShortcutCommand.java index 489e06991f73..cfca38ba76b5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/KeyShortcutCommand.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/KeyShortcutCommand.java @@ -29,7 +29,7 @@ public class KeyShortcutCommand extends TypeCommand { super(text, line); } - public ActionCallback _execute(PlaybackRunner.StatusCallback cb, Robot robot) { + public ActionCallback _execute(PlaybackRunner.StatusCallback cb, Robot robot, boolean directActionCall) { final String one = getText().substring(PREFIX.length()); if (!one.endsWith("]")) { dumpError(cb, "Expected " + "]"); diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/StopCommand.java b/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/StopCommand.java index f35e11de6e25..467d3a633fc7 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/StopCommand.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/playback/commands/StopCommand.java @@ -28,7 +28,7 @@ public class StopCommand extends AbstractCommand { super(text, line); } - protected ActionCallback _execute(PlaybackRunner.StatusCallback cb, Robot robot) { + protected ActionCallback _execute(PlaybackRunner.StatusCallback cb, Robot robot, boolean directActionCall) { cb.message("Stopped", getLine()); return new ActionCallback.Done(); } diff --git a/platform/platform-resources-en/src/misc/registry.properties b/platform/platform-resources-en/src/misc/registry.properties index 4696e71bf4e2..f32831ce2ae7 100644 --- a/platform/platform-resources-en/src/misc/registry.properties +++ b/platform/platform-resources-en/src/misc/registry.properties @@ -15,7 +15,8 @@ actionSystem.commandProcessingTimeout.description=Timeout after which currently actionSystem.typeAheadTimeBeforeDialog=2000 actionSystem.typeAheadTimeBeforeDialog.description=If dialog shown as a result of some typing, all key types withing this time will be redispatched after dialog is closed actionSystem.playback.autodelay=20 -actionSystem.playback.description=Delay between events generated by the playback runner +actionSystem.playback.autodelay.description=Delay between events generated by the playback runner +actionSystem.playback.useDirectActionCall=true actionSystem.focusIdleTimeout=20 actionSystem.mac.screenMenuNotUpdatedFix=false actionSystem.keyGestures.enabled=false