From fd0a8d6aac7172cb15af7080c744f97da2fe2ff2 Mon Sep 17 00:00:00 2001 From: Denis Fokin Date: Thu, 15 Sep 2016 16:43:04 +0300 Subject: [PATCH 1/2] IDEA-35480 Per project/frame modality (enter/leave modal per project) --- .../impl/quickfix/ImportClassFixBase.java | 7 +- .../application/impl/LaterInvocator.java | 97 +++++++++++++++++-- .../ui/impl/DialogWrapperPeerImpl.java | 14 ++- 3 files changed, 106 insertions(+), 12 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFixBase.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFixBase.java index 002e605f10d3..12d6646f9a2f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFixBase.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ImportClassFixBase.java @@ -36,6 +36,7 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.packageDependencies.DependencyRule; import com.intellij.packageDependencies.DependencyValidationManager; @@ -319,13 +320,17 @@ public abstract class ImportClassFixBase action.execute()); 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 c7f1769b4d73..1322ffaba79d 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 @@ -23,6 +23,7 @@ import com.intellij.openapi.diagnostic.FrequentEventDetector; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.ActionCallback; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Conditions; @@ -33,6 +34,7 @@ import com.intellij.util.ExceptionUtil; import com.intellij.util.concurrency.Semaphore; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.Stack; +import com.intellij.util.containers.WeakHashMap; import com.intellij.util.ui.UIUtil; import io.netty.util.internal.SystemPropertyUtil; import org.jetbrains.annotations.NonNls; @@ -80,7 +82,13 @@ public class LaterInvocator { } } + // Application modal entities private static final List ourModalEntities = ContainerUtil.createLockFreeCopyOnWriteList(); + + // Per-project modal entities + private static WeakHashMap> projectToModalEntities = new WeakHashMap<>(); + private static WeakHashMap> projectToModalEntitiesStack = new WeakHashMap<>(); + private static final Stack ourModalityStack = new Stack<>(ModalityState.NON_MODAL); private static final List ourQueue = new ArrayList<>(); //protected by LOCK private static volatile int ourQueueSkipCount; // optimization @@ -204,6 +212,60 @@ public class LaterInvocator { } } + public static void enterModal(Project project, Dialog dialog) { + LOG.assertTrue(isDispatchThread(), "enterModal() should be invoked in event-dispatch thread"); + + if (LOG.isDebugEnabled()) { + LOG.debug("enterModal:" + dialog.getName() + " ; for project: " + project.getName()); + } + + if (project == null) { + enterModal(dialog); + } + + List modalEntitiesList = projectToModalEntities.getOrDefault(project, ContainerUtil.createLockFreeCopyOnWriteList()); + projectToModalEntities.put(project, modalEntitiesList); + modalEntitiesList.add(dialog); + + Stack modalEntitiesStack = projectToModalEntitiesStack.getOrDefault(project, new Stack<>(ModalityState.NON_MODAL)); + projectToModalEntitiesStack.put(project, modalEntitiesStack); + modalEntitiesStack.push(new ModalityStateEx(ArrayUtil.toObjectArray(ourModalEntities))); + } + + + public static void leaveModal(Project project, Dialog dialog) { + LOG.assertTrue(isDispatchThread(), "leaveModal() should be invoked in event-dispatch thread"); + + if (LOG.isDebugEnabled()) { + LOG.debug("leaveModal:" + dialog.getName() + " ; for project: " + project.getName()); + } + + ourModalityStateMulticaster.getMulticaster().beforeModalityStateChanged(false); + + int index = ourModalEntities.indexOf(dialog); + + if (index != -1) { + ourModalEntities.remove(index); + ourModalityStack.remove(index + 1); + for (int i = 1; i < ourModalityStack.size(); i++) { + ((ModalityStateEx)ourModalityStack.get(i)).removeModality(dialog); + } + } else { + List dialogs = projectToModalEntities.get(project); + int perProjectIndex = dialogs.indexOf(dialog); + LOG.assertTrue(perProjectIndex >= 0); + dialogs.remove(perProjectIndex); + Stack states = projectToModalEntitiesStack.get(project); + states.remove(perProjectIndex + 1); + for (int i = 1; i < states.size(); i++) { + ((ModalityStateEx)states.get(i)).removeModality(dialog); + } + } + + ourQueueSkipCount = 0; + requestFlush(); + } + public static void leaveModal(@NotNull Object modalEntity) { LOG.assertTrue(isDispatchThread(), "leaveModal() should be invoked in event-dispatch thread"); @@ -235,13 +297,17 @@ public class LaterInvocator { requestFlush(); } + public static Object[] getCurrentModalEntitiesForProject(Project project) { + ApplicationManager.getApplication().assertIsDispatchThread(); + if (ourModalEntities.isEmpty()) { + return ArrayUtil.toObjectArray(ourModalEntities); + } + return ArrayUtil.toObjectArray(projectToModalEntities.get(project)); + } + @NotNull public static Object[] getCurrentModalEntities() { - ApplicationManager.getApplication().assertIsDispatchThread(); - //TODO! - //LOG.assertTrue(IdeEventQueue.getInstance().isInInputEvent() || isInMyRunnable()); - - return ArrayUtil.toObjectArray(ourModalEntities); + return getCurrentModalEntitiesForProject(null); } @NotNull @@ -249,9 +315,22 @@ public class LaterInvocator { return ourModalityStack.peek(); } - public static boolean isInModalContext() { + public static boolean isInModalContextForProject(final Project project) { LOG.assertTrue(isDispatchThread()); - return !ourModalEntities.isEmpty(); + + if (ourModalEntities.isEmpty()) return false; + + List modalEntitiesForProject = getModalEntitiesForProject(project); + + return modalEntitiesForProject == null || modalEntitiesForProject.isEmpty(); + } + + private static List getModalEntitiesForProject(Project project) { + return projectToModalEntities.get(project); + } + + public static boolean isInModalContext() { + return isInModalContextForProject(null); } private static boolean isDispatchThread() { @@ -268,8 +347,8 @@ public class LaterInvocator { * There might be some requests in the queue, but ourFlushQueueRunnable might not be scheduled yet. In these circumstances * {@link EventQueue#peekEvent()} default implementation would return null, and {@link UIUtil#dispatchAllInvocationEvents()} would * stop processing events too early and lead to spurious test failures. - * - * @see IdeEventQueue#peekEvent() + * + * @see IdeEventQueue#peekEvent() */ public static boolean ensureFlushRequested() { if (getNextEvent(false) != null) { diff --git a/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java b/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java index f0ff9a88e804..5c0c912c4542 100644 --- a/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/ui/impl/DialogWrapperPeerImpl.java @@ -438,9 +438,15 @@ public class DialogWrapperPeerImpl extends DialogWrapperPeer implements FocusTra boolean changeModalityState = appStarted && myDialog.isModal() && !isProgressDialog(); // ProgressWindow starts a modality state itself + Project project = myProject; + if (changeModalityState) { commandProcessor.enterModal(); - LaterInvocator.enterModal(myDialog); + if (Registry.is("ide.perProjectModality")) { + LaterInvocator.enterModal(project, myDialog.getWindow()); + } else { + LaterInvocator.enterModal(myDialog); + } } if (appStarted) { @@ -453,7 +459,11 @@ public class DialogWrapperPeerImpl extends DialogWrapperPeer implements FocusTra finally { if (changeModalityState) { commandProcessor.leaveModal(); - LaterInvocator.leaveModal(myDialog); + if (Registry.is("ide.perProjectModality")) { + LaterInvocator.leaveModal(project, myDialog.getWindow()); + } else { + LaterInvocator.leaveModal(myDialog); + } } myDialog.getFocusManager().doWhenFocusSettlesDown(result.createSetDoneRunnable()); From 419af8ce27fc310c718cec49a0250d299067af49 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Thu, 15 Sep 2016 16:28:40 +0300 Subject: [PATCH 2/2] typo --- .../inspections/MigrateAssertToMatcherAssertInspection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/inspections/MigrateAssertToMatcherAssertInspection.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/inspections/MigrateAssertToMatcherAssertInspection.java index 0b583803a984..41a2e42111d9 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/inspections/MigrateAssertToMatcherAssertInspection.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/inspections/MigrateAssertToMatcherAssertInspection.java @@ -142,7 +142,7 @@ public class MigrateAssertToMatcherAssertInspection extends LocalInspectionTool replaced = TypeConversionDescriptor.replaceExpression(methodCall, templatePair.getFirst(), templatePair.getSecond()); } catch (IncorrectOperationException e) { - LOG.error("Replacer can't can't match expression:\n" + + LOG.error("Replacer can't match expression:\n" + methodCall.getText() + "\nwith replacement template:\n(" + templatePair.getFirst() +