blob: 77401fdb32f5b8fb42405ac0c5b91c257bcf2e19 [file] [log] [blame]
package com.intellij.vcs.log.impl;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.extensions.ExtensionPointName;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.openapi.wm.ex.ToolWindowManagerListener;
import com.intellij.openapi.wm.impl.ToolWindowImpl;
import com.intellij.openapi.wm.impl.ToolWindowManagerImpl;
import com.intellij.ui.content.Content;
import com.intellij.ui.content.ContentManagerAdapter;
import com.intellij.ui.content.ContentManagerEvent;
import com.intellij.util.Consumer;
import com.intellij.util.containers.ConcurrentHashSet;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.ui.UIUtil;
import com.intellij.vcs.log.VcsLogProvider;
import com.intellij.vcs.log.VcsLogRefresher;
import com.intellij.vcs.log.VcsLogSettings;
import com.intellij.vcs.log.data.*;
import com.intellij.vcs.log.ui.VcsLogColorManagerImpl;
import com.intellij.vcs.log.ui.VcsLogUiImpl;
import com.intellij.vcs.log.ui.frame.VcsLogGraphTable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
/**
* @author Kirill Likhodedov
*/
public class VcsLogManager implements Disposable {
public static final ExtensionPointName<VcsLogProvider> LOG_PROVIDER_EP = ExtensionPointName.create("com.intellij.logProvider");
@NotNull private final Project myProject;
@NotNull private final ProjectLevelVcsManager myVcsManager;
@NotNull private final VcsLogSettings mySettings;
@NotNull private final VcsLogUiProperties myUiProperties;
private PostponeableLogRefresher myLogRefresher;
private volatile VcsLogUiImpl myUi;
public VcsLogManager(@NotNull Project project, @NotNull ProjectLevelVcsManager vcsManager,
@NotNull VcsLogSettings settings,
@NotNull VcsLogUiProperties uiProperties) {
myProject = project;
myVcsManager = vcsManager;
mySettings = settings;
myUiProperties = uiProperties;
Disposer.register(myProject, this);
}
@NotNull
public JComponent initContent() {
final Map<VirtualFile, VcsLogProvider> logProviders = findLogProviders();
Consumer<DataPack> dataPackUpdateHandler = new Consumer<DataPack>() {
@Override
public void consume(final DataPack dataPack) {
UIUtil.invokeLaterIfNeeded(new Runnable() {
@Override
public void run() {
if (!Disposer.isDisposed(myUi)) {
myUi.setDataPack(dataPack);
myProject.getMessageBus().syncPublisher(VcsLogDataHolder.REFRESH_COMPLETED).refresh(dataPack);
}
}
});
}
};
VcsLogDataHolder logDataHolder = new VcsLogDataHolder(myProject, this, logProviders, mySettings, dataPackUpdateHandler);
myUi = new VcsLogUiImpl(logDataHolder, myProject, mySettings,
new VcsLogColorManagerImpl(logProviders.keySet()), myUiProperties, EmptyDataPack.getInstance());
myLogRefresher = new PostponeableLogRefresher(myProject, logDataHolder);
refreshLogOnVcsEvents(logProviders);
logDataHolder.initialize();
// todo fix selection
final VcsLogGraphTable graphTable = myUi.getTable();
if (graphTable.getRowCount() > 0) {
IdeFocusManager.getInstance(myProject).requestFocus(graphTable, true).doWhenProcessed(new Runnable() {
@Override
public void run() {
graphTable.setRowSelectionInterval(0, 0);
}
});
}
return myUi.getMainFrame().getMainComponent();
}
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(), myLogRefresher);
}
}
@NotNull
public Map<VirtualFile, VcsLogProvider> findLogProviders() {
Map<VirtualFile, VcsLogProvider> logProviders = ContainerUtil.newHashMap();
VcsLogProvider[] allLogProviders = Extensions.getExtensions(LOG_PROVIDER_EP, myProject);
for (AbstractVcs vcs : myVcsManager.getAllActiveVcss()) {
for (VcsLogProvider provider : allLogProviders) {
if (provider.getSupportedVcs().equals(vcs.getKeyInstanceMethod())) {
for (VirtualFile root : myVcsManager.getRootsUnderVcs(vcs)) {
logProviders.put(root, provider);
}
break;
}
}
}
return logProviders;
}
/**
* The instance of the {@link com.intellij.vcs.log.ui.VcsLogUiImpl} or null if the log was not initialized yet.
*/
@Nullable
public VcsLogUiImpl getLogUi() {
return myUi;
}
@Override
public void dispose() {
}
private static class PostponeableLogRefresher implements VcsLogRefresher, Disposable {
private static final String TOOLWINDOW_ID = ChangesViewContentManager.TOOLWINDOW_ID;
@NotNull private final VcsLogDataHolder myDataHolder;
@NotNull private final ToolWindowManagerImpl myToolWindowManager;
@NotNull private final ToolWindowImpl myToolWindow;
@NotNull private final MyRefreshPostponedEventsListener myPostponedEventsListener;
@NotNull private final Set<VirtualFile> myRootsToRefresh = new ConcurrentHashSet<VirtualFile>();
public PostponeableLogRefresher(@NotNull Project project, @NotNull VcsLogDataHolder dataHolder) {
myDataHolder = dataHolder;
myToolWindowManager = (ToolWindowManagerImpl)ToolWindowManager.getInstance(project);
myToolWindow = (ToolWindowImpl)myToolWindowManager.getToolWindow(TOOLWINDOW_ID);
Disposer.register(myToolWindow.getContentManager(), this);
myPostponedEventsListener = new MyRefreshPostponedEventsListener();
myToolWindow.getContentManager().addContentManagerListener(myPostponedEventsListener);
myToolWindowManager.addToolWindowManagerListener(myPostponedEventsListener);
}
@Override
public void refresh(@NotNull VirtualFile root) {
if (isOurContentPaneShowing()) {
myDataHolder.refresh(Collections.singleton(root));
}
else {
myRootsToRefresh.add(root);
}
}
@Override
public void dispose() {
myToolWindow.getContentManager().removeContentManagerListener(myPostponedEventsListener);
myToolWindowManager.removeToolWindowManagerListener(myPostponedEventsListener);
}
private boolean isOurContentPaneShowing() {
if (myToolWindowManager.isToolWindowRegistered(TOOLWINDOW_ID) && myToolWindow.isVisible()) {
Content content = myToolWindow.getContentManager().getSelectedContent();
return content != null && content.getTabName().equals(VcsLogContentProvider.TAB_NAME);
}
return false;
}
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.
myDataHolder.refresh(toRefresh);
}
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();
}
}
}
}
}