blob: 297408c3ec24fa026263f53cd62131276b11b21f [file] [log] [blame]
/*
* Copyright 2000-2009 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.openapi.vcs.changes.ui;
import com.intellij.icons.AllIcons;
import com.intellij.lifecycle.PeriodicalTasksCloser;
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;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.VcsBundle;
import com.intellij.openapi.vcs.VcsListener;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowAnchor;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.ui.content.*;
import com.intellij.util.Alarm;
import com.intellij.util.NotNullFunction;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
import java.util.concurrent.CountDownLatch;
/**
* @author yole
*/
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;
public static ChangesViewContentI getInstance(Project project) {
return PeriodicalTasksCloser.getInstance().safeGetComponent(project, ChangesViewContentI.class);
}
private ContentManager myContentManager;
private ToolWindow myToolWindow;
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);
myVcsManager = vcsManager;
myVcsChangeAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, project);
}
public void projectOpened() {
if (ApplicationManager.getApplication().isHeadlessEnvironment()) return;
StartupManager.getInstance(myProject).registerPostStartupActivity(new DumbAwareRunnable() {
public void run() {
final ToolWindowManager toolWindowManager = ToolWindowManager.getInstance(myProject);
if (toolWindowManager != null) {
myToolWindow = toolWindowManager.registerToolWindow(TOOLWINDOW_ID, true, ToolWindowAnchor.BOTTOM, myProject, true);
myToolWindow.setIcon(AllIcons.Toolwindows.ToolWindowChanges);
updateToolWindowAvailability();
final ContentManager contentManager = myToolWindow.getContentManager();
myContentManagerListener = new MyContentManagerListener();
contentManager.addContentManagerListener(myContentManagerListener);
myVcsManager.addVcsListener(myVcsListener);
Disposer.register(myProject, new Disposable(){
public void dispose() {
contentManager.removeContentManagerListener(myContentManagerListener);
myVcsManager.removeVcsListener(myVcsListener);
}
});
loadExtensionTabs();
myContentManager = contentManager;
final List<Content> ordered = doPresetOrdering(myAddedContents);
for(Content content: ordered) {
myContentManager.addContent(content);
}
myAddedContents.clear();
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);
for(ChangesViewContentEP ep: contentEPs) {
final NotNullFunction<Project,Boolean> predicate = ep.newPredicateInstance(myProject);
if (predicate == null || predicate.fun(myProject).equals(Boolean.TRUE)) {
final Content content = ContentFactory.SERVICE.getInstance().createContent(new ContentStub(ep), ep.getTabName(), false);
content.setCloseable(false);
content.putUserData(myEPKey, ep);
contentList.add(content);
}
}
myAddedContents.addAll(0, contentList);
}
private void addExtensionTab(final ChangesViewContentEP ep) {
final Content content = ContentFactory.SERVICE.getInstance().createContent(new ContentStub(ep), ep.getTabName(), false);
content.setCloseable(false);
content.putUserData(myEPKey, ep);
addIntoCorrectPlace(content);
}
private void updateExtensionTabs() {
final ChangesViewContentEP[] contentEPs = myProject.getExtensions(ChangesViewContentEP.EP_NAME);
for(ChangesViewContentEP ep: contentEPs) {
final NotNullFunction<Project,Boolean> predicate = ep.newPredicateInstance(myProject);
if (predicate == null) continue;
Content epContent = findEPContent(ep);
final Boolean predicateResult = predicate.fun(myProject);
if (predicateResult.equals(Boolean.TRUE) && epContent == null) {
addExtensionTab(ep);
}
else if (predicateResult.equals(Boolean.FALSE) && epContent != null) {
if (!(epContent.getComponent() instanceof ContentStub)) {
ep.getInstance(myProject).disposeContent();
}
myContentManager.removeContent(epContent, true);
}
}
}
@Nullable
private Content findEPContent(final ChangesViewContentEP ep) {
final Content[] contents = myContentManager.getContents();
for(Content content: contents) {
if (content.getUserData(myEPKey) == ep) {
return content;
}
}
return null;
}
private void updateToolWindowAvailability() {
final AbstractVcs[] abstractVcses = myVcsManager.getAllActiveVcss();
myToolWindow.setAvailable(abstractVcses.length > 0, null);
}
public boolean isToolwindowVisible() {
return ! myToolWindow.isDisposed() && myToolWindow.isVisible();
}
public void projectClosed() {
myVcsChangeAlarm.cancelAllRequests();
}
@NonNls @NotNull
public String getComponentName() {
return "ChangesViewContentManager";
}
public void addContent(Content content) {
if (myContentManager == null) {
myAddedContents.add(content);
}
else {
addIntoCorrectPlace(content);
}
}
public void removeContent(final Content content) {
if (myContentManager != null && (! myContentManager.isDisposed())) { // for unit tests
myContentManager.removeContent(content, true);
}
}
public void setSelectedContent(final Content content) {
myContentManager.setSelectedContent(content);
}
@Nullable
public <T> T getActiveComponent(final Class<T> aClass) {
final Content content = myContentManager.getSelectedContent();
if (content != null && aClass.isInstance(content.getComponent())) {
//noinspection unchecked
return (T) content.getComponent();
}
return null;
}
public boolean isContentSelected(final Content content) {
return Comparing.equal(content, myContentManager.getSelectedContent());
}
public void selectContent(final String tabName) {
for(Content content: myContentManager.getContents()) {
if (content.getDisplayName().equals(tabName)) {
myContentManager.setSelectedContent(content);
break;
}
}
}
private class MyVcsListener implements VcsListener {
public void directoryMappingChanged() {
myVcsChangeAlarm.cancelAllRequests();
myVcsChangeAlarm.addRequest(new Runnable() {
public void run() {
if (myProject.isDisposed()) return;
updateToolWindowAvailability();
updateExtensionTabs();
}
}, 100, ModalityState.NON_MODAL);
}
}
private static class ContentStub extends JPanel {
private final ChangesViewContentEP myEP;
private ContentStub(final ChangesViewContentEP EP) {
myEP = EP;
}
public ChangesViewContentEP getEP() {
return myEP;
}
}
private class MyContentManagerListener extends ContentManagerAdapter {
public void selectionChanged(final ContentManagerEvent event) {
Content content = event.getContent();
if (content.getComponent() instanceof ContentStub) {
ChangesViewContentEP ep = ((ContentStub) content.getComponent()).getEP();
ChangesViewContentProvider provider = ep.getInstance(myProject);
final JComponent contentComponent = provider.initContent();
content.setComponent(contentComponent);
if (contentComponent instanceof Disposable) {
content.setDisposer((Disposable) contentComponent);
}
}
}
}
private static final String[] ourPresetOrder = {"Local", "Repository", "Incoming", "Shelf"};
private static List<Content> doPresetOrdering(final List<Content> contents) {
final List<Content> result = new ArrayList<Content>(contents.size());
for (final String preset : ourPresetOrder) {
for (Iterator<Content> iterator = contents.iterator(); iterator.hasNext();) {
final Content current = iterator.next();
if (preset.equals(current.getTabName())) {
iterator.remove();
result.add(current);
}
}
}
result.addAll(contents);
return result;
}
private void addIntoCorrectPlace(final Content content) {
final String name = content.getTabName();
final Content[] contents = myContentManager.getContents();
int idxOfBeingInserted = -1;
for (int i = 0; i < ourPresetOrder.length; i++) {
final String s = ourPresetOrder[i];
if (s.equals(name)) {
idxOfBeingInserted = i;
}
}
if (idxOfBeingInserted == -1) {
myContentManager.addContent(content);
return;
}
final Set<String> existingNames = new HashSet<String>();
for (Content existingContent : contents) {
existingNames.add(existingContent.getTabName());
}
int place = idxOfBeingInserted;
for (int i = 0; i < idxOfBeingInserted; i++) {
if (! existingNames.contains(ourPresetOrder[i])) {
-- place;
}
}
myContentManager.addContent(content, place);
}
}