[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.
This commit is contained in:
Kirill Likhodedov
2013-09-25 18:29:59 +04:00
parent 3067539d85
commit 8f1ea0fd7c
2 changed files with 167 additions and 38 deletions
@@ -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<VcsLogProvider> 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<VirtualFile, VcsLogProvider> 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<VcsLogDataHolder>() {
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<VirtualFile, VcsLogProvider> 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<VcsLogDataHolder>() {
@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<VirtualFile, VcsLogProvider> logProviders) {
@Override
public void disposeComponent() {
if (myLogRefresher != null) {
myLogRefresher.dispose();
}
super.disposeComponent();
}
private void refreshLogOnVcsEvents(@NotNull Map<VirtualFile, VcsLogProvider> logProviders) {
MultiMap<VcsLogProvider, VirtualFile> providers2roots = MultiMap.create();
for (Map.Entry<VirtualFile, VcsLogProvider> entry : logProviders.entrySet()) {
providers2roots.putValue(entry.getValue(), entry.getKey());
}
for (Map.Entry<VcsLogProvider, Collection<VirtualFile>> 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<VirtualFile> myRootsToRefreshRefs = ContainerUtil.newHashSet();
@NotNull private final Set<VirtualFile> 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<VirtualFile> safeGetAndClear(@NotNull Set<VirtualFile> unsafeRefs) {
Set<VirtualFile> 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();
}
}
}
}
}
@@ -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<ChangesViewContentEP> 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<Content> myAddedContents = new ArrayList<Content>();
@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<Content> contentList = new LinkedList<Content>();
final ChangesViewContentEP[] contentEPs = myProject.getExtensions(ChangesViewContentEP.EP_NAME);