diff --git a/platform/core-api/src/com/intellij/openapi/util/SimpleTimer.java b/platform/core-api/src/com/intellij/openapi/util/SimpleTimer.java index c04d74b0a3e1..68cc8f97a3d8 100644 --- a/platform/core-api/src/com/intellij/openapi/util/SimpleTimer.java +++ b/platform/core-api/src/com/intellij/openapi/util/SimpleTimer.java @@ -18,6 +18,7 @@ package com.intellij.openapi.util; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProcessCanceledException; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import java.util.*; @@ -30,21 +31,23 @@ public class SimpleTimer { private final Timer ourTimer; - private static final SimpleTimer ourInstance = new SimpleTimer(); - @NonNls private static final String THREAD_NAME = "SimpleTimer"; + private static final SimpleTimer ourInstance = newInstance("Shared SimpleTimer"); private long myNextScheduledTime = Long.MAX_VALUE; private TimerTask myNextProcessingTask; private final Map> myTime2Task = new TreeMap>(); + private String myThreadName; - private SimpleTimer() { + private SimpleTimer(@NotNull String threadName) { + myThreadName = threadName; + final Thread thread = Thread.currentThread(); final int currentPrio = thread.getPriority(); try { // need this becase the timer's thread will inherit the priority on creation thread.setPriority(Thread.MIN_PRIORITY + 1); - ourTimer = new Timer(THREAD_NAME, true); + ourTimer = new Timer(threadName, true); } finally { thread.setPriority(currentPrio); @@ -54,6 +57,10 @@ public class SimpleTimer { public static SimpleTimer getInstance() { return ourInstance; } + + public static SimpleTimer newInstance(@NotNull String name) { + return new SimpleTimer(name); + } public SimpleTimerTask setUp(final Runnable runnable, long delay) { synchronized (myTime2Task) { @@ -150,7 +157,7 @@ public class SimpleTimer { } public boolean isTimerThread(Thread thread) { - return THREAD_NAME.equals(thread.getName()); + return myThreadName.equals(thread.getName()); } void onCancelled(SimpleTimerTask task) { diff --git a/platform/funcTests/project1/module1/scripts/generateActions/_suite.ijs b/platform/funcTests/project1/module1/scripts/generateActions/_suite.ijs new file mode 100644 index 000000000000..4e3edad05246 --- /dev/null +++ b/platform/funcTests/project1/module1/scripts/generateActions/_suite.ijs @@ -0,0 +1,6 @@ +%startTest Generate actions + +%include extract_Superclass.ijs +%include newClass_projectView.ijs + +%endTest \ No newline at end of file diff --git a/platform/funcTests/project1/module1/scripts/generateActions/extract_Superclass.ijs b/platform/funcTests/project1/module1/scripts/generateActions/extract_Superclass.ijs index 7012ffb1e60a..c78f7c40a9db 100644 --- a/platform/funcTests/project1/module1/scripts/generateActions/extract_Superclass.ijs +++ b/platform/funcTests/project1/module1/scripts/generateActions/extract_Superclass.ijs @@ -1,12 +1,15 @@ -%startTest Create superclass +%startTest Create superclass from popup menu -%%include ../include/project1Init.ijs -%%action GotoClass -%%PsiManager\n - -%include ../include/init.ijs +%include ../include/project1Init.ijs +%action GotoClass +FileEditorManager\n +%call checkFocus(editorTab=FileEditorManager.java) %call contextMenu(Refactor|Extract Superclass) TestSuperclass\n -%call checkFocus(dialog=Analyze and Replace Usages) +%call waitForDialog(Analyze and Replace Usages) +%[space] +%call waitForDialog(Use Interface Where Possible) +%[space] +%call checkFocus(editorTab=FileEditorManager.java) %endTest \ No newline at end of file diff --git a/platform/funcTests/project1/module1/scripts/navbar/basicTest.ijs b/platform/funcTests/project1/module1/scripts/navbar/basicTest.ijs index ad5ac84c24ce..29ee97d49e8b 100644 --- a/platform/funcTests/project1/module1/scripts/navbar/basicTest.ijs +++ b/platform/funcTests/project1/module1/scripts/navbar/basicTest.ijs @@ -8,9 +8,9 @@ PsiManager\n %[left] %[left] %[down] -%call checkFocus(navBar=module1>src>com>intellij>[testProject]>idea>PsiManager|navBarPopup=[fabrique]>idea>mps) +%call checkFocus(navBar=module1>src>com>intellij>[testProject]>idea>PsiManager|navBarPopup=fabrique>[idea]>mps) \n %call flushUi() \n -%call checkFocus(editorTab=ActiveLibrary.java) +%call checkFocus(editorTab=FileEditorManager.java) diff --git a/platform/platform-api/src/com/intellij/openapi/util/EdtRunnable.java b/platform/platform-api/src/com/intellij/openapi/util/EdtRunnable.java index 8e470f548a62..84f174706322 100644 --- a/platform/platform-api/src/com/intellij/openapi/util/EdtRunnable.java +++ b/platform/platform-api/src/com/intellij/openapi/util/EdtRunnable.java @@ -17,15 +17,28 @@ package com.intellij.openapi.util; import com.intellij.util.ui.UIUtil; -public abstract class EdtRunnable implements Runnable { +public abstract class EdtRunnable implements ExpirableRunnable { + + private boolean myExpired; public final void run() { UIUtil.invokeLaterIfNeeded(new Runnable() { public void run() { - runEdt(); + if (!isExpired()) { + runEdt(); + } } }); } + public void expire() { + myExpired = true; + } + + @Override + public boolean isExpired() { + return myExpired; + } + public abstract void runEdt(); } \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/ide/impl/DataManagerImpl.java b/platform/platform-impl/src/com/intellij/ide/impl/DataManagerImpl.java index df0256266971..d26dcdd0afc8 100644 --- a/platform/platform-impl/src/com/intellij/ide/impl/DataManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ide/impl/DataManagerImpl.java @@ -319,10 +319,8 @@ public class DataManagerImpl extends DataManager implements ApplicationComponent myEventCount + "; current event count = " + currentEventCount ); */ - if (false) { LOG.error("cannot share data context between Swing events; initial event count = " + myEventCount + "; current event count = " + currentEventCount); - } } Component _component = myRef.get(); diff --git a/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressWindow.java b/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressWindow.java index c1b6548b60ff..c9684dbc24cb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressWindow.java +++ b/platform/platform-impl/src/com/intellij/openapi/progress/util/ProgressWindow.java @@ -247,19 +247,25 @@ public class ProgressWindow extends BlockingProgressIndicator implements Disposa super.stop(); - if (myDialog != null) { - myDialog.hide(); - if (myDialog.wasShown()) { - myFocusTrackback.restoreFocus(); - } - else { - myFocusTrackback.consume(); - } - } - myStoppedAlready = true; + UIUtil.invokeLaterIfNeeded(new Runnable() { + @Override + public void run() { + if (myDialog != null) { + myDialog.hide(); + if (myDialog.wasShown()) { + myFocusTrackback.restoreFocus(); + } + else { + myFocusTrackback.consume(); + } + } - Disposer.dispose(this); + myStoppedAlready = true; + + Disposer.dispose(ProgressWindow.this); + } + }); SwingUtilities.invokeLater(EmptyRunnable.INSTANCE); // Just to give blocking dispatching a chance to go out. } diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/playback/util/WindowSystemPlaybackCall.java b/platform/platform-impl/src/com/intellij/openapi/ui/playback/util/WindowSystemPlaybackCall.java index 84f2f0dfd96c..6f31ee622eeb 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/playback/util/WindowSystemPlaybackCall.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/playback/util/WindowSystemPlaybackCall.java @@ -24,6 +24,9 @@ import com.intellij.openapi.ui.Queryable; import com.intellij.openapi.ui.playback.PlaybackContext; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.AsyncResult; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.SimpleTimer; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.openapi.wm.IdeFrame; import com.intellij.openapi.wm.ToolWindow; @@ -34,8 +37,10 @@ import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.*; +import java.awt.event.AWTEventListener; import java.awt.event.InputEvent; import java.awt.event.MouseEvent; +import java.awt.event.WindowEvent; import java.util.*; public class WindowSystemPlaybackCall { @@ -67,6 +72,46 @@ public class WindowSystemPlaybackCall { return result; } + + public static AsyncResult waitForDialog(final PlaybackContext context, final String title) { + final AsyncResult result = new AsyncResult(); + + final Ref listener = new Ref(); + listener.set(new AWTEventListener() { + @Override + public void eventDispatched(AWTEvent event) { + if (event.getID() == WindowEvent.WINDOW_ACTIVATED) { + final Window wnd = ((WindowEvent)event).getWindow(); + if (wnd instanceof JDialog) { + if (title.equals(((JDialog)wnd).getTitle())) { + Toolkit.getDefaultToolkit().removeAWTEventListener(listener.get()); + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + getUiReady(context).notify(result); + } + }); + } + } + } + } + }); + + Toolkit.getDefaultToolkit().addAWTEventListener(listener.get(), WindowEvent.WINDOW_EVENT_MASK); + + SimpleTimer.getInstance().setUp(new Runnable() { + @Override + public void run() { + Toolkit.getDefaultToolkit().removeAWTEventListener(listener.get()); + if (!result.isProcessed()) { + result.setRejected("Timed out waiting for window: " + title); + } + } + }, Registry.intValue("actionSystem.commandProcessingTimeout")); + + return result; + } + public static AsyncResult checkFocus(final PlaybackContext context, String expected) { final AsyncResult result = new AsyncResult(); final Map expectedMap = new LinkedHashMap(); diff --git a/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java index 42c57c9dd791..0973b1662520 100644 --- a/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/wm/impl/FocusManagerImpl.java @@ -27,10 +27,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.application.ex.ApplicationManagerEx; import com.intellij.openapi.ui.popup.JBPopup; -import com.intellij.openapi.util.ActionCallback; -import com.intellij.openapi.util.EdtRunnable; -import com.intellij.openapi.util.Expirable; -import com.intellij.openapi.util.ExpirableRunnable; +import com.intellij.openapi.util.*; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.wm.*; import com.intellij.openapi.wm.ex.IdeFocusTraversalPolicy; @@ -79,24 +76,11 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { final EdtAlarm myFocusedComponentAlaram; private final EdtAlarm myForcedFocusRequestsAlarm; + private SimpleTimer myTimer = SimpleTimer.newInstance("FocusManager timer"); + private final EdtAlarm myIdleAlarm; private final Set myIdleRequests = new LinkedHashSet(); - private final EdtRunnable myIdleRunnable = new EdtRunnable() { - public void runEdt() { - if (canFlushIdleRequests()) { - flushIdleRequests(); - } - else { - if (processFocusRevalidation()) { - if (isFocusTransferReady()) { - flushIdleRequests(); - } - } - - restartIdleAlarm(); - } - } - }; + private boolean myFlushWasDelayedToFixFocus; private ExpirableRunnable myFocusRevalidator; @@ -106,6 +90,24 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { private UiActivityMonitor myActivityMonitor; private boolean myTypeaheadEnabled = true; + + private class IdleRunnable extends EdtRunnable { + public void runEdt() { + if (canFlushIdleRequests()) { + flushIdleRequests(); + } + else { + if (processFocusRevalidation()) { + if (isFocusTransferReady()) { + flushIdleRequests(); + } + } + + restartIdleAlarm(); + } + } + } + private boolean canFlushIdleRequests() { Component focusOwner = getFocusOwner(); return isFocusTransferReady() @@ -128,9 +130,9 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { myQueue = IdeEventQueue.getInstance(); myActivityMonitor = UiActivityMonitor.getInstance(); - myFocusedComponentAlaram = new EdtAlarm(this); - myForcedFocusRequestsAlarm = new EdtAlarm(this); - myIdleAlarm = new EdtAlarm(this); + myFocusedComponentAlaram = new EdtAlarm(); + myForcedFocusRequestsAlarm = new EdtAlarm(); + myIdleAlarm = new EdtAlarm(); final AppListener myAppListener = new AppListener(); myApp.getMessageBus().connect().subscribe(ApplicationActivationListener.TOPIC, myAppListener); @@ -183,6 +185,8 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { @NotNull public ActionCallback requestFocus(@NotNull final FocusCommand command, final boolean forced) { + assertDispatchThread(); + if (isInternalMode) { recordCommand(command, new Throwable(), forced); } @@ -460,7 +464,7 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { private void restartIdleAlarm() { myIdleAlarm.cancelAllRequests(); - myIdleAlarm.addRequest(myIdleRunnable, Registry.intValue("actionSystem.focusIdleTimeout")); + myIdleAlarm.addRequest(new IdleRunnable(), Registry.intValue("actionSystem.focusIdleTimeout")); } private void flushIdleRequests() { @@ -602,6 +606,8 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { } public boolean isFocusTransferReady() { + assertDispatchThread(); + if (myRunContext != null) return true; invalidateFocusRequestsQueue(); @@ -741,6 +747,8 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { } public void suspendKeyProcessingUntil(@NotNull final ActionCallback done) { + assertDispatchThread(); + requestFocus(new FocusCommand(done) { public ActionCallback run() { return done; @@ -749,6 +757,8 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { } public Expirable getTimestamp(final boolean trackOnlyForcedCommands) { + assertDispatchThread(); + return new Expirable() { long myOwnStamp = trackOnlyForcedCommands ? myForcedCmdTimestamp : myCmdTimestamp; @@ -760,6 +770,8 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { @Override public FocusRequestor getFurtherRequestor() { + assertDispatchThread(); + FurtherRequestor requestor = new FurtherRequestor(this, getTimestamp(true)); myValidFurtherRequestors.add(requestor); revalidateFurtherRequestors(); @@ -789,6 +801,8 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { @Override public Component getFocusOwner() { + assertDispatchThread(); + Component result = null; if (myRunContext != null) { result = (Component)myRunContext.getData(PlatformDataKeys.CONTEXT_COMPONENT.getName()); @@ -815,6 +829,8 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { @Override public void runOnOwnContext(DataContext context, Runnable runnable) { + assertDispatchThread(); + myRunContext = context; try { runnable.run(); @@ -825,12 +841,16 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { @Override public Component getLastFocusedFor(IdeFrame frame) { + assertDispatchThread(); + WeakReference ref = myLastFocused.get(frame); return ref != null ? ref.get() : null; } @Override public void toFront(JComponent c) { + assertDispatchThread(); + if (c == null) return; final Window window = UIUtil.getParentOfType(Window.class, c); @@ -877,19 +897,20 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { } - static class EdtAlarm { - private final Alarm myAlarm; - - private EdtAlarm(Disposable parent) { - myAlarm = new Alarm(Alarm.ThreadToUse.OWN_THREAD, parent); - } + class EdtAlarm { + private Set myRequests = new HashSet(); + public void cancelAllRequests() { - myAlarm.cancelAllRequests(); + for (EdtRunnable each : myRequests) { + each.expire(); + } + myRequests.clear(); } public void addRequest(EdtRunnable runnable, int delay) { - myAlarm.addRequest(runnable, delay); + myRequests.add(runnable); + myTimer.setUp(runnable, delay); } } @@ -1018,4 +1039,8 @@ public class FocusManagerImpl extends IdeFocusManager implements Disposable { public boolean isFocusTransferEnabled() { return myApp.isActive() || !Registry.is("actionSystem.suspendFocusTransferIfApplicationInactive"); } + + private void assertDispatchThread() { + ApplicationManager.getApplication().assertIsDispatchThread(); + } }