blob: c91e578cbdee30fdcf031780e650aaaeb13553f1 [file] [log] [blame]
/*
* Copyright 2000-2011 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.ide.favoritesTreeView;
import com.intellij.ProjectTopics;
import com.intellij.ide.CopyPasteUtil;
import com.intellij.ide.projectView.BaseProjectTreeBuilder;
import com.intellij.ide.projectView.ProjectView;
import com.intellij.ide.projectView.ProjectViewPsiTreeChangeListener;
import com.intellij.ide.projectView.impl.ProjectAbstractTreeStructureBase;
import com.intellij.ide.util.treeView.AbstractTreeNode;
import com.intellij.ide.util.treeView.AbstractTreeStructure;
import com.intellij.ide.util.treeView.AbstractTreeUpdater;
import com.intellij.ide.util.treeView.NodeDescriptor;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.ide.CopyPasteManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ModuleRootAdapter;
import com.intellij.openapi.roots.ModuleRootEvent;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.vcs.FileStatusListener;
import com.intellij.openapi.vcs.FileStatusManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.SmartPsiElementPointer;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.ui.tree.TreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
/**
* @author Konstantin Bulenkov
*/
public class FavoritesViewTreeBuilder extends BaseProjectTreeBuilder {
private final ProjectViewPsiTreeChangeListener myPsiTreeChangeListener;
private final FileStatusListener myFileStatusListener;
private final CopyPasteUtil.DefaultCopyPasteListener myCopyPasteListener;
private final FavoritesListener myFavoritesListener;
public FavoritesViewTreeBuilder(@NotNull Project project,
JTree tree,
DefaultTreeModel treeModel,
ProjectAbstractTreeStructureBase treeStructure) {
super(project,
tree,
treeModel,
treeStructure,
new FavoritesComparator(ProjectView.getInstance(project), FavoritesProjectViewPane.ID));
final MessageBusConnection bus = myProject.getMessageBus().connect(this);
myPsiTreeChangeListener = new ProjectViewPsiTreeChangeListener(myProject) {
@Override
protected DefaultMutableTreeNode getRootNode() {
return FavoritesViewTreeBuilder.this.getRootNode();
}
@Override
protected AbstractTreeUpdater getUpdater() {
return FavoritesViewTreeBuilder.this.getUpdater();
}
@Override
protected boolean isFlattenPackages() {
return getStructure().isFlattenPackages();
}
@Override
protected void childrenChanged(PsiElement parent, final boolean stopProcessingForThisModificationCount) {
if (findNodeByElement(parent) == null) {
queueUpdate(true);
}
else {
super.childrenChanged(parent, true);
}
}
};
bus.subscribe(ProjectTopics.PROJECT_ROOTS, new ModuleRootAdapter() {
@Override
public void rootsChanged(ModuleRootEvent event) {
queueUpdate(true);
}
});
PsiManager.getInstance(myProject).addPsiTreeChangeListener(myPsiTreeChangeListener);
myFileStatusListener = new MyFileStatusListener();
FileStatusManager.getInstance(myProject).addFileStatusListener(myFileStatusListener);
myCopyPasteListener = new CopyPasteUtil.DefaultCopyPasteListener(getUpdater());
CopyPasteManager.getInstance().addContentChangedListener(myCopyPasteListener);
myFavoritesListener = new FavoritesListener() {
@Override
public void rootsChanged() {
updateFromRoot();
}
@Override
public void listAdded(String listName) {
updateFromRoot();
}
@Override
public void listRemoved(String listName) {
updateFromRoot();
}
};
initRootNode();
FavoritesManager.getInstance(myProject).addFavoritesListener(myFavoritesListener);
}
@NotNull
public FavoritesTreeStructure getStructure() {
final AbstractTreeStructure structure = getTreeStructure();
assert structure instanceof FavoritesTreeStructure;
return (FavoritesTreeStructure)structure;
}
public AbstractTreeNode getRoot() {
final Object rootElement = getRootElement();
assert rootElement instanceof AbstractTreeNode;
return (AbstractTreeNode)rootElement;
}
@Override
public void updateFromRoot() {
updateFromRootCB();
}
@Override
@NotNull
public ActionCallback updateFromRootCB() {
getStructure().rootsChanged();
if (isDisposed()) return new ActionCallback.Done();
getUpdater().cancelAllRequests();
return super.updateFromRootCB();
}
@NotNull
@Override
public ActionCallback select(Object element, VirtualFile file, boolean requestFocus) {
final DefaultMutableTreeNode node = findSmartFirstLevelNodeByElement(element);
if (node != null) {
return TreeUtil.selectInTree(node, requestFocus, getTree());
}
return super.select(element, file, requestFocus);
}
@Nullable
private static DefaultMutableTreeNode findFirstLevelNodeWithObject(final DefaultMutableTreeNode aRoot, final Object aObject) {
for (int i = 0; i < aRoot.getChildCount(); i++) {
final DefaultMutableTreeNode child = (DefaultMutableTreeNode)aRoot.getChildAt(i);
Object userObject = child.getUserObject();
if (userObject instanceof FavoritesTreeNodeDescriptor) {
if (Comparing.equal(((FavoritesTreeNodeDescriptor)userObject).getElement(), aObject)) {
return child;
}
}
}
return null;
}
@Override
protected Object findNodeByElement(Object element) {
final Object node = findSmartFirstLevelNodeByElement(element);
if (node != null) return node;
return super.findNodeByElement(element);
}
@Nullable
DefaultMutableTreeNode findSmartFirstLevelNodeByElement(final Object element) {
for (Object child : getRoot().getChildren()) {
AbstractTreeNode favorite = (AbstractTreeNode)child;
Object currentValue = favorite.getValue();
if (currentValue instanceof SmartPsiElementPointer) {
currentValue = ((SmartPsiElementPointer)favorite.getValue()).getElement();
}
/*else if (currentValue instanceof PsiJavaFile) {
final PsiClass[] classes = ((PsiJavaFile)currentValue).getClasses();
if (classes.length > 0) {
currentValue = classes[0];
}
}*/
if (Comparing.equal(element, currentValue)) {
final DefaultMutableTreeNode nodeWithObject =
findFirstLevelNodeWithObject((DefaultMutableTreeNode)getTree().getModel().getRoot(), favorite);
if (nodeWithObject != null) {
return nodeWithObject;
}
}
}
return null;
}
@Override
public final void dispose() {
super.dispose();
FavoritesManager.getInstance(myProject).removeFavoritesListener(myFavoritesListener);
PsiManager.getInstance(myProject).removePsiTreeChangeListener(myPsiTreeChangeListener);
FileStatusManager.getInstance(myProject).removeFileStatusListener(myFileStatusListener);
CopyPasteManager.getInstance().removeContentChangedListener(myCopyPasteListener);
}
@Override
protected boolean isAlwaysShowPlus(NodeDescriptor nodeDescriptor) {
final Object[] childElements = getStructure().getChildElements(nodeDescriptor);
return childElements != null && childElements.length > 0;
}
@Override
protected boolean isAutoExpandNode(NodeDescriptor nodeDescriptor) {
return nodeDescriptor.getParentDescriptor() == null;
}
private final class MyFileStatusListener implements FileStatusListener {
@Override
public void fileStatusesChanged() {
queueUpdateFrom(getRootNode(), false);
}
@Override
public void fileStatusChanged(@NotNull VirtualFile vFile) {
PsiElement element;
PsiManager psiManager = PsiManager.getInstance(myProject);
if (vFile.isDirectory()) {
element = psiManager.findDirectory(vFile);
}
else {
element = psiManager.findFile(vFile);
}
if (!getUpdater().addSubtreeToUpdateByElement(element) &&
element instanceof PsiFile &&
((PsiFile)element).getFileType() == StdFileTypes.JAVA) {
getUpdater().addSubtreeToUpdateByElement(((PsiFile)element).getContainingDirectory());
}
}
}
}