diff --git a/platform/core-impl/src/com/intellij/openapi/application/impl/ModalityStateEx.java b/platform/core-impl/src/com/intellij/openapi/application/impl/ModalityStateEx.java index 8f5800481b44..313998d39405 100644 --- a/platform/core-impl/src/com/intellij/openapi/application/impl/ModalityStateEx.java +++ b/platform/core-impl/src/com/intellij/openapi/application/impl/ModalityStateEx.java @@ -17,44 +17,30 @@ package com.intellij.openapi.application.impl; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.util.Function; -import com.intellij.util.containers.ContainerUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.containers.WeakList; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collections; import java.util.List; public class ModalityStateEx extends ModalityState { - private static final WeakReference[] EMPTY_REFS_ARRAY = new WeakReference[0]; + private final WeakList myModalEntities = new WeakList(); - private final WeakReference[] myModalEntities; - - public ModalityStateEx() { - this(EMPTY_REFS_ARRAY); - } + public ModalityStateEx() { } // used by reflection to initialize NON_MODAL public ModalityStateEx(@NotNull Object[] modalEntities) { - if (modalEntities.length > 0) { - myModalEntities = new WeakReference[modalEntities.length]; - for (int i = 0; i < modalEntities.length; i++) { - Object entity = modalEntities[i]; - myModalEntities[i] = new WeakReference(entity); - } - } - else{ - myModalEntities = EMPTY_REFS_ARRAY; - } + Collections.addAll(myModalEntities, modalEntities); } private List getModalEntities() { - return ContainerUtil.mapNotNull(myModalEntities, new Function() { - @Override - public Object fun(WeakReference reference) { - return reference.get(); - } - }); + ArrayList result = new ArrayList(); + for (Object entity : myModalEntities) { + result.add(entity); + } + return result; } @NotNull @@ -64,8 +50,9 @@ public class ModalityStateEx extends ModalityState { @NotNull ModalityStateEx appendEntity(@NotNull Object anEntity){ - List list = new ArrayList(myModalEntities.length+1); - list.addAll(getModalEntities()); + List modalEntities = getModalEntities(); + List list = new ArrayList(modalEntities.size() + 1); + list.addAll(modalEntities); list.add(anEntity); return new ModalityStateEx(list.toArray()); } @@ -81,21 +68,10 @@ public class ModalityStateEx extends ModalityState { return false; } - boolean contains(@NotNull Object modalEntity) { - return getModalEntities().contains(modalEntity); - } - @NonNls public String toString() { - if (myModalEntities.length == 0) return "ModalityState.NON_MODAL"; - @NonNls StringBuilder buffer = new StringBuilder(); - buffer.append("ModalityState:"); - for (int i = 0; i < myModalEntities.length; i++) { - Object entity = myModalEntities[i].get(); - if (i > 0) buffer.append(", "); - buffer.append(entity); - } - return buffer.toString(); + List modalEntities = getModalEntities(); + return modalEntities.isEmpty() ? "ModalityState.NON_MODAL" : "ModalityState:" + StringUtil.join(modalEntities, ", "); } @Override @@ -113,4 +89,8 @@ public class ModalityStateEx extends ModalityState { public int hashCode() { return getModalEntities().hashCode(); } + + void removeModality(Object modalEntity) { + myModalEntities.remove(modalEntity); + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/application/impl/LaterInvocator.java b/platform/platform-impl/src/com/intellij/openapi/application/impl/LaterInvocator.java index 79327119cb9a..1fcb451d0a6d 100644 --- a/platform/platform-impl/src/com/intellij/openapi/application/impl/LaterInvocator.java +++ b/platform/platform-impl/src/com/intellij/openapi/application/impl/LaterInvocator.java @@ -41,7 +41,6 @@ import org.jetbrains.annotations.TestOnly; import javax.swing.*; import java.awt.*; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; @@ -89,8 +88,6 @@ public class LaterInvocator { private static final EventDispatcher ourModalityStateMulticaster = EventDispatcher.create(ModalityStateListener.class); - private static final List ourForcedFlushQueue = new ArrayList(); - public static void addModalityStateListener(@NotNull ModalityStateListener listener, @NotNull Disposable parentDisposable) { if (!ourModalityStateMulticaster.getListeners().contains(listener)) { ourModalityStateMulticaster.addListener(listener, parentDisposable); @@ -204,43 +201,25 @@ public class LaterInvocator { LOG.debug("leaveModal:" + modalEntity); } - //noinspection StatementWithEmptyBody - while (ourFlushQueueRunnable.runNextEvent()); - ourModalityStateMulticaster.getMulticaster().beforeModalityStateChanged(false); - boolean removed = ourModalEntities.remove(modalEntity); - if (ourModalityStack.size() > 1) { - ourModalityStack.pop(); + int index = ourModalEntities.indexOf(modalEntity); + LOG.assertTrue(index >= 0); + ourModalEntities.remove(index); + ourModalityStack.remove(index + 1); + for (int i = 1; i < ourModalityStack.size(); i++) { + ((ModalityStateEx)ourModalityStack.get(i)).removeModality(modalEntity); } - LOG.assertTrue(removed, modalEntity); - LOG.assertTrue(!ourModalityStack.isEmpty()); TransactionGuardImpl guard = IdeaApplication.isLoaded() ? (TransactionGuardImpl)TransactionGuard.getInstance() : null; if (guard != null) { guard.leftModality(modalEntity); } - cleanupQueueForModal(modalEntity); ourQueueSkipCount = 0; requestFlush(); } - private static void cleanupQueueForModal(@NotNull final Object modalEntity) { - synchronized (LOCK) { - for (Iterator iterator = ourQueue.iterator(); iterator.hasNext(); ) { - RunnableInfo runnableInfo = iterator.next(); - if (runnableInfo.modalityState instanceof ModalityStateEx) { - ModalityStateEx stateEx = (ModalityStateEx)runnableInfo.modalityState; - if (stateEx.contains(modalEntity)) { - ourForcedFlushQueue.add(runnableInfo); - iterator.remove(); - } - } - } - } - } - @TestOnly public static void leaveAllModals() { while (!ourModalEntities.isEmpty()) { @@ -298,17 +277,6 @@ public class LaterInvocator { @Nullable private static RunnableInfo getNextEvent(boolean remove) { synchronized (LOCK) { - if (!ourForcedFlushQueue.isEmpty()) { - final RunnableInfo toRun = remove ? ourForcedFlushQueue.remove(0) : ourForcedFlushQueue.get(0); - if (!toRun.expired.value(null)) { - return toRun; - } - else { - toRun.callback.setDone(); - } - } - - ModalityState currentModality = getCurrentModalityState(); while (ourQueueSkipCount < ourQueue.size()) { diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/LaterInvocatorTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/LaterInvocatorTest.java index 4eebdf11364f..b6b7966c2000 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/LaterInvocatorTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/application/impl/LaterInvocatorTest.java @@ -202,35 +202,6 @@ public class LaterInvocatorTest extends PlatformTestCase { }); } - public void testRunQueuedRunnablesOnLeavingModality() { - UIUtil.invokeAndWaitIfNeeded(new Runnable() { - @Override - public void run() { - LaterInvocator.invokeLater(ENTER_MODAL, ModalityState.NON_MODAL); - LaterInvocator.invokeLater(new MyRunnable("3"), ModalityState.NON_MODAL); - flushSwingQueue(); - checkOrder(0); - - final ModalityState modalityState = ModalityState.stateForComponent(myWindow1); - LaterInvocator.invokeLater(new MyRunnable("1") { - @Override - public void run() { - super.run(); - checkOrder(1); - LaterInvocator.invokeLater(new MyRunnable("2"), modalityState); - checkOrder(1); - LaterInvocator.leaveModal(myWindow1); - checkOrder(2); - } - }, modalityState); - flushSwingQueue(); // let "1" run - - flushSwingQueue(); // let "3" run - checkOrder(3); - } - }); - } - public void testStress() throws Exception { UIUtil.invokeAndWaitIfNeeded((Runnable)() -> { int N = 1000; @@ -551,4 +522,45 @@ public class LaterInvocatorTest extends PlatformTestCase { assertSame(modalityState1, ModalityState.current()); }); } + + public void testNonNestedModalityState() { //happens with per-project modality + Object modal1 = new Object(); + Object modal2 = new Object(); + UIUtil.invokeAndWaitIfNeeded((Runnable)() -> { + LaterInvocator.enterModal(modal1); // [modal1] + ModalityState ms_1 = ModalityState.current(); + ApplicationManager.getApplication().invokeLater(new MyRunnable("m1"), ms_1); + + + LaterInvocator.enterModal(modal2); //[modal1, modal2] + ModalityState ms_12 = ModalityState.current(); + assertNotSame(ms_1, ms_12); + assertTrue(ms_12.dominates(ms_1)); + + UIUtil.dispatchAllInvocationEvents(); + assertEmpty(myOrder); + + ApplicationManager.getApplication().invokeLater(new MyRunnable("m12"), ms_12); + + + LaterInvocator.leaveModal(modal1); // [modal2] + assertEmpty(myOrder); + UIUtil.dispatchAllInvocationEvents(); + assertOrderedEquals(myOrder, "m12"); + + ModalityState ms_2 = ModalityState.current(); + assertSame(ms_12, ms_2); + assertTrue(ms_2.dominates(ms_1)); + + ApplicationManager.getApplication().invokeLater(new MyRunnable("m1x"), ms_1); + ApplicationManager.getApplication().invokeLater(new MyRunnable("m2"), ms_2); + UIUtil.dispatchAllInvocationEvents(); + assertOrderedEquals(myOrder, "m12", "m2"); + + + LaterInvocator.leaveModal(modal2); // NON_MODAL + UIUtil.dispatchAllInvocationEvents(); + assertOrderedEquals(myOrder, "m12", "m2", "m1", "m1x"); + }); + } }