From 1910030993d691a32e6d8a952c74dcad88fb97be Mon Sep 17 00:00:00 2001 From: JohnHake Date: Fri, 28 Feb 2020 12:32:49 -0800 Subject: [PATCH] Utils added IJSDK-681 --- .../maxOpenPrj/ProjectCountingService.java | 3 - .../maxOpenPrj/ProjectOpenCloseListener.java | 87 +++------------ .../intellij/sdk/utils/SdkBalloonHelper.java | 104 ++++++++++++++++++ 3 files changed, 122 insertions(+), 72 deletions(-) create mode 100644 max_opened_projects/src/main/java/org/intellij/sdk/utils/SdkBalloonHelper.java diff --git a/max_opened_projects/src/main/java/org/intellij/sdk/maxOpenPrj/ProjectCountingService.java b/max_opened_projects/src/main/java/org/intellij/sdk/maxOpenPrj/ProjectCountingService.java index 4af162d2a..61a83f084 100644 --- a/max_opened_projects/src/main/java/org/intellij/sdk/maxOpenPrj/ProjectCountingService.java +++ b/max_opened_projects/src/main/java/org/intellij/sdk/maxOpenPrj/ProjectCountingService.java @@ -2,9 +2,6 @@ package org.intellij.sdk.maxOpenPrj; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.ProjectManager; - /** * Application service implementation to keep a running count of * how many projects are open at a given time. diff --git a/max_opened_projects/src/main/java/org/intellij/sdk/maxOpenPrj/ProjectOpenCloseListener.java b/max_opened_projects/src/main/java/org/intellij/sdk/maxOpenPrj/ProjectOpenCloseListener.java index 0f37660a4..5e3dad8b5 100644 --- a/max_opened_projects/src/main/java/org/intellij/sdk/maxOpenPrj/ProjectOpenCloseListener.java +++ b/max_opened_projects/src/main/java/org/intellij/sdk/maxOpenPrj/ProjectOpenCloseListener.java @@ -2,25 +2,20 @@ package org.intellij.sdk.maxOpenPrj; -import com.intellij.openapi.Disposable; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; -import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.project.ProjectManagerListener; -import com.intellij.openapi.ui.popup.Balloon; -import com.intellij.openapi.ui.popup.JBPopupFactory; -import com.intellij.openapi.util.Disposer; -import com.intellij.openapi.wm.WindowManager; -import com.intellij.util.ui.UIUtil; +import org.intellij.sdk.utils.SdkBalloonHelper; import org.jetbrains.annotations.NotNull; -import javax.swing.*; - /** * Listener to detect project open and close. - * Depends on the org.intellij.sdk.maxOpenPrj.ProjectCountingService + * Depends on org.intellij.sdk.maxOpenPrj.ProjectCountingService + * Depends on org.intellij.sdk.utils.SdkBalloonHelper */ -public class ProjectOpenCloseListener implements ProjectManagerListener, Disposable { +public class ProjectOpenCloseListener implements ProjectManagerListener { + private static final String MAX_OPEN_PROJ_DISCLAIM = "This is an IntelliJ Platform SDK demo.

"; /** * Invoked on project open. @@ -29,15 +24,19 @@ public class ProjectOpenCloseListener implements ProjectManagerListener, Disposa */ @Override public void projectOpened(@NotNull Project project) { + // Ensure this isn't part of testing + if (ApplicationManager.getApplication().isUnitTestMode()) return; // Get the counting service ProjectCountingService projectCountingService = ServiceManager.getService(ProjectCountingService.class); // Increment the project count projectCountingService.incrProjectCount(); // See if the total # of projects violates the limit. if (projectCountingService.projectLimitExceeded()) { - System.out.println("\n\nOpen limit transition called for: " + project.toString()); // Transitioned to outside the limit - showBalloon("projectOpened", project, "Opening Project \"" + project.getName() + "\"", "The number of open projects exceeds the SDK plugin max_opened_projects limit."); + String title = String.format("Opening Project \"%s\"", project.getName()); + String message = MAX_OPEN_PROJ_DISCLAIM + "The number of open projects exceeds the SDK plugin max_opened_projects limit."; + SdkBalloonHelper balloonHelper = SdkBalloonHelper.getSdkBalloonHelper(); + balloonHelper.showBalloon(project, title, message); } } @@ -48,6 +47,8 @@ public class ProjectOpenCloseListener implements ProjectManagerListener, Disposa */ @Override public void projectClosed(@NotNull Project project) { + // Ensure this isn't part of testing + if (ApplicationManager.getApplication().isUnitTestMode()) return; // Get the counting service ProjectCountingService projectCountingService = ServiceManager.getService(ProjectCountingService.class); // Was the count above the limit? @@ -56,64 +57,12 @@ public class ProjectOpenCloseListener implements ProjectManagerListener, Disposa projectCountingService.decrProjectCount(); // See if the total # of projects no longer violates the limit. if (!projectCountingService.projectLimitExceeded() && previouslyOverCount) { - System.out.println("\n\nCLOSED limit transition called for: " + project.toString()); // Transitioned to within the limit. - showBalloon("projectClosed", project, "\"" + project.getName() + "\" Has Been Closed", "The number of open projects is below the SDK plugin max_opened_projects limit."); + String title = String.format("\"%s\" Has Been Closed", project.getName()); + String message = MAX_OPEN_PROJ_DISCLAIM + "The number of open projects is below the SDK plugin max_opened_projects limit."; + SdkBalloonHelper balloonHelper = SdkBalloonHelper.getSdkBalloonHelper(); + balloonHelper.showBalloon(project, title, message); } } - /* - CLOSED limit transition called for: Project (name=testKotlin, containerState=DISPOSE_IN_PROGRESS, componentStore=/Users/jhake/Documents/source/scratch/testKotlin) - From: projectClosed - allProjects[2] = Project (name=conditional_operator_test, containerState=ACTIVE, componentStore=/Users/jhake/Documents/source/scratch/conditional_operator_test) , Open = true - From: projectClosed - allProjects[1] = Project (name=foobar, containerState=ACTIVE, componentStore=/Users/jhake/Documents/source/scratch/foobar) , Open = true - From: projectClosed - allProjects[0] = Project (name=SimpleTest, containerState=ACTIVE, componentStore=/Users/jhake/Documents/source/scratch/SimpleTest) , Open = true - */ - private void showBalloon(String caller, Project project, String title, String message) { - // Ensure the project is open. If not, use the next youngest one in the project list - ProjectManager projectManager = ProjectManager.getInstance(); - Project[] allProjects = projectManager.getOpenProjects(); - Project validProject = allProjects[allProjects.length - 1]; - if (validProject == null) { - return; - } - System.out.println(String.format("From: %s - validProject = %s", caller, validProject.toString())); - - // Verify the place to put the balloon - final WindowManager manager = WindowManager.getInstance(); - final JFrame frame = manager.getFrame(validProject); - JRootPane pane = frame.getRootPane(); - if (pane == null) { - return; - } - - // Construct and show the balloon - JLabel component = new JLabel(message); - final Balloon balloon = JBPopupFactory.getInstance().createBalloonBuilder(component) - .setShadow(true) - .setAnimationCycle(200) // Was 0 - .setHideOnClickOutside(true) - .setHideOnAction(false) - .setFillColor(UIUtil.getControlColor()) - .setTitle(title) // Added - .setFadeoutTime(5000) // Added - .createBalloon(); - Disposer.register(validProject, balloon); - balloon.showInCenterOf(pane); - -// final Balloon.Position position = QuickEditAction.getBalloonPosition(editor); -// RelativePoint point = JBPopupFactory.getInstance().guessBestPopupLocation(editor); -// if (position == Balloon.Position.above) { -// final Point p = point.getPoint(); -// point = new RelativePoint(point.getComponent(), new Point(p.x, p.y - editor.getLineHeight())); -// } - - } - - /** - * Usually not invoked directly, see Disposable class javadoc. - */ - @Override - public void dispose() { - // noop - } } diff --git a/max_opened_projects/src/main/java/org/intellij/sdk/utils/SdkBalloonHelper.java b/max_opened_projects/src/main/java/org/intellij/sdk/utils/SdkBalloonHelper.java new file mode 100644 index 000000000..1fdb423c0 --- /dev/null +++ b/max_opened_projects/src/main/java/org/intellij/sdk/utils/SdkBalloonHelper.java @@ -0,0 +1,104 @@ +// Copyright 2000-2020 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + +package org.intellij.sdk.utils; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.ui.popup.Balloon; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.wm.WindowManager; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +/** + * This is just a utility class to help communicate the state of this plugin using Popups + * + * @see Popup documentation + */ +public class SdkBalloonHelper { + + public static SdkBalloonHelper getSdkBalloonHelper() { + return new SdkBalloonHelper(); + } + + /** + * This method constructs a Balloon-type popup and displays it in the middle of a Project window. + * Adds the Balloon to the Project's Disposer + * + * @param project The Project to host the Balloon. + * @param title A short description of what the Balloon conveys + * @param message Detailed information + */ + public void showBalloon(@Nullable Project project, @NotNull String title, @NotNull String message) { + // Ensure the project is open. If not, use the next youngest one to show the balloon + Project displayProject = findValidProject(project); + if (displayProject == null) return; + + // Verify the place to put the balloon + JRootPane pane = getProjectRootPane(displayProject); + if (pane == null) return; + + // Construct the balloon + JLabel component = new JLabel("" + message + ""); + final Balloon balloon = JBPopupFactory.getInstance().createBalloonBuilder(component) + .setShadow(true) + .setHideOnClickOutside(true) + .setHideOnAction(false) + .setFillColor(UIUtil.getControlColor()) + .setTitle(title) + .setFadeoutTime(6000) + .createBalloon(); + + // Register it for disposal with the project that will display it + Disposer.register(displayProject, balloon); + + // Show the balloon in the middle of the project's window - it will disappear per the animation + balloon.showInCenterOf(pane); + } + + /** + * This function verifies that the provided Project is still open. + * If the Project is not open (closed, or in some state of disposal,) + * the function tries to find the next most-recently opened Project. + * + * @param dodgyProject The Project to be verified as open + * @return dodgyProject if it is verified as open, + * Or the last Project listed in ProjectManager's list of open Projects. + * Or null if no other Projects are open. (Edge case when IDE is closing.) + */ + @Nullable + private Project findValidProject(@Nullable Project dodgyProject) { + Project validProject = dodgyProject; + if ((validProject == null) || !validProject.isOpen()) { + // Find the next most-recently opened Project that is still open. + ProjectManager projectManager = ProjectManager.getInstance(); + Project[] allProjects = projectManager.getOpenProjects(); + validProject = allProjects.length > 0 ? allProjects[allProjects.length - 1] : null; + } + return validProject; + } + + /** + * This function gets the JRootPane for an open Project + * + * @param project The open Project + * @return A valid JRootPane for the Project + * Otherwise null + */ + @Nullable + private JRootPane getProjectRootPane(@Nullable Project project) { + JRootPane projectPane = null; + if ((project != null) && project.isOpen()) { + // Get the frame for the project, then the JRootPane + final WindowManager manager = WindowManager.getInstance(); + final JFrame frame = manager.getFrame(project); + projectPane = frame != null ? frame.getRootPane() : null; + } + return projectPane; + } + +}