diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/PostponableLogRefresher.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/PostponableLogRefresher.java new file mode 100644 index 000000000000..eb853fe4bf33 --- /dev/null +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/PostponableLogRefresher.java @@ -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 myRootsToRefresh = ContainerUtil.newHashSet(); + @NotNull private final Set 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 toRefresh = new HashSet(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 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; + } + } +} diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogManager.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogManager.java index 3085e848ab3d..b93d1493f58a 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogManager.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogManager.java @@ -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 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 logProviders, - @NotNull VcsLogTabsRefresher refresher) { + @NotNull VcsLogRefresher refresher, + @NotNull Disposable disposableParent) { MultiMap providers2roots = MultiMap.create(); for (Map.Entry entry : logProviders.entrySet()) { providers2roots.putValue(entry.getValue(), entry.getKey()); @@ -157,7 +136,7 @@ public class VcsLogManager implements Disposable { for (Map.Entry> 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; } diff --git a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogTabsRefresher.java b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogTabsRefresher.java index f8d075b7a511..5e6ed14e3578 100644 --- a/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogTabsRefresher.java +++ b/platform/vcs-log/impl/src/com/intellij/vcs/log/impl/VcsLogTabsRefresher.java @@ -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 myTabToFiltererMap = ContainerUtil.newHashMap(); - @NotNull private final Set 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 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 toRefresh = new HashSet(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 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() { + @Override + public boolean value(VcsLogWindow window) { + return window instanceof VcsLogTab && ((VcsLogTab)window).myTabName.equals(tabName); + } + }); + if (logWindow != null) { + myRefresher.filtererActivated(logWindow.getFilterer(), false); } }