fixed double keystroke
authorKirill Kalishev <kirill.kalishev@jetbrains.com>
Thu, 4 Mar 2010 14:20:43 +0000 (17:20 +0300)
committerKirill Kalishev <kirill.kalishev@jetbrains.com>
Thu, 4 Mar 2010 14:20:43 +0000 (17:20 +0300)
platform/platform-impl/src/com/intellij/ide/IdeEventQueue.java
platform/platform-impl/src/com/intellij/openapi/keymap/impl/IdeKeyEventDispatcher.java
platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeyState.java [new file with mode: 0644]
platform/platform-impl/src/com/intellij/openapi/keymap/impl/keyGestures/KeyboardGestureProcessor.java
platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java
platform/platform-resources-en/src/misc/registry.properties

index 1f0b844e5dc11fd1cff7e7275c5dc4a919f9bc5d..18b431ca8c0dadc02b65b4f118d723234d61aa16 100644 (file)
@@ -742,7 +742,7 @@ public class IdeEventQueue extends EventQueue {
     return peekEvent(FocusEvent.FOCUS_GAINED) != null || peekEvent(FocusEvent.FOCUS_LOST) != null;
   }
 
-  private boolean isReady() {
+  public boolean isReady() {
     return !myKeyboardBusy && myKeyEventDispatcher.isReady();
   }
 
index a57ad910a96750272d292f47186d5ff32a2ef9e6..c4f0098fbd654994216e8f2bddc461d43f0c7810 100644 (file)
@@ -39,12 +39,15 @@ import com.intellij.openapi.ui.popup.JBPopup;
 import com.intellij.openapi.util.Disposer;
 import com.intellij.openapi.util.Pair;
 import com.intellij.openapi.util.SystemInfo;
+import com.intellij.openapi.util.registry.Registry;
+import com.intellij.openapi.wm.StatusBar;
 import com.intellij.openapi.wm.WindowManager;
 import com.intellij.openapi.wm.ex.StatusBarEx;
 import com.intellij.openapi.wm.impl.FloatingDecorator;
 import com.intellij.openapi.wm.impl.IdeFrameImpl;
 import com.intellij.openapi.wm.impl.IdeGlassPaneEx;
 import com.intellij.ui.popup.AbstractPopup;
+import com.intellij.util.Alarm;
 import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -69,12 +72,6 @@ public final class IdeKeyEventDispatcher implements Disposable {
   @NonNls
   private static final String GET_CACHED_STROKE_METHOD_NAME = "getCachedStroke";
 
-  public static final int STATE_INIT = 0;
-  private static final int STATE_WAIT_FOR_SECOND_KEYSTROKE = 1;
-  private static final int STATE_SECOND_STROKE_IN_PROGRESS = 2;
-  private static final int STATE_PROCESSED = 3;
-  public static final int STATE_KEY_GESTURE_PROCESSOR = 4;
-
   private KeyStroke myFirstKeyStroke;
   /**
    * When we "dispatch" key event via keymap, i.e. when registered action has been executed
@@ -82,7 +79,7 @@ public final class IdeKeyEventDispatcher implements Disposable {
    * KEY_TYPED event because they are not valid.
    */
   private boolean myPressedWasProcessed;
-  private int myState = STATE_INIT;
+  private KeyState myState = KeyState.STATE_INIT;
 
   private final PresentationFactory myPresentationFactory = new PresentationFactory();
   private boolean myDisposed = false;
@@ -94,6 +91,24 @@ public final class IdeKeyEventDispatcher implements Disposable {
   private final KeyProcessorContext myContext = new KeyProcessorContext();
   private IdeEventQueue myQueue;
 
+  private Alarm mySecondStrokeTimeout = new Alarm();
+  private Runnable mySecondStrokeTimeoutRunnable = new Runnable() {
+    public void run() {
+      if (myState == KeyState.STATE_WAIT_FOR_SECOND_KEYSTROKE) {
+        resetState();
+        if (myContext != null) {
+          Project project = PlatformDataKeys.PROJECT.getData(myContext.getDataContext());
+          if (project != null) {
+            StatusBar status = WindowManager.getInstance().getStatusBar(project);
+            if (status != null) {
+              status.setInfo(null);
+            }
+          }
+        }
+      }
+    }
+  };
+
   public IdeKeyEventDispatcher(IdeEventQueue queue){
     myQueue = queue;
     Application parent = ApplicationManager.getApplication();  // Application is null on early start when e.g. license dialog is shown
@@ -101,7 +116,7 @@ public final class IdeKeyEventDispatcher implements Disposable {
   }
 
   public boolean isWaitingForSecondKeyStroke(){
-    return getState() == STATE_WAIT_FOR_SECOND_KEYSTROKE || isPressedWasProcessed();
+    return getState() == KeyState.STATE_WAIT_FOR_SECOND_KEYSTROKE || isPressedWasProcessed();
   }
 
   /**
@@ -148,7 +163,7 @@ public final class IdeKeyEventDispatcher implements Disposable {
         // 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(STATE_PROCESSED);
+        setState(KeyState.STATE_PROCESSED);
         setPressedWasProcessed(true);
         return false;
       }
@@ -171,15 +186,15 @@ public final class IdeKeyEventDispatcher implements Disposable {
     myContext.setModalContext(isModalContext);
     myContext.setInputEvent(e);
 
-    if (getState() == STATE_INIT) {
+    if (getState() == KeyState.STATE_INIT) {
       return inInitState();
-    } else if (getState() == STATE_PROCESSED) {
+    } else if (getState() == KeyState.STATE_PROCESSED) {
       return inProcessedState();
-    } else if (getState() == STATE_WAIT_FOR_SECOND_KEYSTROKE) {
+    } else if (getState() == KeyState.STATE_WAIT_FOR_SECOND_KEYSTROKE) {
       return inWaitForSecondStrokeState();
-    } else if (getState() == STATE_SECOND_STROKE_IN_PROGRESS) {
+    } else if (getState() == KeyState.STATE_SECOND_STROKE_IN_PROGRESS) {
       return inSecondStrokeInProgressState();
-    } else if (getState() == STATE_KEY_GESTURE_PROCESSOR) {
+    } else if (getState() == KeyState.STATE_KEY_GESTURE_PROCESSOR) {
       return myKeyGestureProcessor.process();
     } else {
       throw new IllegalStateException("state = " + getState());
@@ -232,7 +247,7 @@ public final class IdeKeyEventDispatcher implements Disposable {
   private boolean inWaitForSecondStrokeState() {
     // a key pressed means that the user starts to enter the second stroke...
     if (KeyEvent.KEY_PRESSED==myContext.getInputEvent().getID()) {
-      setState(STATE_SECOND_STROKE_IN_PROGRESS);
+      setState(KeyState.STATE_SECOND_STROKE_IN_PROGRESS);
       return inSecondStrokeInProgressState();
     }
     // looks like RELEASEs (from the first stroke) go here...  skip them
@@ -274,7 +289,7 @@ public final class IdeKeyEventDispatcher implements Disposable {
     // when any key is released, we stop waiting for the second stroke
     if(KeyEvent.KEY_RELEASED==e.getID()){
       myFirstKeyStroke=null;
-      setState(STATE_INIT);
+      setState(KeyState.STATE_INIT);
       Project project = PlatformDataKeys.PROJECT.getData(myContext.getDataContext());
       WindowManager.getInstance().getStatusBar(project).setInfo(null);
       return false;
@@ -312,7 +327,7 @@ public final class IdeKeyEventDispatcher implements Disposable {
       //see IDEADEV-8615
       return true;
     }
-    setState(STATE_INIT);
+    setState(KeyState.STATE_INIT);
     setPressedWasProcessed(false);
     return inInitState();
   }
@@ -359,7 +374,7 @@ public final class IdeKeyEventDispatcher implements Disposable {
          e.getID() == KeyEvent.KEY_TYPED && hasMnemonicInWindow(focusOwner, e.getKeyChar())))
       {
         setPressedWasProcessed(true);
-        setState(STATE_PROCESSED);
+        setState(KeyState.STATE_PROCESSED);
         return false;
       }
     }
@@ -399,7 +414,11 @@ public final class IdeKeyEventDispatcher implements Disposable {
       }
 
       WindowManager.getInstance().getStatusBar(project).setInfo(message.toString());
-      setState(STATE_WAIT_FOR_SECOND_KEYSTROKE);
+
+      mySecondStrokeTimeout.cancelAllRequests();
+      mySecondStrokeTimeout.addRequest(mySecondStrokeTimeoutRunnable, Registry.intValue("actionSystem.secondKeystrokeTimout"));
+
+      setState(KeyState.STATE_WAIT_FOR_SECOND_KEYSTROKE);
       return true;
     }else{
       return processAction(e, myActionProcessor);
@@ -460,7 +479,7 @@ public final class IdeKeyEventDispatcher implements Disposable {
     }
 
     public void onUpdatePassed(final InputEvent inputEvent, final AnAction action, final AnActionEvent actionEvent) {
-      setState(STATE_PROCESSED);
+      setState(KeyState.STATE_PROCESSED);
       setPressedWasProcessed(inputEvent.getID() == KeyEvent.KEY_PRESSED);
     }
 
@@ -616,11 +635,11 @@ public final class IdeKeyEventDispatcher implements Disposable {
     myDisposed = true;
   }
 
-  public int getState() {
+  public KeyState getState() {
     return myState;
   }
 
-  public void setState(final int state) {
+  public void setState(final KeyState state) {
     myState = state;
     if (myQueue != null) {
       myQueue.maybeReady();
@@ -628,7 +647,7 @@ public final class IdeKeyEventDispatcher implements Disposable {
   }
 
   public void resetState() {
-    setState(STATE_INIT);
+    setState(KeyState.STATE_INIT);
     setPressedWasProcessed(false);
   }
 
@@ -641,6 +660,6 @@ public final class IdeKeyEventDispatcher implements Disposable {
   }
 
   public boolean isReady() {
-    return myState == STATE_INIT;
+    return myState == KeyState.STATE_INIT || myState == KeyState.STATE_PROCESSED;
   }
 }
diff --git a/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeyState.java b/platform/platform-impl/src/com/intellij/openapi/keymap/impl/KeyState.java
new file mode 100644 (file)
index 0000000..49d704e
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2000-2010 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.openapi.keymap.impl;
+
+public enum KeyState {
+  STATE_INIT,
+  STATE_WAIT_FOR_SECOND_KEYSTROKE,
+  STATE_SECOND_STROKE_IN_PROGRESS,
+  STATE_PROCESSED,
+  STATE_KEY_GESTURE_PROCESSOR;
+
+}
index 1aff4cc3e38d221f7d7db92fe856f0a83f2d99cf..5b0ca2e68bc6caf533d7990bd1f534070aaf8ae9 100644 (file)
@@ -18,6 +18,7 @@ package com.intellij.openapi.keymap.impl.keyGestures;
 import com.intellij.openapi.actionSystem.*;
 import com.intellij.openapi.keymap.impl.ActionProcessor;
 import com.intellij.openapi.keymap.impl.IdeKeyEventDispatcher;
+import com.intellij.openapi.keymap.impl.KeyState;
 import com.intellij.openapi.util.registry.Registry;
 
 import javax.swing.*;
@@ -96,14 +97,14 @@ public class KeyboardGestureProcessor {
   }
 
   void setState(KeyGestureState state) {
-    final boolean isGestureProcessingState = myDispatcher.getState() == IdeKeyEventDispatcher.STATE_KEY_GESTURE_PROCESSOR;
+    final boolean isGestureProcessingState = myDispatcher.getState() == KeyState.STATE_KEY_GESTURE_PROCESSOR;
     if (state == myWaitForStart) {
       myContext.actionKey = null;
       if (isGestureProcessingState) {
-        myDispatcher.setState(IdeKeyEventDispatcher.STATE_INIT);
+        myDispatcher.setState(KeyState.STATE_INIT);
       }
     } else if (state == myWaitForAction) {
-      myDispatcher.setState(IdeKeyEventDispatcher.STATE_KEY_GESTURE_PROCESSOR);
+      myDispatcher.setState(KeyState.STATE_KEY_GESTURE_PROCESSOR);
     }
     myState = state;
   }
index 69c8cd6d29dab6731768de4cb3beb0883d6df68b..c6c7d5fc746f88e885bc8d71e3f23e64720d91ed 100644 (file)
@@ -72,7 +72,7 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable {
   private final Set<Runnable> myIdleRequests = new com.intellij.util.containers.HashSet<Runnable>();
   private final EdtRunnable myIdleRunnable = new EdtRunnable() {
     public void runEdt() {
-      if (isFocusTransferReady() && !isIdleQueueEmpty()) {
+      if (isFocusTransferReady() && !isIdleQueueEmpty() && IdeEventQueue.getInstance().isReady()) {
         flushIdleRequests();
       }
       else {
index fa238ac53853b7d0d6d33d8856bae17b16cd9345..3e416e34df5556f663d21922073adc72c19d30bb 100644 (file)
@@ -19,6 +19,7 @@ actionSystem.mac.screenMenuNotUpdatedFix=false
 actionSystem.keyGestures.enabled=false
 actionSystem.suspendFocusTransferIfApplicationInactive=true
 actionSystem.noContextComponentWhileFocusTransfer=true
+actionSystem.secondKeystrokeTimout=2000
 
 ide.debugMode=false
 ide.debugMode.description=Record additonal information to make bug reports more informative