From 8f1ea0fd7c40654bc4e65819bb643bc5d5cc8ffd Mon Sep 17 00:00:00 2001 From: Kirill Likhodedov Date: Mon, 23 Sep 2013 20:03:56 +0400 Subject: [PATCH] [log] Don't refresh the log when it is hidden * Wait for the ChangesViewContentManager to fully initialize before initializing the VcsLogManager: to make sure that the toolwindow's content manager is available. * When log refresh is requested, check if the toolwindow is visible, and the Log content pane is selected. If yes, send the refresh request to the log. Otherwise remember which root needs to be refreshed. When the content pane becomes visible, refresh all roots which need to be refreshed. --- .../com/intellij/dvcs/log/VcsLogManager.java | 185 ++++++++++++++---- .../changes/ui/ChangesViewContentManager.java | 20 ++ 2 files changed, 167 insertions(+), 38 deletions(-) diff --git a/platform/dvcs/src/com/intellij/dvcs/log/VcsLogManager.java b/platform/dvcs/src/com/intellij/dvcs/log/VcsLogManager.java index 0c2ffdf19646..f8dc1f51eb9d 100644 --- a/platform/dvcs/src/com/intellij/dvcs/log/VcsLogManager.java +++ b/platform/dvcs/src/com/intellij/dvcs/log/VcsLogManager.java @@ -1,12 +1,12 @@ package com.intellij.dvcs.log; import com.intellij.openapi.Disposable; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.AbstractProjectComponent; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.DumbAwareRunnable; import com.intellij.openapi.project.Project; -import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.vcs.AbstractVcs; @@ -14,8 +14,14 @@ import com.intellij.openapi.vcs.ProjectLevelVcsManager; import com.intellij.openapi.vcs.changes.ui.ChangesViewContentI; import com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.openapi.wm.ex.ToolWindowManagerEx; +import com.intellij.openapi.wm.ex.ToolWindowManagerListener; +import com.intellij.openapi.wm.impl.ToolWindowImpl; import com.intellij.ui.components.JBLoadingPanel; import com.intellij.ui.content.Content; +import com.intellij.ui.content.ContentManagerAdapter; +import com.intellij.ui.content.ContentManagerEvent; import com.intellij.ui.content.impl.ContentImpl; import com.intellij.util.Consumer; import com.intellij.util.containers.ContainerUtil; @@ -31,6 +37,7 @@ import javax.swing.*; import java.awt.*; import java.util.Collection; import java.util.Map; +import java.util.Set; /** * @author Kirill Likhodedov @@ -40,6 +47,7 @@ public class VcsLogManager extends AbstractProjectComponent { public static final ExtensionPointName LOG_PROVIDER_EP = ExtensionPointName.create("com.intellij.logProvider"); @NotNull private final ProjectLevelVcsManager myVcsManager; + private PostponeableLogRefresher myLogRefresher; protected VcsLogManager(@NotNull Project project, @NotNull ProjectLevelVcsManager vcsManagerInitializedFirst) { super(project); @@ -50,57 +58,59 @@ public class VcsLogManager extends AbstractProjectComponent { public void initComponent() { super.initComponent(); - StartupManager.getInstance(myProject).registerPostStartupActivity(new DumbAwareRunnable() { - @Override + if (!Registry.is("git.new.log")) { + return; + } + + ApplicationManager.getApplication().executeOnPooledThread(new Runnable() { public void run() { - if (!Registry.is("git.new.log")) { - return; - } - - final Map logProviders = findLogProviders(); - if (logProviders.isEmpty()) { - return; - } - - final VcsLogContainer mainPanel = new VcsLogContainer(myProject); - Content vcsLogContentPane = new ContentImpl(mainPanel, "Log", true); - ChangesViewContentI changesView = ChangesViewContentManager.getInstance(myProject); - changesView.addContent(vcsLogContentPane); - vcsLogContentPane.setCloseable(false); - - VcsLogDataHolder.init(myProject, logProviders, new Consumer() { + ChangesViewContentManager changesView = (ChangesViewContentManager)ChangesViewContentManager.getInstance(myProject); + changesView.executeWhenInitialized(new DumbAwareRunnable() { @Override - public void consume(VcsLogDataHolder vcsLogDataHolder) { - Disposer.register(myProject, vcsLogDataHolder); - VcsLogUI logUI = new VcsLogUI(vcsLogDataHolder, myProject, new VcsLogColorManagerImpl(logProviders.keySet())); - mainPanel.init(logUI.getMainFrame().getMainComponent()); - refreshLogOnVcsEvents(vcsLogDataHolder, logProviders); + public void run() { + final Map logProviders = findLogProviders(); + if (logProviders.isEmpty()) { + return; + } + + final VcsLogContainer mainPanel = new VcsLogContainer(myProject); + final Content content = new ContentImpl(mainPanel, "Log", true); + ChangesViewContentI changesView = ChangesViewContentManager.getInstance(myProject); + changesView.addContent(content); + content.setCloseable(false); + + VcsLogDataHolder.init(myProject, logProviders, new Consumer() { + @Override + public void consume(VcsLogDataHolder vcsLogDataHolder) { + Disposer.register(myProject, vcsLogDataHolder); + VcsLogUI logUI = new VcsLogUI(vcsLogDataHolder, myProject, new VcsLogColorManagerImpl(logProviders.keySet())); + mainPanel.init(logUI.getMainFrame().getMainComponent()); + myLogRefresher = new PostponeableLogRefresher(myProject, vcsLogDataHolder, content); + refreshLogOnVcsEvents(logProviders); + } + }); } }); - } }); } - private static void refreshLogOnVcsEvents(@NotNull final VcsLogDataHolder vcsLogDataHolder, - @NotNull Map logProviders) { + @Override + public void disposeComponent() { + if (myLogRefresher != null) { + myLogRefresher.dispose(); + } + super.disposeComponent(); + } + + private void refreshLogOnVcsEvents(@NotNull Map logProviders) { MultiMap providers2roots = MultiMap.create(); for (Map.Entry entry : logProviders.entrySet()) { providers2roots.putValue(entry.getValue(), entry.getKey()); } for (Map.Entry> entry : providers2roots.entrySet()) { - entry.getKey().subscribeToRootRefreshEvents(entry.getValue(), new VcsLogRefresher() { - @Override - public void refresh(@NotNull VirtualFile root) { - vcsLogDataHolder.refresh(root); - } - - @Override - public void refreshRefs(@NotNull VirtualFile root) { - vcsLogDataHolder.refreshRefs(root); - } - }); + entry.getKey().subscribeToRootRefreshEvents(entry.getValue(), myLogRefresher); } } @@ -138,4 +148,103 @@ public class VcsLogManager extends AbstractProjectComponent { } } + private static class PostponeableLogRefresher implements VcsLogRefresher { + + @NotNull private final VcsLogDataHolder myDataHolder; + @NotNull private final Content myContentPane; + @NotNull private final ToolWindowManagerEx myToolWindowManager; + @NotNull private final ToolWindowImpl myToolWindow; + @NotNull private final MyRefreshPostponedEventsListener myPostponedEventsListener; + + @NotNull private final Set myRootsToRefreshRefs = ContainerUtil.newHashSet(); + @NotNull private final Set myRootsToRefresh = ContainerUtil.newHashSet(); + @NotNull private final Object REFRESH_LOCK = new Object(); + + public PostponeableLogRefresher(@NotNull Project project, @NotNull VcsLogDataHolder dataHolder, @NotNull Content contentPane) { + myDataHolder = dataHolder; + myContentPane = contentPane; + myToolWindowManager = ((ToolWindowManagerEx)ToolWindowManager.getInstance(project)); + myToolWindow = (ToolWindowImpl)myToolWindowManager.getToolWindow(ChangesViewContentManager.TOOLWINDOW_ID); + + myPostponedEventsListener = new MyRefreshPostponedEventsListener(); + myToolWindow.getContentManager().addContentManagerListener(myPostponedEventsListener); + myToolWindowManager.addToolWindowManagerListener(myPostponedEventsListener); + } + + @Override + public void refresh(@NotNull VirtualFile root) { + if (isOurContentPaneShowing()) { + myDataHolder.refresh(root); + } + else { + synchronized (REFRESH_LOCK) { + myRootsToRefresh.add(root); + } + } + } + + @Override + public void refreshRefs(@NotNull VirtualFile root) { + if (isOurContentPaneShowing()) { + myDataHolder.refreshRefs(root); + } + else { + synchronized (REFRESH_LOCK) { + myRootsToRefreshRefs.add(root); + } + } + } + + void dispose() { + myToolWindow.getContentManager().removeContentManagerListener(myPostponedEventsListener); + myToolWindowManager.removeToolWindowManagerListener(myPostponedEventsListener); + } + + private boolean isOurContentPaneShowing() { + return myToolWindow.isVisible() && myContentPane.equals(myToolWindow.getContentManager().getSelectedContent()); + } + + private void refreshPostponedRoots() { + for (VirtualFile root : safeGetAndClear(myRootsToRefresh)) { + myDataHolder.refresh(root); // TODO support batch root refresh + } + for (VirtualFile root : safeGetAndClear(myRootsToRefreshRefs)) { + myDataHolder.refreshRefs(root); // TODO support batch root refresh + } + } + + @NotNull + private Set safeGetAndClear(@NotNull Set unsafeRefs) { + Set safeRefs = ContainerUtil.newHashSet(); + synchronized (REFRESH_LOCK) { + safeRefs.addAll(safeRefs); + unsafeRefs.clear(); + } + return safeRefs; + } + + private class MyRefreshPostponedEventsListener extends ContentManagerAdapter implements ToolWindowManagerListener { + + @Override + public void selectionChanged(ContentManagerEvent event) { + refreshRootsIfNeeded(); + } + + @Override + public void stateChanged() { + refreshRootsIfNeeded(); + } + + @Override + public void toolWindowRegistered(@NotNull String id) { + } + + private void refreshRootsIfNeeded() { + if (isOurContentPaneShowing()) { + refreshPostponedRoots(); + } + } + } + } + } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesViewContentManager.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesViewContentManager.java index 09eeca37fb6c..297408c3ec24 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesViewContentManager.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesViewContentManager.java @@ -22,6 +22,7 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.components.AbstractProjectComponent; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.DumbAwareRunnable; import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.StartupManager; @@ -44,6 +45,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.*; +import java.util.concurrent.CountDownLatch; /** * @author yole @@ -51,6 +53,8 @@ import java.util.*; public class ChangesViewContentManager extends AbstractProjectComponent implements ChangesViewContentI { public static final String TOOLWINDOW_ID = VcsBundle.message("changes.toolwindow.name"); private static final Key myEPKey = Key.create("ChangesViewContentEP"); + private static final Logger LOG = Logger.getInstance(ChangesViewContentManager.class); + private MyContentManagerListener myContentManagerListener; private final ProjectLevelVcsManager myVcsManager; @@ -63,6 +67,7 @@ public class ChangesViewContentManager extends AbstractProjectComponent implemen private final VcsListener myVcsListener = new MyVcsListener(); private final Alarm myVcsChangeAlarm; private final List myAddedContents = new ArrayList(); + @NotNull private final CountDownLatch myInitializationWaiter = new CountDownLatch(1); public ChangesViewContentManager(final Project project, final ProjectLevelVcsManager vcsManager) { super(project); @@ -103,11 +108,26 @@ public class ChangesViewContentManager extends AbstractProjectComponent implemen if (contentManager.getContentCount() > 0) { contentManager.setSelectedContent(contentManager.getContent(0)); } + myInitializationWaiter.countDown(); } } }); } + /** + * Makes the current thread wait until the ChangesViewContentManager is initialized. + * When it initializes, executes the given runnable. + */ + public void executeWhenInitialized(@NotNull final Runnable runnable) { + try { + myInitializationWaiter.await(); + runnable.run(); + } + catch (InterruptedException e) { + LOG.error(e); + } + } + private void loadExtensionTabs() { final List contentList = new LinkedList(); final ChangesViewContentEP[] contentEPs = myProject.getExtensions(ChangesViewContentEP.EP_NAME);