VCS: asynch load of committed changes when do "Browse changes"
[fedora-idea.git] / platform / platform-api / src / com / intellij / ide / util / treeView / TreeState.java
blobc7195efde31f26dbf8477c288ed7631294488146
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.ide.util.treeView;
18 import com.intellij.navigation.NavigationItem;
19 import com.intellij.openapi.util.*;
20 import com.intellij.util.ui.tree.TreeUtil;
21 import org.jdom.Element;
22 import org.jetbrains.annotations.NonNls;
23 import org.jetbrains.annotations.NotNull;
25 import javax.swing.*;
26 import javax.swing.tree.DefaultMutableTreeNode;
27 import javax.swing.tree.TreeNode;
28 import javax.swing.tree.TreePath;
29 import java.util.ArrayList;
30 import java.util.List;
32 public class TreeState implements JDOMExternalizable {
33 @NonNls private static final String PATH = "PATH";
34 @NonNls private static final String PATH_ELEMENT = "PATH_ELEMENT";
35 @NonNls private static final String USER_OBJECT = "USER_OBJECT";
37 static class PathElement implements JDOMExternalizable {
38 public String myItemId;
39 public String myItemType;
41 private final int myItemIndex;
42 private Object myUserObject;
44 public PathElement(final String itemId, final String itemType, final int itemIndex, Object userObject) {
45 myItemId = itemId;
46 myItemType = itemType;
48 myItemIndex = itemIndex;
49 myUserObject = userObject;
52 public PathElement() {
53 myItemIndex = -1;
54 myUserObject = null;
57 public boolean matchedWith(NodeDescriptor nodeDescriptor) {
58 return Comparing.equal(myItemId, getDescriptorKey(nodeDescriptor)) &&
59 Comparing.equal(myItemType, getDescriptorType(nodeDescriptor));
62 public boolean matchedWithByObject(Object object) {
63 return myUserObject != null && myUserObject.equals(object);
66 public void readExternal(Element element) throws InvalidDataException {
67 DefaultJDOMExternalizer.readExternal(this, element);
68 myUserObject = element.getAttributeValue(USER_OBJECT);
71 public void writeExternal(Element element) throws WriteExternalException {
72 DefaultJDOMExternalizer.writeExternal(this, element);
73 if (myUserObject instanceof String){
74 element.setAttribute(USER_OBJECT, (String)myUserObject);
79 private final List<List<PathElement>> myExpandedPaths;
80 private final List<List<PathElement>> mySelectedPaths;
81 private boolean myScrollToSelection;
83 private TreeState(List<List<PathElement>> expandedPaths, final List<List<PathElement>> selectedPaths) {
84 myExpandedPaths = expandedPaths;
85 mySelectedPaths = selectedPaths;
86 myScrollToSelection = true;
89 public TreeState() {
90 this(new ArrayList<List<PathElement>>(), new ArrayList<List<PathElement>>());
93 public void readExternal(Element element) throws InvalidDataException {
94 myExpandedPaths.clear();
95 final List paths = element.getChildren(PATH);
96 for (final Object path : paths) {
97 Element xmlPathElement = (Element)path;
98 myExpandedPaths.add(readPath(xmlPathElement));
102 private static List<PathElement> readPath(final Element xmlPathElement) throws InvalidDataException {
103 final ArrayList<PathElement> result = new ArrayList<PathElement>();
104 final List elements = xmlPathElement.getChildren(PATH_ELEMENT);
105 for (final Object element : elements) {
106 Element xmlPathElementElement = (Element)element;
107 final PathElement pathElement = new PathElement();
108 pathElement.readExternal(xmlPathElementElement);
109 result.add(pathElement);
111 return result;
114 public static TreeState createOn(JTree tree, final DefaultMutableTreeNode treeNode) {
115 return new TreeState(createExpandedPaths(tree, treeNode), createSelectedPaths(tree, treeNode));
118 public void writeExternal(Element element) throws WriteExternalException {
119 for (List<PathElement> path : myExpandedPaths) {
120 final Element pathElement = new Element(PATH);
121 writeExternal(pathElement, path);
122 element.addContent(pathElement);
126 private static void writeExternal(final Element pathXmlElement, final List<PathElement> path) throws WriteExternalException {
127 for (final PathElement aPath : path) {
128 final Element pathXmlElementElement = new Element(PATH_ELEMENT);
129 aPath.writeExternal(pathXmlElementElement);
130 pathXmlElement.addContent(pathXmlElementElement);
134 public static TreeState createOn(@NotNull JTree tree) {
135 return new TreeState(createPaths(tree), new ArrayList<List<PathElement>>());
139 private static List<List<PathElement>> createPaths(final JTree tree) {
140 final ArrayList<List<PathElement>> result = new ArrayList<List<PathElement>>();
141 final List<TreePath> expandedPaths = TreeUtil.collectExpandedPaths(tree);
142 for (final TreePath expandedPath : expandedPaths) {
143 final List<PathElement> path = createPath(expandedPath);
144 if (path != null) {
145 result.add(path);
148 return result;
151 private static List<List<PathElement>> createExpandedPaths(JTree tree, final DefaultMutableTreeNode treeNode) {
152 final ArrayList<List<PathElement>> result = new ArrayList<List<PathElement>>();
153 final List<TreePath> expandedPaths = TreeUtil.collectExpandedPaths(tree, new TreePath(treeNode.getPath()));
154 for (final TreePath expandedPath : expandedPaths) {
155 final List<PathElement> path = createPath(expandedPath);
156 if (path != null) {
157 result.add(path);
160 return result;
163 private static List<List<PathElement>> createSelectedPaths(JTree tree, final DefaultMutableTreeNode treeNode) {
164 final ArrayList<List<PathElement>> result = new ArrayList<List<PathElement>>();
165 final List<TreePath> selectedPaths
166 = TreeUtil.collectSelectedPaths(tree, new TreePath(treeNode.getPath()));
167 for (final TreePath expandedPath : selectedPaths) {
168 final List<PathElement> path = createPath(expandedPath);
169 if (path != null) {
170 result.add(path);
173 return result;
176 private static List<PathElement> createPath(final TreePath treePath) {
177 final ArrayList<PathElement> result = new ArrayList<PathElement>();
178 for (int i = 0; i < treePath.getPathCount(); i++) {
179 final Object pathComponent = treePath.getPathComponent(i);
180 if (pathComponent instanceof DefaultMutableTreeNode) {
181 final DefaultMutableTreeNode node = (DefaultMutableTreeNode)pathComponent;
182 final TreeNode parent = node.getParent();
184 final Object userObject = node.getUserObject();
185 if (userObject instanceof NodeDescriptor) {
186 final NodeDescriptor nodeDescriptor = (NodeDescriptor)userObject;
187 //nodeDescriptor.update();
188 final int childIndex = parent != null ? parent.getIndex(node) : 0;
189 result.add(new PathElement(getDescriptorKey(nodeDescriptor), getDescriptorType(nodeDescriptor), childIndex, nodeDescriptor));
191 else {
192 result.add(new PathElement("", "", 0, userObject));
195 else {
196 return null;
199 return result;
202 private static String getDescriptorKey(final NodeDescriptor nodeDescriptor) {
203 if (nodeDescriptor instanceof AbstractTreeNode) {
204 Object value;
205 if (nodeDescriptor instanceof NodeDescriptorProvidingKey) {
206 value = ((NodeDescriptorProvidingKey)nodeDescriptor).getKey();
208 else {
209 value = ((AbstractTreeNode)nodeDescriptor).getValue();
212 if (value instanceof NavigationItem) {
213 try {
214 final String name = ((NavigationItem)value).getName();
215 return name != null ? name : value.toString();
217 catch (Exception e) {
218 //ignore for invalid psi element
222 return nodeDescriptor.toString();
225 private static String getDescriptorType(final NodeDescriptor nodeDescriptor) {
226 return nodeDescriptor.getClass().getName();
229 public void applyTo(JTree tree) {
230 applyExpanded(getFacade(tree), tree.getModel().getRoot());
233 private void applyExpanded(final TreeFacade tree, final Object root) {
234 tree.getIntialized().doWhenDone(new Runnable() {
235 public void run() {
236 _applyExpanded(tree, root);
241 private void _applyExpanded(TreeFacade tree, Object root) {
242 if (!(root instanceof DefaultMutableTreeNode)) {
243 return;
245 final DefaultMutableTreeNode nodeRoot = (DefaultMutableTreeNode)root;
246 final TreeNode[] nodePath = nodeRoot.getPath();
247 if (nodePath.length > 0) {
248 for (final List<PathElement> path : myExpandedPaths) {
249 applyTo(nodePath.length - 1,path, root, tree);
254 public void applyTo(final JTree tree, final DefaultMutableTreeNode node) {
255 applyExpanded(getFacade(tree), node);
256 if (tree.getSelectionCount() == 0) {
257 applySelected(tree, node);
261 // todo
262 private void applySelected(final JTree tree, final DefaultMutableTreeNode node) {
263 TreeUtil.unselect(tree, node);
264 List<TreePath> selectionPaths = new ArrayList<TreePath>();
265 for (List<PathElement> pathElements : mySelectedPaths) {
266 applySelectedTo(pathElements, tree.getModel().getRoot(), tree, selectionPaths, myScrollToSelection);
269 if (selectionPaths.size() > 1) {
270 for (TreePath path : selectionPaths) {
271 tree.addSelectionPath(path);
277 private static DefaultMutableTreeNode findMatchedChild(DefaultMutableTreeNode parent, PathElement pathElement) {
279 for (int j = 0; j < parent.getChildCount(); j++) {
280 final TreeNode child = parent.getChildAt(j);
281 if (!(child instanceof DefaultMutableTreeNode)) continue;
282 final DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)child;
283 final Object userObject = childNode.getUserObject();
284 if (pathElement.matchedWithByObject(userObject)) return childNode;
287 for (int j = 0; j < parent.getChildCount(); j++) {
288 final TreeNode child = parent.getChildAt(j);
289 if (!(child instanceof DefaultMutableTreeNode)) continue;
290 final DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)child;
291 final Object userObject = childNode.getUserObject();
292 if (!(userObject instanceof NodeDescriptor)) continue;
293 final NodeDescriptor nodeDescriptor = (NodeDescriptor)userObject;
294 if (pathElement.matchedWith(nodeDescriptor)) return childNode;
297 if (parent.getChildCount() > 0) {
298 int index = pathElement.myItemIndex;
299 if (index >= parent.getChildCount()) {
300 index = parent.getChildCount()-1;
302 final TreeNode child = parent.getChildAt(index);
303 if (child instanceof DefaultMutableTreeNode) {
304 return (DefaultMutableTreeNode) child;
308 return null;
312 private static boolean applyTo(final int positionInPath, final List<PathElement> path, final Object root, final TreeFacade tree) {
313 if (!(root instanceof DefaultMutableTreeNode)) return false;
315 final DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)root;
317 final Object userObject = treeNode.getUserObject();
318 final PathElement pathElement = path.get(positionInPath);
320 if (userObject instanceof NodeDescriptor) {
321 if (!pathElement.matchedWith((NodeDescriptor)userObject)) return false;
323 else {
324 if (!pathElement.matchedWithByObject(userObject)) return false;
327 tree.expand(treeNode).doWhenDone(new Runnable() {
328 public void run() {
329 if (positionInPath == path.size() - 1) {
330 return;
333 for (int j = 0; j < treeNode.getChildCount(); j++) {
334 final TreeNode child = treeNode.getChildAt(j);
335 final boolean resultFromChild = applyTo(positionInPath + 1, path, child, tree);
336 if (resultFromChild) {
337 break;
344 return true;
347 private static void applySelectedTo(final List<PathElement> path,
348 Object root,
349 JTree tree,
350 final List<TreePath> outSelectionPaths, final boolean scrollToSelection) {
352 for (int i = 1; i < path.size(); i++) {
353 if (!(root instanceof DefaultMutableTreeNode)) return;
355 root = findMatchedChild((DefaultMutableTreeNode)root, path.get(i));
358 if (!(root instanceof DefaultMutableTreeNode)) return;
360 final TreePath pathInNewTree = new TreePath(((DefaultMutableTreeNode) root).getPath());
361 if (scrollToSelection) {
362 TreeUtil.selectPath(tree, pathInNewTree);
363 } else {
364 tree.setSelectionPath(pathInNewTree);
366 outSelectionPaths.add(pathInNewTree);
369 interface TreeFacade {
370 ActionCallback getIntialized();
371 ActionCallback expand(DefaultMutableTreeNode node);
374 private static TreeFacade getFacade(JTree tree) {
375 final AbstractTreeBuilder builder = AbstractTreeBuilder.getBuilderFor(tree);
376 return builder != null ? new BuilderFacade(builder) : new JTreeFacade(tree);
379 static class JTreeFacade implements TreeFacade {
381 private JTree myTree;
383 JTreeFacade(JTree tree) {
384 myTree = tree;
387 public ActionCallback expand(DefaultMutableTreeNode node) {
388 myTree.expandPath(new TreePath(node.getPath()));
389 return new ActionCallback.Done();
392 public ActionCallback getIntialized() {
393 return new ActionCallback.Done();
397 static class BuilderFacade implements TreeFacade {
399 private AbstractTreeBuilder myBuilder;
401 BuilderFacade(AbstractTreeBuilder builder) {
402 myBuilder = builder;
405 public ActionCallback getIntialized() {
406 return myBuilder.getIntialized();
409 public ActionCallback expand(DefaultMutableTreeNode node) {
410 final Object userObject = node.getUserObject();
411 if (!(userObject instanceof NodeDescriptor)) return new ActionCallback.Rejected();
413 NodeDescriptor desc = (NodeDescriptor)userObject;
415 final Object element = myBuilder.getTreeStructureElement(desc);
417 final ActionCallback result = new ActionCallback();
419 myBuilder.expand(element, new Runnable() {
420 public void run() {
421 result.setDone();
425 return result;
429 public void setScrollToSelection(boolean scrollToSelection) {
430 myScrollToSelection = scrollToSelection;