blob: 35d23b1fb64952e70b63ddbe02aea0f8b76aa92c [file] [log] [blame]
package org.intellij.lang.xpath.xslt.associations.impl;
import com.intellij.ide.projectView.ProjectViewNode;
import com.intellij.ide.projectView.impl.AbstractProjectTreeStructure;
import com.intellij.ide.projectView.impl.GroupByTypeComparator;
import com.intellij.ide.projectView.impl.ProjectTreeBuilder;
import com.intellij.ide.projectView.impl.nodes.PsiFileNode;
import com.intellij.ide.util.PsiElementListCellRenderer;
import com.intellij.ide.util.treeView.NodeDescriptor;
import com.intellij.ide.util.treeView.NodeRenderer;
import com.intellij.ide.util.treeView.TreeState;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Progressive;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Splitter;
import com.intellij.openapi.util.Disposer;
import com.intellij.psi.PsiFile;
import com.intellij.ui.*;
import com.intellij.ui.components.JBList;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.IconUtil;
import com.intellij.util.config.StorageAccessors;
import com.intellij.util.ui.UIUtil;
import icons.XpathIcons;
import org.intellij.lang.xpath.xslt.XsltSupport;
import org.intellij.lang.xpath.xslt.associations.FileAssociationsManager;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class AssociationsEditor {
@NonNls private static final String DIVIDER_PROPORTION = "dividerProportion";
private JPanel myComponent;
private JBList myList;
private Tree myTree;
private Splitter mySplitter;
private final StorageAccessors myProperties = StorageAccessors.createGlobal("AssociationsEditor");
private final AssociationsModel myListModel;
private final TransactionalManager myManager;
private final ProjectTreeBuilder myBuilder;
public AssociationsEditor(final Project project, final TreeState oldState) {
myManager = ((FileAssociationsManagerImpl)FileAssociationsManager.getInstance(project)).getTempManager();
initUI();
final DefaultTreeModel treeModel = new DefaultTreeModel(new DefaultMutableTreeNode());
myTree.setModel(treeModel);
myBuilder = new ProjectTreeBuilder(project, myTree, treeModel, new MyGroupByTypeComparator(), new MyProjectStructure(project));
myTree.setCellRenderer(new MyNodeRenderer(myManager));
new TreeSpeedSearch(myTree);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
ApplicationManager.getApplication().invokeLater(new Runnable() {
@Override
public void run() {
if (oldState == null) {
expandTree(treeModel);
}
else {
oldState.applyTo(myTree);
}
}
});
}
});
myListModel = new AssociationsModel(myTree, myManager);
myListModel.addListDataListener(new ListDataListener() {
public void intervalAdded(ListDataEvent listDataEvent) {
myTree.invalidate();
myTree.repaint();
}
public void intervalRemoved(ListDataEvent listDataEvent) {
myTree.invalidate();
myTree.repaint();
}
public void contentsChanged(ListDataEvent listDataEvent) {
}
});
myList.setModel(myListModel);
}
private void initUI() {
myComponent = new JPanel(new BorderLayout());
mySplitter = new Splitter(false, 0.3f);
myComponent.add(mySplitter, BorderLayout.CENTER);
JPanel leftPanel = new JPanel(new BorderLayout());
leftPanel.setBorder(IdeBorderFactory.createTitledBorder("Project XSLT Files", false, new Insets(0, 0, 0, 0)));
myTree = new Tree();
myTree.setRootVisible(false);
myTree.setShowsRootHandles(false);
leftPanel.add(new JBScrollPane(myTree), BorderLayout.CENTER);
mySplitter.setFirstComponent(leftPanel);
myList = new JBList();
myList.setCellRenderer(new MyCellRenderer());
myList.setMinimumSize(new Dimension(120, 200));
myList.getEmptyText().setText("No associated files");
JPanel rightPanel = ToolbarDecorator.createDecorator(myList)
.addExtraAction(AnActionButton.fromAction(new AddAssociationActionWrapper()))
.addExtraAction(AnActionButton.fromAction(new RemoveAssociationAction()))
.disableUpDownActions().disableAddAction().disableRemoveAction().createPanel();
UIUtil.addBorder(rightPanel, IdeBorderFactory.createTitledBorder("Associated Files", false, new Insets(0, 0, 0, 0)));
mySplitter.setSecondComponent(rightPanel);
final float dividerProportion = myProperties.getFloat(DIVIDER_PROPORTION, 0.3f);
mySplitter.setProportion(dividerProportion);
}
private void expandTree(DefaultTreeModel newModel) {
final TreePath rootPath = new TreePath(newModel.getRoot());
final Object element = myBuilder.getTreeStructure().getRootElement();
myBuilder.batch(new Progressive() {
@Override
public void run(@NotNull ProgressIndicator indicator) {
myBuilder.expand(element, null);
myBuilder.expand(myBuilder.getTreeStructure().getChildElements(element), null);
}
});
myTree.setSelectionPath(rootPath);
myTree.scrollRectToVisible(new Rectangle(new Point(0, 0)));
}
public TreeState getState() {
return TreeState.createOn(myTree);
}
public JPanel getComponent() {
return myComponent;
}
@Nullable
static Object getTreeSelection(JTree tree) {
final TreePath[] selectionPath = tree.getSelectionPaths();
if (selectionPath == null || selectionPath.length != 1) return null;
final Object component = selectionPath[0].getLastPathComponent();
return getObject(component);
}
@Nullable
private static Object getObject(Object component) {
if (!(component instanceof DefaultMutableTreeNode)) return null;
final DefaultMutableTreeNode node = ((DefaultMutableTreeNode)component);
final Object userObject = node.getUserObject();
if (!(userObject instanceof ProjectViewNode)) return null;
return ((ProjectViewNode<?>)userObject).getValue();
}
public boolean isModified() {
return myManager.isModified();
}
public void apply() {
myManager.applyChanges();
}
public void reset() {
myManager.reset();
final Object selection = getTreeSelection(myTree);
myListModel.update(selection instanceof PsiFile ? ((PsiFile)selection) : null);
}
public void dispose() {
myProperties.setFloat(DIVIDER_PROPORTION, mySplitter.getProportion());
Disposer.dispose(myBuilder);
myManager.dispose();
}
public void select(final PsiFile file) {
myBuilder.getReady(this).doWhenDone(new Runnable() {
@Override
public void run() {
myBuilder.select(file, file.getVirtualFile(), true);
}
});
}
class AddAssociationActionWrapper extends AddAssociationAction {
public AddAssociationActionWrapper() {
super(myManager);
}
public void actionPerformed(AnActionEvent e) {
final PsiFile selection = (PsiFile)getTreeSelection(myTree);
addAssociation(selection);
myListModel.update(selection);
}
public void update(AnActionEvent e) {
e.getPresentation().setEnabled(getTreeSelection(myTree) instanceof PsiFile);
}
}
class RemoveAssociationAction extends AnAction {
public RemoveAssociationAction() {
super("Remove", "Remove Association", IconUtil.getRemoveIcon());
}
public void actionPerformed(AnActionEvent e) {
final PsiFile selection = (PsiFile)getTreeSelection(myTree);
final PsiFile listSelection = (PsiFile)getListSelection();
myManager.removeAssociation(selection, listSelection);
myListModel.update(selection);
}
public void update(AnActionEvent e) {
e.getPresentation().setEnabled(getListSelection() instanceof PsiFile);
}
private Object getListSelection() {
return myList.getSelectedValue();
}
}
private static class MyGroupByTypeComparator extends GroupByTypeComparator {
public MyGroupByTypeComparator() {
super(true);
}
protected boolean isSortByType() {
return false;
}
}
@SuppressWarnings({"ALL"})
private static class MyProjectStructure extends AbstractProjectTreeStructure {
public MyProjectStructure(Project project) {
super(project);
}
public List getProviders() {
return Collections.EMPTY_LIST;
}
public Object[] getChildElements(Object obj) {
final Object[] childElements = super.getChildElements(obj);
List l = new ArrayList(childElements.length);
for (Object o : childElements) {
if (o instanceof ProjectViewNode) {
final ProjectViewNode node = (ProjectViewNode)o;
final Object element = node.getValue();
if (element instanceof PsiFile) {
if (XsltSupport.isXsltFile((PsiFile)element)) {
l.add(o);
}
}
else {
l.add(o);
}
}
}
return l.size() != childElements.length ? (Object[])l.toArray(new Object[l.size()]) : childElements;
}
public boolean isShowMembers() {
return false;
}
public boolean isShowModules() {
return true;
}
public boolean isFlattenPackages() {
return true;
}
public boolean isAbbreviatePackageNames() {
return false;
}
public boolean isHideEmptyMiddlePackages() {
return true;
}
public boolean isShowLibraryContents() {
return false;
}
}
static class AssociationsModel extends AbstractListModel implements TreeSelectionListener {
private final Tree myTree;
private final FileAssociationsManager myManager;
private PsiFile[] myFiles;
public AssociationsModel(Tree tree, FileAssociationsManager manager) {
myTree = tree;
myManager = manager;
myFiles = PsiFile.EMPTY_ARRAY;
myTree.addTreeSelectionListener(this);
}
public int getSize() {
return myFiles.length;
}
public Object getElementAt(int index) {
return myFiles[index];
}
public void valueChanged(TreeSelectionEvent e) {
final Object selection = getTreeSelection(myTree);
if (selection instanceof PsiFile) {
update((PsiFile)selection);
}
else {
update(null);
}
}
public void update(@Nullable PsiFile selection) {
final int oldSize = myFiles.length;
myFiles = PsiFile.EMPTY_ARRAY;
if (myFiles.length != oldSize) fireIntervalRemoved(this, 0, oldSize - 1);
if (selection != null) {
myFiles = myManager.getAssociationsFor(selection);
if (myFiles.length > 0) fireIntervalAdded(this, 0, myFiles.length - 1);
}
}
}
private static class MyNodeRenderer extends NodeRenderer {
private final DefaultMutableTreeNode myTemp = new DefaultMutableTreeNode();
private final FileAssociationsManager myManager;
public MyNodeRenderer(FileAssociationsManager manager) {
myManager = manager;
}
public void customizeCellRenderer(JTree tree,
Object value,
boolean selected,
boolean expanded,
boolean leaf,
int row,
boolean hasFocus) {
final Object object = getObject(value);
if (object instanceof PsiFile) {
final PsiFile file = (PsiFile)object;
final Object userObject = ((DefaultMutableTreeNode)value).getUserObject();
if (myManager.getAssociationsFor(file).length > 0) {
//noinspection unchecked
myTemp.setUserObject(new MyNodeDescriptor((NodeDescriptor<PsiFileNode>)userObject));
super.customizeCellRenderer(tree, myTemp, selected, expanded, leaf, row, hasFocus);
return;
}
}
super.customizeCellRenderer(tree, value, selected, expanded, leaf, row, hasFocus);
}
private static class MyNodeDescriptor extends NodeDescriptor<PsiFileNode> {
private final PsiFileNode myNode;
public MyNodeDescriptor(NodeDescriptor<PsiFileNode> nodeDescriptor) {
super(nodeDescriptor.getProject(), null);
myName = nodeDescriptor.toString();
setIcon(LayeredIcon.create(nodeDescriptor.getIcon(), XpathIcons.Association_small));
myColor = nodeDescriptor.getColor();
myNode = nodeDescriptor.getElement();
}
public boolean update() {
return false;
}
public PsiFileNode getElement() {
return myNode;
}
}
}
private static class MyCellRenderer extends PsiElementListCellRenderer<PsiFile> {
public String getElementText(PsiFile file) {
return file.getName();
}
protected String getContainerText(PsiFile psiElement, String string) {
//noinspection ConstantConditions
return "(" + psiElement.getVirtualFile().getParent().getPresentableUrl() + ")";
}
protected int getIconFlags() {
return 0;
}
}
}