TreeState: cleanup & treePath instead of DMT nodes

This commit is contained in:
Gregory.Shrago
2017-03-31 19:40:47 +03:00
parent a154ffd721
commit 38a7aa0f66
10 changed files with 238 additions and 360 deletions
@@ -27,7 +27,6 @@ import com.intellij.openapi.externalSystem.model.ExternalProjectInfo;
import com.intellij.openapi.externalSystem.model.ProjectKeys;
import com.intellij.openapi.externalSystem.model.ProjectSystemId;
import com.intellij.openapi.externalSystem.model.project.ModuleData;
import com.intellij.openapi.externalSystem.model.task.TaskData;
import com.intellij.openapi.externalSystem.service.project.manage.ExternalSystemTaskActivator;
import com.intellij.openapi.externalSystem.service.project.manage.ExternalSystemTaskActivator.Phase;
import com.intellij.openapi.externalSystem.service.project.manage.ProjectDataManager;
@@ -45,7 +44,6 @@ import com.intellij.openapi.ui.popup.ListPopupStep;
import com.intellij.openapi.ui.popup.PopupStep;
import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.ui.*;
import com.intellij.ui.awt.RelativePoint;
@@ -395,13 +393,7 @@ public class ConfigureTasksActivationDialog extends DialogWrapper {
cleanUpEmptyNodes(node);
}
TreeState treeState = new TreeState();
try {
treeState.readExternal(treeStateElement);
treeState.applyTo(myTree);
}
catch (InvalidDataException ignore) {
}
TreeState.createFrom(treeStateElement).applyTo(myTree);
}
private void cleanUpEmptyNodes(@NotNull CachingSimpleNode node) {
@@ -41,7 +41,6 @@ import com.intellij.openapi.externalSystem.util.ExternalSystemUiUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.SimpleToolWindowPanel;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
@@ -524,16 +523,7 @@ public class ExternalProjectsViewImpl extends SimpleToolWindowPanel implements D
}
private void restoreTreeState() {
if (myState.treeState != null) {
TreeState treeState = new TreeState();
try {
treeState.readExternal(myState.treeState);
treeState.applyTo(myTree);
}
catch (InvalidDataException e) {
LOG.info(e);
}
}
TreeState.createFrom(myState.treeState).applyTo(myTree);
}
private <T extends ExternalSystemNode> List<T> getSelectedNodes(Class<T> aClass) {
@@ -439,9 +439,10 @@ public abstract class AbstractProjectViewPane implements DataProvider, Disposabl
List<Element> subPanes = element.getChildren(ELEMENT_SUBPANE);
for (Element subPane : subPanes) {
String subId = subPane.getAttributeValue(ATTRIBUTE_SUBID);
TreeState treeState = new TreeState();
treeState.readExternal(subPane);
if (!treeState.isEmpty()) myReadTreeState.put(subId, treeState);
TreeState treeState = TreeState.createFrom(subPane);
if (!treeState.isEmpty()) {
myReadTreeState.put(subId, treeState);
}
}
}
@@ -461,13 +462,17 @@ public abstract class AbstractProjectViewPane implements DataProvider, Disposabl
protected void saveExpandedPaths() {
if (myTree != null) {
TreeState treeState = TreeState.createOn(myTree);
if (!treeState.isEmpty()) myReadTreeState.put(getSubId(), treeState);
if (!treeState.isEmpty()) {
myReadTreeState.put(getSubId(), treeState);
}
}
}
public final void restoreExpandedPaths(){
TreeState treeState = myReadTreeState.get(getSubId());
if (treeState != null && !treeState.isEmpty()) treeState.applyTo(myTree);
if (treeState != null && !treeState.isEmpty()) {
treeState.applyTo(myTree);
}
}
public void installComparator() {
@@ -346,7 +346,7 @@ public class ScopeTreeViewPanel extends JPanel implements Disposable {
myBuilder.setTree(myTree);
myTree.getEmptyText().setText("Loading...");
myActionCallback = new ActionCallback();
myTree.putClientProperty(TreeState.CALLBACK, new WeakReference<>(myActionCallback));
UIUtil.putClientProperty(myTree, TreeState.CALLBACK, new WeakReference<>(myActionCallback));
myTree.setModel(myBuilder.build(myProject, true, () -> {
myTree.setPaintBusy(false);
myTree.getEmptyText().setText(UIBundle.message("message.nothingToShow"));
@@ -16,83 +16,80 @@
package com.intellij.ide.util.treeView;
import com.intellij.navigation.NavigationItem;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.Progressive;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.text.StringHash;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.reference.SoftReference;
import com.intellij.util.ExceptionUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.ui.UIUtil;
import com.intellij.util.ui.tree.TreeUtil;
import com.intellij.util.xmlb.XmlSerializer;
import com.intellij.util.xmlb.annotations.Attribute;
import com.intellij.util.xmlb.annotations.Tag;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
/**
* @see #createOn(javax.swing.JTree)
* @see #createOn(javax.swing.JTree, javax.swing.tree.DefaultMutableTreeNode)
* @see #createOn(JTree)
* @see #createOn(JTree, DefaultMutableTreeNode)
*
* @see #applyTo(javax.swing.JTree)
* @see #applyTo(javax.swing.JTree, javax.swing.tree.DefaultMutableTreeNode)
* @see #applyTo(JTree)
* @see #applyTo(JTree, Object)
*/
public class TreeState implements JDOMExternalizable {
@NonNls private static final String PATH = "PATH";
@NonNls private static final String SELECTED = "SELECTED";
@NonNls private static final String PATH_ELEMENT = "PATH_ELEMENT";
@NonNls private static final String USER_OBJECT = "USER_OBJECT";
@NonNls public static final String CALLBACK = "Callback";
private static final Logger LOG = Logger.getInstance(TreeState.class);
public static final Key<WeakReference<ActionCallback>> CALLBACK = Key.create("Callback");
private static final String EXPAND_TAG = "expand";
private static final String SELECT_TAG = "select";
private static final String PATH_TAG = "path";
@Tag("item")
static class PathElement {
public String myItemId;
public String myItemType;
@Attribute("name")
public String id;
@Attribute("type")
public String type;
@Attribute("user")
public String userStr;
private final int myItemIndex;
private Object myUserObject;
Object userObject;
final int index;
public PathElement(final String itemId, final String itemType, final int itemIndex, Object userObject) {
myItemId = itemId;
myItemType = itemType;
myItemIndex = itemIndex;
myUserObject = userObject;
/** @noinspection unused*/
PathElement() {
this(null, null, -1, null);
}
public PathElement() {
myItemIndex = -1;
myUserObject = null;
PathElement(String itemId, String itemType, int itemIndex, Object userObject) {
id = itemId;
type = itemType;
index = itemIndex;
userStr = userObject instanceof String ? (String)userObject : null;
this.userObject = userObject;
}
@Override
public String toString() {
return myItemId + ":" + myItemType;
}
public boolean matchedWith(NodeDescriptor nodeDescriptor) {
return Comparing.equal(myItemId, getDescriptorKey(nodeDescriptor)) &&
Comparing.equal(myItemType, getDescriptorType(nodeDescriptor));
}
public boolean matchedWithByObject(Object object) {
return myUserObject != null && myUserObject.equals(object);
}
public void readExternal(Element element) throws InvalidDataException {
DefaultJDOMExternalizer.readExternal(this, element);
myUserObject = element.getAttributeValue(USER_OBJECT);
}
public void writeExternal(Element element) throws WriteExternalException {
DefaultJDOMExternalizer.writeExternal(this, element);
if (myUserObject instanceof String){
element.setAttribute(USER_OBJECT, (String)myUserObject);
}
return id + ": " + type;
}
}
@@ -106,181 +103,124 @@ public class TreeState implements JDOMExternalizable {
myScrollToSelection = true;
}
public TreeState() {
this(new ArrayList<>(), new ArrayList<>());
}
public boolean isEmpty() {
return myExpandedPaths.isEmpty() && mySelectedPaths.isEmpty();
}
@Override
public void readExternal(Element element) throws InvalidDataException {
readExternal(element, myExpandedPaths, PATH);
readExternal(element, mySelectedPaths, SELECTED);
readExternal(element, myExpandedPaths, EXPAND_TAG);
readExternal(element, mySelectedPaths, SELECT_TAG);
}
private static void readExternal(Element element, List<List<PathElement>> list, String name) throws InvalidDataException {
private static void readExternal(Element root, List<List<PathElement>> list, String name) throws InvalidDataException {
list.clear();
final List paths = element.getChildren(name);
for (final Object path : paths) {
Element xmlPathElement = (Element)path;
list.add(readPath(xmlPathElement));
for (Element element : root.getChildren(name)) {
for (Element child : element.getChildren(PATH_TAG)) {
PathElement[] path = XmlSerializer.deserialize(child, PathElement[].class);
list.add(ContainerUtil.immutableList(path));
}
}
}
private static List<PathElement> readPath(final Element xmlPathElement) throws InvalidDataException {
final ArrayList<PathElement> result = new ArrayList<>();
final List elements = xmlPathElement.getChildren(PATH_ELEMENT);
for (final Object element : elements) {
Element xmlPathElementElement = (Element)element;
final PathElement pathElement = new PathElement();
pathElement.readExternal(xmlPathElementElement);
result.add(pathElement);
}
return result;
}
@NotNull
public static TreeState createOn(JTree tree, final DefaultMutableTreeNode treeNode) {
return new TreeState(createExpandedPaths(tree, treeNode), createSelectedPaths(tree, treeNode));
return new TreeState(createPaths(tree, TreeUtil.collectExpandedPaths(tree, new TreePath(treeNode.getPath()))),
createPaths(tree, TreeUtil.collectSelectedPaths(tree, new TreePath(treeNode.getPath()))));
}
@NotNull
public static TreeState createOn(@NotNull JTree tree) {
return new TreeState(createPaths(tree), new ArrayList<>());
return new TreeState(createPaths(tree, TreeUtil.collectExpandedPaths(tree)), new ArrayList<>());
}
@NotNull
public static TreeState createFrom(@Nullable Element element) {
TreeState state = new TreeState(new ArrayList<>(), new ArrayList<>());
try {
if (element != null) {
state.readExternal(element);
}
}
catch (InvalidDataException e) {
LOG.warn(e);
}
return state;
}
@Override
public void writeExternal(Element element) throws WriteExternalException {
writeExternal(element, myExpandedPaths, PATH);
writeExternal(element, mySelectedPaths, SELECTED);
writeExternal(element, myExpandedPaths, EXPAND_TAG);
writeExternal(element, mySelectedPaths, SELECT_TAG);
}
private static void writeExternal(Element element, List<List<PathElement>> list, String name) throws WriteExternalException {
Element root = new Element(name);
for (List<PathElement> path : list) {
final Element pathElement = new Element(name);
writeExternal(pathElement, path);
element.addContent(pathElement);
Element e = XmlSerializer.serialize(path.toArray());
e.setName(PATH_TAG);
root.addContent(e);
}
}
private static void writeExternal(final Element pathXmlElement, final List<PathElement> path) throws WriteExternalException {
for (final PathElement aPath : path) {
final Element pathXmlElementElement = new Element(PATH_ELEMENT);
aPath.writeExternal(pathXmlElementElement);
pathXmlElement.addContent(pathXmlElementElement);
}
}
private static List<List<PathElement>> createPaths(final JTree tree) {
final List<TreePath> expandedPaths = TreeUtil.collectExpandedPaths(tree);
return createPaths(tree, expandedPaths);
}
private static List<List<PathElement>> createExpandedPaths(JTree tree, final DefaultMutableTreeNode treeNode) {
final List<TreePath> expandedPaths = TreeUtil.collectExpandedPaths(tree, new TreePath(treeNode.getPath()));
return createPaths(tree, expandedPaths);
}
private static List<List<PathElement>> createSelectedPaths(JTree tree, final DefaultMutableTreeNode treeNode) {
final List<TreePath> selectedPaths
= TreeUtil.collectSelectedPaths(tree, new TreePath(treeNode.getPath()));
return createPaths(tree, selectedPaths);
element.addContent(root);
}
private static List<List<PathElement>> createPaths(JTree tree, List<TreePath> paths) {
ArrayList<List<PathElement>> result = new ArrayList<>();
for (TreePath path : paths) {
if (tree.isRootVisible() || path.getPathCount() > 1) {
List<PathElement> list = createPath(path);
if (list != null) result.add(list);
ContainerUtil.addIfNotNull(result, createPath(tree.getModel(), path));
}
}
return result;
}
private static List<PathElement> createPath(final TreePath treePath) {
final ArrayList<PathElement> result = new ArrayList<>();
@NotNull
private static List<PathElement> createPath(@NotNull TreeModel model, @NotNull TreePath treePath) {
ArrayList<PathElement> result = new ArrayList<>();
Object prev = null;
for (int i = 0; i < treePath.getPathCount(); i++) {
final Object pathComponent = treePath.getPathComponent(i);
if (pathComponent instanceof DefaultMutableTreeNode) {
final DefaultMutableTreeNode node = (DefaultMutableTreeNode)pathComponent;
final TreeNode parent = node.getParent();
final int childIndex = parent != null ? parent.getIndex(node) : 0;
final Object userObject = node.getUserObject();
if (userObject instanceof NodeDescriptor) {
final NodeDescriptor nodeDescriptor = (NodeDescriptor)userObject;
//nodeDescriptor.update();
result.add(new PathElement(getDescriptorKey(nodeDescriptor), getDescriptorType(nodeDescriptor), childIndex, nodeDescriptor));
}
else {
result.add(new PathElement("", "", childIndex, userObject));
}
}
else {
return null;
}
Object cur = treePath.getPathComponent(i);
Object userObject = TreeUtil.getUserObject(cur);
int childIndex = prev == null ? 0 : model.getIndexOfChild(prev, cur);
PathElement pe = new PathElement(calcId(userObject), calcType(userObject), childIndex, userObject);
result.add(pe);
prev = cur;
}
return result;
}
private static String getDescriptorKey(final NodeDescriptor nodeDescriptor) {
if (nodeDescriptor instanceof AbstractTreeNode) {
Object value;
if (nodeDescriptor instanceof NodeDescriptorProvidingKey) {
value = ((NodeDescriptorProvidingKey)nodeDescriptor).getKey();
private static String calcId(Object userObject) {
Object value =
userObject instanceof NodeDescriptorProvidingKey ? ((NodeDescriptorProvidingKey)userObject).getKey() :
userObject instanceof AbstractTreeNode ? ((AbstractTreeNode)userObject).getValue() :
userObject;
if (value instanceof NavigationItem) {
try {
String name = ((NavigationItem)value).getName();
return name != null ? name : value.toString();
}
else {
value = ((AbstractTreeNode)nodeDescriptor).getValue();
}
if (value instanceof NavigationItem) {
try {
final String name = ((NavigationItem)value).getName();
return name != null ? name : value.toString();
}
catch (Exception e) {
//ignore for invalid psi element
}
catch (Exception ignored) {
}
}
return nodeDescriptor.toString();
return userObject.toString();
}
private static String getDescriptorType(final NodeDescriptor nodeDescriptor) {
return nodeDescriptor.getClass().getName();
private static String calcType(Object userObject) {
String name = userObject.getClass().getName();
return Integer.toHexString(StringHash.murmur(name, 31)) + ":" + StringUtil.getShortName(name);
}
public void applyTo(JTree tree) {
applyTo(tree, (DefaultMutableTreeNode)tree.getModel().getRoot());
public void applyTo(@NotNull JTree tree) {
applyTo(tree, tree.getModel().getRoot());
}
private void applyExpanded(TreeFacade tree, Object root, ProgressIndicator indicator) {
indicator.checkCanceled();
if (!(root instanceof DefaultMutableTreeNode)) {
return;
}
final DefaultMutableTreeNode nodeRoot = (DefaultMutableTreeNode)root;
final TreeNode[] nodePath = nodeRoot.getPath();
if (nodePath.length > 0) {
for (final List<PathElement> path : myExpandedPaths) {
applyTo(nodePath.length - 1,path, root, tree, indicator);
}
}
}
public void applyTo(final JTree tree, final DefaultMutableTreeNode node) {
final TreeFacade facade = getFacade(tree);
public void applyTo(@NotNull JTree tree, @Nullable Object root) {
if (root == null) return;
TreeFacade facade = TreeFacade.getFacade(tree);
ActionCallback callback = facade.getInitialized().doWhenDone(new TreeRunnable("TreeState.applyTo: on done facade init") {
@Override
public void perform() {
facade.batch(new Progressive() {
@Override
public void run(@NotNull ProgressIndicator indicator) {
applyExpanded(facade, node, indicator);
}
});
facade.batch(indicator -> applyExpandedTo(facade, new TreePath(root), indicator));
}
});
if (tree.getSelectionCount() == 0) {
@@ -288,158 +228,134 @@ public class TreeState implements JDOMExternalizable {
@Override
public void perform() {
if (tree.getSelectionCount() == 0) {
applySelected(tree, node);
applySelectedTo(tree);
}
}
});
}
}
private void applySelected(final JTree tree, final DefaultMutableTreeNode node) {
TreeUtil.unselect(tree, node);
List<TreePath> selectionPaths = new ArrayList<>();
for (List<PathElement> pathElements : mySelectedPaths) {
applySelectedTo(pathElements, tree.getModel().getRoot(), tree, selectionPaths, myScrollToSelection);
}
private void applyExpandedTo(@NotNull TreeFacade tree, @NotNull TreePath rootPath, @NotNull ProgressIndicator indicator) {
indicator.checkCanceled();
if (rootPath.getPathCount() <= 0) return;
if (selectionPaths.size() > 1) {
for (TreePath path : selectionPaths) {
tree.addSelectionPath(path);
for (List<PathElement> path : myExpandedPaths) {
if (path.isEmpty()) continue;
int index = rootPath.getPathCount() - 1;
if (!pathMatches(path.get(index), rootPath.getPathComponent(index))) continue;
expandImpl(0, path, rootPath, tree, indicator);
}
}
private void applySelectedTo(@NotNull JTree tree) {
List<TreePath> selection = new ArrayList<>();
for (List<PathElement> path : mySelectedPaths) {
TreeModel model = tree.getModel();
TreePath treePath = new TreePath(model.getRoot());
for (int i = 1; treePath != null && i < path.size(); i++) {
treePath = findMatchedChild(model, treePath, path.get(i));
}
ContainerUtil.addIfNotNull(selection, treePath);
}
if (selection.isEmpty()) return;
for (TreePath treePath : selection) {
tree.setSelectionPath(treePath);
}
if (myScrollToSelection) {
TreeUtil.showRowCentered(tree, tree.getRowForPath(selection.get(0)), true, true);
}
}
@Nullable
private static DefaultMutableTreeNode findMatchedChild(DefaultMutableTreeNode parent, PathElement pathElement) {
for (int j = 0; j < parent.getChildCount(); j++) {
final TreeNode child = parent.getChildAt(j);
if (!(child instanceof DefaultMutableTreeNode)) continue;
final DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)child;
final Object userObject = childNode.getUserObject();
if (pathElement.matchedWithByObject(userObject)) return childNode;
private static TreePath findMatchedChild(@NotNull TreeModel model, @NotNull TreePath treePath, @NotNull PathElement pathElement) {
Object parent = treePath.getLastPathComponent();
int childCount = model.getChildCount(parent);
for (int j = 0; j < childCount; j++) {
Object child = model.getChild(parent, j);
if (pathMatches(pathElement, child)) {
return treePath.pathByAddingChild(child);
}
}
for (int j = 0; j < parent.getChildCount(); j++) {
final TreeNode child = parent.getChildAt(j);
if (!(child instanceof DefaultMutableTreeNode)) continue;
final DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)child;
final Object userObject = childNode.getUserObject();
if (!(userObject instanceof NodeDescriptor)) continue;
final NodeDescriptor nodeDescriptor = (NodeDescriptor)userObject;
if (pathElement.matchedWith(nodeDescriptor)) return childNode;
}
if (parent.getChildCount() > 0) {
int index = pathElement.myItemIndex;
if (index >= parent.getChildCount()) {
index = parent.getChildCount()-1;
}
index = Math.max(0, index);
final TreeNode child = parent.getChildAt(index);
if (child instanceof DefaultMutableTreeNode) {
return (DefaultMutableTreeNode) child;
}
if (childCount > 0) {
int index = Math.max(0, Math.min(pathElement.index, childCount - 1));
Object child = model.getChild(parent, index);
return treePath.pathByAddingChild(child);
}
return null;
}
private static boolean applyTo(final int positionInPath,
final List<PathElement> path,
final Object root,
final TreeFacade tree,
final ProgressIndicator indicator) {
if (!(root instanceof DefaultMutableTreeNode)) return false;
private static boolean pathMatches(@NotNull PathElement pe, Object child) {
Object userObject = TreeUtil.getUserObject(child);
if (pe.userObject != null && pe.userObject.equals(userObject)) return true;
return Comparing.equal(pe.id, calcId(userObject)) &&
Comparing.equal(pe.type, calcType(userObject));
}
final DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)root;
final Object userObject = treeNode.getUserObject();
final PathElement pathElement = path.get(positionInPath);
if (userObject instanceof NodeDescriptor) {
if (!pathElement.matchedWith((NodeDescriptor)userObject)) return false;
}
else {
if (!pathElement.matchedWithByObject(userObject)) return false;
}
tree.expand(treeNode).doWhenDone(new TreeRunnable("TreeState.applyTo") {
private static void expandImpl(int positionInPath,
List<PathElement> path,
TreePath treePath,
TreeFacade tree,
ProgressIndicator indicator) {
tree.expand(treePath).doWhenDone(new TreeRunnable("TreeState.applyTo") {
@Override
public void perform() {
indicator.checkCanceled();
if (positionInPath == path.size() - 1) {
return;
}
PathElement next = positionInPath == path.size() - 1 ? null : path.get(positionInPath + 1);
if (next == null) return;
for (int j = 0; j < treeNode.getChildCount(); j++) {
final TreeNode child = treeNode.getChildAt(j);
final boolean resultFromChild = applyTo(positionInPath + 1, path, child, tree, indicator);
if (resultFromChild) {
Object parent = treePath.getLastPathComponent();
TreeModel model = tree.tree.getModel();
int childCount = model.getChildCount(parent);
for (int j = 0; j < childCount; j++) {
Object child = tree.tree.getModel().getChild(parent, j);
if (pathMatches(next, child)) {
expandImpl(positionInPath + 1, path, treePath.pathByAddingChild(child), tree, indicator);
break;
}
}
}
});
return true;
}
private static void applySelectedTo(final List<PathElement> path,
Object root,
JTree tree,
final List<TreePath> outSelectionPaths, final boolean scrollToSelection) {
static abstract class TreeFacade {
for (int i = 1; i < path.size(); i++) {
if (!(root instanceof DefaultMutableTreeNode)) return;
final JTree tree;
root = findMatchedChild((DefaultMutableTreeNode)root, path.get(i));
TreeFacade(@NotNull JTree tree) {this.tree = tree;}
abstract ActionCallback getInitialized();
abstract ActionCallback expand(TreePath treePath);
abstract void batch(Progressive progressive);
static TreeFacade getFacade(JTree tree) {
AbstractTreeBuilder builder = AbstractTreeBuilder.getBuilderFor(tree);
return builder != null ? new BuilderFacade(builder) : new JTreeFacade(tree);
}
if (!(root instanceof DefaultMutableTreeNode)) return;
final TreePath pathInNewTree = new TreePath(((DefaultMutableTreeNode) root).getPath());
if (scrollToSelection) {
TreeUtil.selectPath(tree, pathInNewTree);
} else {
tree.setSelectionPath(pathInNewTree);
}
outSelectionPaths.add(pathInNewTree);
}
interface TreeFacade {
ActionCallback getInitialized();
ActionCallback expand(DefaultMutableTreeNode node);
void batch(Progressive progressive);
}
private static TreeFacade getFacade(JTree tree) {
final AbstractTreeBuilder builder = AbstractTreeBuilder.getBuilderFor(tree);
return builder != null ? new BuilderFacade(builder) : new JTreeFacade(tree);
}
public static class JTreeFacade implements TreeFacade {
private final JTree myTree;
static class JTreeFacade extends TreeFacade {
JTreeFacade(JTree tree) {
myTree = tree;
super(tree);
}
@Override
public ActionCallback expand(DefaultMutableTreeNode node) {
myTree.expandPath(new TreePath(node.getPath()));
public ActionCallback expand(@NotNull TreePath treePath) {
tree.expandPath(treePath);
return ActionCallback.DONE;
}
@Override
public ActionCallback getInitialized() {
final WeakReference<ActionCallback> ref = (WeakReference<ActionCallback>)myTree.getClientProperty(CALLBACK);
final ActionCallback callback = SoftReference.dereference(ref);
WeakReference<ActionCallback> ref = UIUtil.getClientProperty(tree, CALLBACK);
ActionCallback callback = SoftReference.dereference(ref);
if (callback != null) return callback;
return ActionCallback.DONE;
}
@@ -450,11 +366,12 @@ public class TreeState implements JDOMExternalizable {
}
}
static class BuilderFacade implements TreeFacade {
static class BuilderFacade extends TreeFacade {
private final AbstractTreeBuilder myBuilder;
BuilderFacade(AbstractTreeBuilder builder) {
super(ObjectUtils.notNull(builder.getTree()));
myBuilder = builder;
}
@@ -469,16 +386,13 @@ public class TreeState implements JDOMExternalizable {
}
@Override
public ActionCallback expand(DefaultMutableTreeNode node) {
final Object userObject = node.getUserObject();
public ActionCallback expand(TreePath treePath) {
Object userObject = TreeUtil.getUserObject(treePath.getLastPathComponent());
if (!(userObject instanceof NodeDescriptor)) return ActionCallback.REJECTED;
NodeDescriptor desc = (NodeDescriptor)userObject;
final Object element = myBuilder.getTreeStructureElement(desc);
final ActionCallback result = new ActionCallback();
Object element = myBuilder.getTreeStructureElement(desc);
ActionCallback result = new ActionCallback();
myBuilder.expand(element, result.createSetDoneRunnable());
return result;
@@ -491,36 +405,16 @@ public class TreeState implements JDOMExternalizable {
@Override
public String toString() {
StringBuilder sb = new StringBuilder("TreeState(").append(myScrollToSelection).append(")");
append(sb, " expanded:", myExpandedPaths);
append(sb, " selected:", mySelectedPaths);
return sb.toString();
}
private static void append(StringBuilder sb, String prefix, Object object) {
if (prefix != null) {
sb.append(prefix);
Element st = new Element("TreeState");
String content;
try {
writeExternal(st);
content = JDOMUtil.writeChildren(st, "\n");
}
if (object instanceof List) {
appendList(sb, (List)object);
}
else {
sb.append(object);
}
}
private static void appendList(StringBuilder sb, List list) {
if (list.isEmpty()) {
sb.append("{}");
}
else {
String prefix = "{";
for (Object object : list) {
append(sb, prefix, object);
prefix = ", ";
}
sb.append("}");
catch (IOException e) {
content = ExceptionUtil.getThrowableText(e);
}
return "TreeState(" + myScrollToSelection + ")\n" + content;
}
}
@@ -25,6 +25,7 @@ import com.intellij.openapi.util.SystemInfo;
import com.intellij.ui.*;
import com.intellij.util.ReflectionUtil;
import com.intellij.util.ui.*;
import com.intellij.util.ui.tree.TreeUtil;
import com.intellij.util.ui.tree.WideSelectionTreeUI;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -338,7 +339,7 @@ public class Tree extends JTree implements ComponentWithEmptyText, ComponentWith
component = pathObjects[pathObjects.length - 2];
}
Color color = getFileColorFor((DefaultMutableTreeNode)component);
Color color = getFileColorFor(TreeUtil.getUserObject(component));
if (color != null) {
g.setColor(color);
g.fillRect(0, bounds.y, getWidth(), bounds.height);
@@ -722,7 +722,7 @@ public final class TreeUtil {
}
}
public static void collapseAll(@NotNull final JTree tree, final int keepSelectionLevel) {
public static void collapseAll(@NotNull JTree tree, final int keepSelectionLevel) {
final TreePath leadSelectionPath = tree.getLeadSelectionPath();
// Collapse all
int row = tree.getRowCount() - 1;
@@ -730,8 +730,10 @@ public final class TreeUtil {
tree.collapseRow(row);
row--;
}
final DefaultMutableTreeNode root = (DefaultMutableTreeNode)tree.getModel().getRoot();
tree.expandPath(new TreePath(root));
Object root = tree.getModel().getRoot();
if (root != null) {
tree.expandPath(new TreePath(root));
}
if (leadSelectionPath != null) {
final Object[] path = leadSelectionPath.getPath();
final Object[] pathToSelect = new Object[path.length > keepSelectionLevel && keepSelectionLevel >= 0 ? keepSelectionLevel : path.length];
@@ -883,14 +885,14 @@ public final class TreeUtil {
return result;
}
public static void unselect(@NotNull JTree tree, @NotNull final DefaultMutableTreeNode node) {
final TreePath rootPath = new TreePath(node.getPath());
final TreePath[] selectionPaths = tree.getSelectionPaths();
if (selectionPaths != null) {
for (TreePath selectionPath : selectionPaths) {
if (selectionPath.getPathCount() > rootPath.getPathCount() && rootPath.isDescendant(selectionPath)) {
tree.removeSelectionPath(selectionPath);
}
public static void unselectPath(@NotNull JTree tree, @Nullable TreePath path) {
if (path == null) return;
TreePath[] selectionPaths = tree.getSelectionPaths();
if (selectionPaths == null) return;
for (TreePath selectionPath : selectionPaths) {
if (selectionPath.getPathCount() > path.getPathCount() && path.isDescendant(selectionPath)) {
tree.removeSelectionPath(selectionPath);
}
}
}
@@ -984,6 +986,11 @@ public final class TreeUtil {
}
}
@Nullable
public static Object getUserObject(@Nullable Object node) {
return node instanceof DefaultMutableTreeNode ? ((DefaultMutableTreeNode)node).getUserObject() : node;
}
@FunctionalInterface
public interface Traverse{
boolean accept(Object node);
@@ -28,7 +28,6 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
@@ -229,11 +228,11 @@ public class BreakpointItemsTreeController implements BreakpointsCheckboxTree.De
if (paths == null) return;
final List<BreakpointItem> breakpoints = getSelectedBreakpoints(true);
for (TreePath path : paths) {
final Object node = path.getLastPathComponent();
Object node = path.getLastPathComponent();
if (node instanceof BreakpointItemNode) {
final BreakpointItem item = ((BreakpointItemNode)node).getBreakpointItem();
if (!item.allowedToRemove()) {
TreeUtil.unselect(myTreeView, (DefaultMutableTreeNode)node);
TreeUtil.unselectPath(myTreeView, path);
breakpoints.remove(item);
}
}
@@ -26,7 +26,6 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.components.*;
import com.intellij.openapi.project.DumbAwareRunnable;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.WriteExternalException;
import com.intellij.openapi.wm.ToolWindowAnchor;
@@ -359,16 +358,7 @@ public class MavenProjectsNavigator extends MavenSimpleProjectComponent implemen
r.run();
if (shouldCreate) {
if (myState.treeState != null) {
TreeState treeState = new TreeState();
try {
treeState.readExternal(myState.treeState);
treeState.applyTo(myTree);
}
catch (InvalidDataException e) {
MavenLog.LOG.info(e);
}
}
TreeState.createFrom(myState.treeState).applyTo(myTree);
}
});
}
@@ -110,7 +110,7 @@ public class FileAssociationsConfigurable implements SearchableConfigurable, Con
@Override
public TreeState getState() {
return state != null ? state : new TreeState();
return state != null ? state : TreeState.createFrom(null);
}
@Override