[vcs-log] extract PostponableLogRefresher from VcsLogTabsRefresher that does not know about tabs; incapsulate filterer + tab name via VcsLogWindow

This commit is contained in:
Julia Beliaeva
2016-05-05 19:02:44 +03:00
parent a148915ece
commit 72f5825a84
3 changed files with 193 additions and 136 deletions
@@ -0,0 +1,142 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.vcs.log.impl;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.vcs.log.VcsLogRefresher;
import com.intellij.vcs.log.data.DataPack;
import com.intellij.vcs.log.data.DataPackChangeListener;
import com.intellij.vcs.log.data.VcsLogDataManager;
import com.intellij.vcs.log.data.VcsLogFilterer;
import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class PostponableLogRefresher implements VcsLogRefresher {
@NotNull protected final VcsLogDataManager myDataManager;
@NotNull private final Set<VirtualFile> myRootsToRefresh = ContainerUtil.newHashSet();
@NotNull private final Set<VcsLogWindow> myLogWindows = ContainerUtil.newHashSet();
public PostponableLogRefresher(@NotNull VcsLogDataManager dataManager) {
myDataManager = dataManager;
myDataManager.addDataPackChangeListener(new DataPackChangeListener() {
@Override
public void onDataPackChange(@NotNull DataPack dataPack) {
for (VcsLogWindow window : myLogWindows) {
dataPackArrived(window.getFilterer(), window.isVisible());
}
}
});
}
@NotNull
public Disposable addLogWindow(@NotNull VcsLogWindow window) {
myLogWindows.add(window);
filtererActivated(window.getFilterer(), true);
return new Disposable() {
@Override
public void dispose() {
myLogWindows.remove(window);
}
};
}
@NotNull
public Disposable addLogWindow(@NotNull VcsLogFilterer filterer) {
return addLogWindow(new VcsLogWindow(filterer));
}
private static boolean keepUpToDate() {
return Registry.is("vcs.log.keep.up.to.date");
}
protected boolean canRefreshNow() {
if (keepUpToDate()) return true;
for (VcsLogWindow window : myLogWindows) {
if (window.isVisible()) return true;
}
return false;
}
public void filtererActivated(@NotNull VcsLogFilterer filterer, boolean firstTime) {
if (!myRootsToRefresh.isEmpty()) {
refreshPostponedRoots();
}
else {
if (!filterer.isValid() || firstTime) {
filterer.onRefresh();
}
}
}
private static void dataPackArrived(@NotNull VcsLogFilterer filterer, boolean canRefreshNow) {
if (canRefreshNow) {
filterer.onRefresh();
}
else {
filterer.invalidate();
}
}
public void refresh(@NotNull final VirtualFile root) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
if (canRefreshNow()) {
myDataManager.refresh(Collections.singleton(root));
}
else {
myRootsToRefresh.add(root);
}
}
}, ModalityState.any());
}
protected void refreshPostponedRoots() {
Set<VirtualFile> toRefresh = new HashSet<VirtualFile>(myRootsToRefresh);
myRootsToRefresh.removeAll(toRefresh); // clear the set, but keep roots which could possibly arrive after collecting them in the var.
myDataManager.refresh(toRefresh);
}
@NotNull
public Set<VcsLogWindow> getLogWindows() {
return myLogWindows;
}
public static class VcsLogWindow {
@NotNull private final VcsLogFilterer myFilterer;
public VcsLogWindow(@NotNull VcsLogFilterer filterer) {
myFilterer = filterer;
}
@NotNull
public VcsLogFilterer getFilterer() {
return myFilterer;
}
public boolean isVisible() {
return true;
}
}
}
@@ -33,7 +33,11 @@ import com.intellij.util.Consumer;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import com.intellij.vcs.log.VcsLogProvider;
import com.intellij.vcs.log.data.*;
import com.intellij.vcs.log.VcsLogRefresher;
import com.intellij.vcs.log.data.VcsLogDataManager;
import com.intellij.vcs.log.data.VcsLogFiltererImpl;
import com.intellij.vcs.log.data.VcsLogTabsProperties;
import com.intellij.vcs.log.data.VcsLogUiProperties;
import com.intellij.vcs.log.graph.PermanentGraph;
import com.intellij.vcs.log.ui.VcsLogColorManagerImpl;
import com.intellij.vcs.log.ui.VcsLogUiImpl;
@@ -59,6 +63,7 @@ public class VcsLogManager implements Disposable {
private VcsLogDataManager myDataManager;
private VcsLogColorManagerImpl myColorManager;
private VcsLogTabsRefresher myTabsLogRefresher;
private PostponableLogRefresher myPostponableRefresher;
public VcsLogManager(@NotNull Project project, @NotNull VcsLogTabsProperties uiProperties) {
myProject = project;
@@ -74,40 +79,12 @@ public class VcsLogManager implements Disposable {
return Arrays.asList(ProjectLevelVcsManager.getInstance(myProject).getAllVcsRoots());
}
public void watchTab(@NotNull final String contentTabName, @NotNull VcsLogUiImpl logUi) {
myTabsLogRefresher.addTabToWatch(contentTabName, logUi.getFilterer());
Disposer.register(logUi, new Disposable() {
@Override
public void dispose() {
unwatchTab(contentTabName);
}
});
public void watchTab(@NotNull String contentTabName, @NotNull VcsLogUiImpl logUi) {
Disposer.register(logUi, myTabsLogRefresher.addTabToWatch(contentTabName, logUi.getFilterer()));
}
public void unwatchTab(@NotNull String contentTabName) {
if (myTabsLogRefresher != null) myTabsLogRefresher.removeTabFromWatch(contentTabName);
}
private void watch(@NotNull final VcsLogUiImpl ui) {
final DataPackChangeListener listener = new DataPackChangeListener() {
@Override
public void onDataPackChange(@NotNull DataPack dataPack) {
ui.getFilterer().onRefresh();
}
};
myDataManager.addDataPackChangeListener(listener);
Disposer.register(ui, new Disposable() {
@Override
public void dispose() {
myDataManager.removeDataPackChangeListener(listener);
}
});
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
ui.getFilterer().onRefresh();
}
});
private void watch(@NotNull VcsLogUiImpl ui) {
Disposer.register(ui, myPostponableRefresher.addLogWindow(ui.getFilterer()));
}
@NotNull
@@ -138,9 +115,10 @@ public class VcsLogManager implements Disposable {
Map<VirtualFile, VcsLogProvider> logProviders = findLogProviders(getVcsRoots(), myProject);
myDataManager = new VcsLogDataManager(myProject, logProviders, new MyFatalErrorsConsumer());
myTabsLogRefresher = new VcsLogTabsRefresher(myProject, myDataManager);
myPostponableRefresher = new PostponableLogRefresher(myDataManager);
myTabsLogRefresher = new VcsLogTabsRefresher(myProject, myPostponableRefresher, myDataManager);
refreshLogOnVcsEvents(logProviders, myTabsLogRefresher);
refreshLogOnVcsEvents(logProviders, myPostponableRefresher, myDataManager);
myColorManager = new VcsLogColorManagerImpl(logProviders.keySet());
@@ -149,7 +127,8 @@ public class VcsLogManager implements Disposable {
}
private static void refreshLogOnVcsEvents(@NotNull Map<VirtualFile, VcsLogProvider> logProviders,
@NotNull VcsLogTabsRefresher refresher) {
@NotNull VcsLogRefresher refresher,
@NotNull Disposable disposableParent) {
MultiMap<VcsLogProvider, VirtualFile> providers2roots = MultiMap.create();
for (Map.Entry<VirtualFile, VcsLogProvider> entry : logProviders.entrySet()) {
providers2roots.putValue(entry.getValue(), entry.getKey());
@@ -157,7 +136,7 @@ public class VcsLogManager implements Disposable {
for (Map.Entry<VcsLogProvider, Collection<VirtualFile>> entry : providers2roots.entrySet()) {
Disposable disposable = entry.getKey().subscribeToRootRefreshEvents(entry.getValue(), refresher);
Disposer.register(refresher, disposable);
Disposer.register(disposableParent, disposable);
}
}
@@ -200,6 +179,7 @@ public class VcsLogManager implements Disposable {
myDataManager = null;
myTabsLogRefresher = null;
myPostponableRefresher = null;
myColorManager = null;
myUi = null;
}
@@ -16,13 +16,10 @@
package com.intellij.vcs.log.impl;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.registry.Registry;
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.ToolWindowManagerListener;
import com.intellij.openapi.wm.impl.ToolWindowImpl;
@@ -33,111 +30,33 @@ import com.intellij.ui.content.ContentManagerEvent;
import com.intellij.ui.content.TabbedContent;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.vcs.log.VcsLogRefresher;
import com.intellij.vcs.log.data.DataPack;
import com.intellij.vcs.log.data.DataPackChangeListener;
import com.intellij.vcs.log.data.VcsLogDataManager;
import com.intellij.vcs.log.data.VcsLogFilterer;
import com.intellij.vcs.log.impl.PostponableLogRefresher.VcsLogWindow;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class VcsLogTabsRefresher implements VcsLogRefresher, Disposable {
public class VcsLogTabsRefresher implements Disposable {
private static final String TOOLWINDOW_ID = ChangesViewContentManager.TOOLWINDOW_ID;
@NotNull private final VcsLogDataManager myDataManager;
@NotNull private final PostponableLogRefresher myRefresher;
@NotNull private final ToolWindowManagerImpl myToolWindowManager;
@NotNull private final ToolWindowImpl myToolWindow;
@NotNull private final MyRefreshPostponedEventsListener myPostponedEventsListener;
@NotNull private final Map<String, VcsLogFilterer> myTabToFiltererMap = ContainerUtil.newHashMap();
@NotNull private final Set<VirtualFile> myRootsToRefresh = ContainerUtil.newHashSet();
public VcsLogTabsRefresher(@NotNull Project project, @NotNull VcsLogDataManager dataManager) {
myDataManager = dataManager;
public VcsLogTabsRefresher(@NotNull Project project, @NotNull PostponableLogRefresher refresher, @NotNull Disposable parentDisposable) {
myRefresher = refresher;
myToolWindowManager = (ToolWindowManagerImpl)ToolWindowManager.getInstance(project);
myToolWindow = ObjectUtils.assertNotNull((ToolWindowImpl)myToolWindowManager.getToolWindow(TOOLWINDOW_ID));
Disposer.register(dataManager, this);
myPostponedEventsListener = new MyRefreshPostponedEventsListener();
myToolWindow.getContentManager().addContentManagerListener(myPostponedEventsListener);
myToolWindowManager.addToolWindowManagerListener(myPostponedEventsListener);
myDataManager.addDataPackChangeListener(new DataPackChangeListener() {
@Override
public void onDataPackChange(@NotNull DataPack dataPack) {
for (Map.Entry<String, VcsLogFilterer> tabAndFilterer : myTabToFiltererMap.entrySet()) {
dataPackArrived(tabAndFilterer.getValue(), isTabVisible(tabAndFilterer.getKey()));
}
}
});
}
private void tabActivated(@NotNull VcsLogFilterer filterer, boolean firstTime) {
if (!myRootsToRefresh.isEmpty()) {
refreshPostponedRoots();
}
else {
if (!filterer.isValid() || firstTime) {
filterer.onRefresh();
}
}
}
private static void dataPackArrived(@NotNull VcsLogFilterer filterer, boolean visible) {
if (visible) {
filterer.onRefresh();
}
else {
filterer.invalidate();
}
}
@Override
public void refresh(@NotNull final VirtualFile root) {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
if (canRefreshNow()) {
myDataManager.refresh(Collections.singleton(root));
}
else {
myRootsToRefresh.add(root);
}
}
}, ModalityState.any());
}
private void refreshPostponedRoots() {
Set<VirtualFile> toRefresh = new HashSet<VirtualFile>(myRootsToRefresh);
myRootsToRefresh.removeAll(toRefresh); // clear the set, but keep roots which could possibly arrive after collecting them in the var.
myDataManager.refresh(toRefresh);
}
private static boolean keepUpToDate() {
return Registry.is("vcs.log.keep.up.to.date");
}
private boolean canRefreshNow() {
return isOneOfTabsVisible(myTabToFiltererMap.keySet()) || keepUpToDate();
}
protected boolean isTabVisible(@NotNull String tab) {
return isOneOfTabsVisible(Collections.singleton(tab));
}
private boolean isOneOfTabsVisible(@NotNull Set<String> tabs) {
if (tabs.isEmpty()) return true;
// probably, each log ui should determine where it is located (not necessarily in Log window tool tabs) and when it is visible
String selectedTab = getSelectedTabName();
return selectedTab != null && tabs.contains(selectedTab);
Disposer.register(parentDisposable, this);
}
@Nullable
@@ -151,13 +70,9 @@ public class VcsLogTabsRefresher implements VcsLogRefresher, Disposable {
return null;
}
public void addTabToWatch(@NotNull String contentTabName, @NotNull VcsLogFilterer filterer) {
myTabToFiltererMap.put(contentTabName, filterer);
tabActivated(filterer, true);
}
public void removeTabFromWatch(@NotNull String contentTabName) {
myTabToFiltererMap.remove(contentTabName);
@NotNull
public Disposable addTabToWatch(@NotNull String contentTabName, @NotNull VcsLogFilterer filterer) {
return myRefresher.addLogWindow(new VcsLogTab(filterer, contentTabName));
}
@Override
@@ -172,6 +87,21 @@ public class VcsLogTabsRefresher implements VcsLogRefresher, Disposable {
}
}
public class VcsLogTab extends PostponableLogRefresher.VcsLogWindow {
@NotNull private final String myTabName;
public VcsLogTab(@NotNull VcsLogFilterer filterer, @NotNull String tabName) {
super(filterer);
myTabName = tabName;
}
@Override
public boolean isVisible() {
String selectedTab = getSelectedTabName();
return selectedTab != null && myTabName.equals(selectedTab);
}
}
private class MyRefreshPostponedEventsListener extends ContentManagerAdapter
implements ToolWindowManagerListener, PropertyChangeListener {
@@ -183,9 +113,14 @@ public class VcsLogTabsRefresher implements VcsLogRefresher, Disposable {
}
private void selectionChanged(String tabName) {
VcsLogFilterer filterer = myTabToFiltererMap.get(tabName);
if (filterer != null) {
tabActivated(filterer, false);
VcsLogWindow logWindow = ContainerUtil.find(myRefresher.getLogWindows(), new Condition<VcsLogWindow>() {
@Override
public boolean value(VcsLogWindow window) {
return window instanceof VcsLogTab && ((VcsLogTab)window).myTabName.equals(tabName);
}
});
if (logWindow != null) {
myRefresher.filtererActivated(logWindow.getFilterer(), false);
}
}