Merge branch 'tree-ui'

This commit is contained in:
Kirill Kalishev
2009-10-08 19:13:22 +04:00
8 changed files with 195 additions and 120 deletions
@@ -315,10 +315,14 @@ public class AbstractTreeBuilder implements Disposable {
UIUtil.invokeLaterIfNeeded(runnable);
}
public final ActionCallback getReady() {
public final ActionCallback getIntialized() {
return myUi.getInitialized();
}
public final ActionCallback getReady(Object requestor) {
return myUi.getReady(requestor);
}
protected void sortChildren(Comparator<TreeNode> nodeComparator, DefaultMutableTreeNode node, ArrayList<TreeNode> children) {
Collections.sort(children, nodeComparator);
}
@@ -42,7 +42,7 @@ import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
class AbstractTreeUi {
public class AbstractTreeUi {
private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.treeView.AbstractTreeBuilder");
protected JTree myTree;// protected for TestNG
@SuppressWarnings({"WeakerAccess"}) protected DefaultTreeModel myTreeModel;
@@ -128,6 +128,7 @@ class AbstractTreeUi {
private Set<DefaultMutableTreeNode> myNotForSmartExpand = new HashSet<DefaultMutableTreeNode>();
private TreePath myRequestedExpand;
private ActionCallback myInitialized = new ActionCallback();
private Map<Object, ActionCallback> myReadyCallbacks = new WeakHashMap<Object, ActionCallback>();
protected final void init(AbstractTreeBuilder builder,
JTree tree,
@@ -1102,7 +1103,7 @@ class AbstractTreeUi {
}
private boolean hasSheduledUpdates() {
return getUpdater().hasNodesToUpdate() || isLoadingInBackground();
return getUpdater().hasNodesToUpdate() || isLoadingInBackgroundNow();
}
public boolean isReady() {
@@ -1130,15 +1131,23 @@ class AbstractTreeUi {
if (isReleased()) return;
if (isReady()) {
if (myTree.isShowing()) {
if (myTree.isShowing() || myUpdateIfInactive) {
myInitialized.setDone();
}
if (myTree.isShowing()) {
if (isReady()) {
if (getBuilder().isToEnsureSelectionOnFocusGained() && Registry.is("ide.tree.ensureSelectionOnFocusGained")) {
TreeUtil.ensureSelection(myTree);
}
}
}
if (myInitialized.isDone()) {
for (ActionCallback each : getReadyCallbacks(true)) {
each.setDone();
}
}
}
}
@@ -1288,6 +1297,14 @@ class AbstractTreeUi {
return myInitialized;
}
public ActionCallback getReady(Object requestor) {
if (isReady()) {
return new ActionCallback.Done();
} else {
return addReadyCallback(requestor);
}
}
private void addToUpdating(DefaultMutableTreeNode node) {
synchronized (myUpdatingChildren) {
myUpdatingChildren.add(node);
@@ -1306,6 +1323,12 @@ class AbstractTreeUi {
}
}
boolean hasUpdatingNow() {
synchronized (myUpdatingChildren) {
return myUpdatingChildren.size() > 0;
}
}
public Map getNodeActions() {
return myNodeActions;
}
@@ -1326,6 +1349,10 @@ class AbstractTreeUi {
return result;
}
public boolean hasNodesToUpdate() {
return getUpdater().hasNodesToUpdate() || hasUpdatingNow() || isLoadingInBackgroundNow();
}
static class ElementNode extends DefaultMutableTreeNode {
Set<Object> myElements = new HashSet<Object>();
@@ -1393,7 +1420,7 @@ class AbstractTreeUi {
}
}
private boolean isLoadingInBackground() {
private boolean isLoadingInBackgroundNow() {
synchronized (myLoadingParents) {
return myLoadingParents.size() > 0;
}
@@ -1739,25 +1766,7 @@ class AbstractTreeUi {
removeNodeFromParent(childNode, selectedIndex >= 0);
disposeNode(childNode);
if (selectedIndex >= 0) {
if (parentNode.getChildCount() > 0) {
if (parentNode.getChildCount() > selectedIndex) {
TreeNode newChildNode = parentNode.getChildAt(selectedIndex);
if (isValidForSelectionAdjusting(newChildNode)) {
addSelectionPath(new TreePath(myTreeModel.getPathToRoot(newChildNode)), true, getExpiredElementCondition(disposedElement));
}
}
else {
TreeNode newChild = parentNode.getChildAt(parentNode.getChildCount() - 1);
if (isValidForSelectionAdjusting(newChild)) {
addSelectionPath(new TreePath(myTreeModel.getPathToRoot(newChild)), true, getExpiredElementCondition(disposedElement));
}
}
}
else {
addSelectionPath(new TreePath(myTreeModel.getPathToRoot(parentNode)), true, getExpiredElementCondition(disposedElement));
}
}
adjustSelectionOnChildRemove(parentNode, selectedIndex, disposedElement);
}
else {
elementToIndexMap.remove(getBuilder().getTreeStructureElement(childDesc));
@@ -1771,7 +1780,39 @@ class AbstractTreeUi {
return new ActionCallback.Done();
}
private void adjustSelectionOnChildRemove(DefaultMutableTreeNode parentNode, int selectedIndex, Object disposedElement) {
DefaultMutableTreeNode node = getNodeForElement(disposedElement, false);
if (node != null && isValidForSelectionAdjusting(node)) {
Object newElement = getElementFor(node);
addSelectionPath(getPathFor(node), true, getExpiredElementCondition(newElement));
return;
}
if (selectedIndex >= 0) {
if (parentNode.getChildCount() > 0) {
if (parentNode.getChildCount() > selectedIndex) {
TreeNode newChildNode = parentNode.getChildAt(selectedIndex);
if (isValidForSelectionAdjusting(newChildNode)) {
addSelectionPath(new TreePath(myTreeModel.getPathToRoot(newChildNode)), true, getExpiredElementCondition(disposedElement));
}
}
else {
TreeNode newChild = parentNode.getChildAt(parentNode.getChildCount() - 1);
if (isValidForSelectionAdjusting(newChild)) {
addSelectionPath(new TreePath(myTreeModel.getPathToRoot(newChild)), true, getExpiredElementCondition(disposedElement));
}
}
}
else {
addSelectionPath(new TreePath(myTreeModel.getPathToRoot(parentNode)), true, getExpiredElementCondition(disposedElement));
}
}
}
private boolean isValidForSelectionAdjusting(TreeNode node) {
if (!myTree.isRootVisible() && getRootNode() == node) return false;
if (isLoadingNode(node)) return true;
final Object elementInTree = getElementFor(node);
@@ -2335,7 +2376,14 @@ class AbstractTreeUi {
if (!addToSelection) {
myTree.clearSelection();
}
addNext(elementsToSelect, 0, onDone, originalRows, deferred, scrollToVisible, canSmartExpand);
addNext(elementsToSelect, 0, new Runnable() {
public void run() {
if (getTree().isSelectionEmpty()) {
restoreSelection(currentElements);
}
runDone(onDone);
}
}, originalRows, deferred, scrollToVisible, canSmartExpand);
}
else {
addToDeferred(elementsToSelect, onDone);
@@ -2344,6 +2392,16 @@ class AbstractTreeUi {
});
}
private void restoreSelection(Set<Object> selection) {
for (Object each : selection) {
DefaultMutableTreeNode node = getNodeForElement(each, false);
if (node != null && isValidForSelectionAdjusting(node)) {
addSelectionPath(getPathFor(node), false, null);
}
}
}
private void addToDeferred(final Object[] elementsToSelect, final Runnable onDone) {
myDeferredSelections.clear();
myDeferredSelections.add(new Runnable() {
@@ -2437,6 +2495,7 @@ class AbstractTreeUi {
private void selectVisible(Object element, final Runnable onDone, boolean addToSelection, boolean canBeCentered, final boolean scroll) {
final DefaultMutableTreeNode toSelect = getNodeForElement(element, false);
if (toSelect == null) {
runDone(onDone);
return;
@@ -2663,6 +2722,22 @@ class AbstractTreeUi {
}
private String asString(DefaultMutableTreeNode node) {
if (node == null) return null;
StringBuffer children = new StringBuffer(node.toString());
children.append(" [");
for (int i = 0; i < node.getChildCount(); i++) {
children.append(node.getChildAt(i));
if (i < node.getChildCount() - 1) {
children.append(",");
}
}
children.append("]");
return children.toString();
}
@Nullable
private Object getElementFor(Object node) {
if (!(node instanceof DefaultMutableTreeNode)) return null;
@@ -3083,4 +3158,27 @@ class AbstractTreeUi {
UpdaterTreeState getUpdaterState() {
return myUpdaterState;
}
private ActionCallback addReadyCallback(Object requestor) {
synchronized (myReadyCallbacks) {
ActionCallback cb = myReadyCallbacks.get(requestor);
if (cb == null) {
cb = new ActionCallback();
myReadyCallbacks.put(requestor, cb);
}
return cb;
}
}
private ActionCallback[] getReadyCallbacks(boolean clear) {
synchronized (myReadyCallbacks) {
ActionCallback[] result = myReadyCallbacks.values().toArray(new ActionCallback[myReadyCallbacks.size()]);
if (clear) {
myReadyCallbacks.clear();
}
return result;
}
}
}
@@ -83,7 +83,7 @@ public class AbstractTreeUpdater implements Disposable, Activatable {
}
public boolean hasNodesToUpdate() {
return myNodeQueue.size() > 0 || !myUpdateQueue.isEmpty();
return myNodeQueue.size() > 0;
}
public void dispose() {
@@ -229,7 +229,7 @@ public class TreeState implements JDOMExternalizable {
}
private void applyExpanded(final TreeFacade tree, final Object root) {
tree.getReady().doWhenDone(new Runnable() {
tree.getIntialized().doWhenDone(new Runnable() {
public void run() {
_applyExpanded(tree, root);
}
@@ -360,7 +360,7 @@ public class TreeState implements JDOMExternalizable {
}
interface TreeFacade {
ActionCallback getReady();
ActionCallback getIntialized();
ActionCallback expand(DefaultMutableTreeNode node);
}
@@ -382,7 +382,7 @@ public class TreeState implements JDOMExternalizable {
return new ActionCallback.Done();
}
public ActionCallback getReady() {
public ActionCallback getIntialized() {
return new ActionCallback.Done();
}
}
@@ -395,8 +395,8 @@ public class TreeState implements JDOMExternalizable {
myBuilder = builder;
}
public ActionCallback getReady() {
return myBuilder.getReady();
public ActionCallback getIntialized() {
return myBuilder.getIntialized();
}
public ActionCallback expand(DefaultMutableTreeNode node) {
@@ -124,30 +124,12 @@ public class UpdaterTreeState {
}
public boolean restore(@Nullable DefaultMutableTreeNode actionNode) {
if (isProcessingNow() || !myCanRunRestore) return false;
if (isProcessingNow() || !myCanRunRestore || myUi.hasNodesToUpdate()) return false;
invalidateToSelectWithRefsToParent(actionNode);
myProcessingNow = true;
if (actionNode != null) {
Object readyElement = myUi.getElementFor(actionNode);
if (readyElement != null) {
Iterator<Object> toSelect = myToSelect.keySet().iterator();
while (toSelect.hasNext()) {
Object eachToSelect = toSelect.next();
if (readyElement.equals(myUi.getTreeStructure().getParentElement(eachToSelect))) {
List<Object> children = myUi.getLoadedChildrenFor(readyElement);
if (!children.contains(eachToSelect)) {
toSelect.remove();
if (!myToSelect.containsKey(readyElement)) {
addAdjustedSelection(eachToSelect, Condition.FALSE);
}
}
}
}
}
}
final Object[] toSelect = getToSelect();
final Object[] toExpand = getToExpand();
@@ -190,6 +172,27 @@ public class UpdaterTreeState {
return true;
}
private void invalidateToSelectWithRefsToParent(DefaultMutableTreeNode actionNode) {
if (actionNode != null) {
Object readyElement = myUi.getElementFor(actionNode);
if (readyElement != null) {
Iterator<Object> toSelect = myToSelect.keySet().iterator();
while (toSelect.hasNext()) {
Object eachToSelect = toSelect.next();
if (readyElement.equals(myUi.getTreeStructure().getParentElement(eachToSelect))) {
List<Object> children = myUi.getLoadedChildrenFor(readyElement);
if (!children.contains(eachToSelect)) {
toSelect.remove();
if (!myToSelect.containsKey(readyElement) && !myUi.getSelectedElements().contains(eachToSelect)) {
addAdjustedSelection(eachToSelect, Condition.FALSE);
}
}
}
}
}
}
}
void beforeSubtreeUpdate() {
myCanRunRestore = true;
}
@@ -204,6 +207,8 @@ public class UpdaterTreeState {
successfulSelections.retainAll(selected);
wasFullyRejected = successfulSelections.size() == 0;
} else if (selected.size() == 0 && originallySelected.size() == 0) {
wasFullyRejected = true;
}
if (wasFullyRejected && selected.size() > 0) return;
@@ -6,16 +6,17 @@ import com.intellij.ide.util.treeView.NodeDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Ref;
import com.intellij.ui.speedSearch.ElementFilter;
import com.intellij.ui.treeStructure.*;
import com.intellij.ui.treeStructure.PatchedDefaultMutableTreeNode;
import com.intellij.ui.treeStructure.SimpleTree;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.ui.update.MergingUpdateQueue;
import com.intellij.util.ui.update.Update;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.*;
import java.util.Comparator;
public class FilteringTreeBuilder extends AbstractTreeBuilder {
@@ -102,81 +103,42 @@ public class FilteringTreeBuilder extends AbstractTreeBuilder {
}
}
private ActionCallback refilterNow(Object preferredSelection, final boolean adjustSelection) {
private ActionCallback refilterNow(final Object preferredSelection, final boolean adjustSelection) {
final ActionCallback result = new ActionCallback();
Object selectedObject = getSelected();
final Ref<Object> toSelect = new Ref<Object>(isSelectable(selectedObject) ? selectedObject : null);
if (preferredSelection != null) {
toSelect.set(preferredSelection);
}
((FilteringTreeStructure)getTreeStructure()).refilter();
getFilteredStructure().refilter();
updateFromRoot();
result.setDone();
getReady(this).doWhenDone(new Runnable() {
public void run() {
Object toSelect = preferredSelection != null ? preferredSelection : myLastSuccessfulSelect;
if (adjustSelection) {
boolean wasSelected = false;
if (toSelect.get() != null && isSelectable(toSelect.get()) && isSimpleTree()) {
wasSelected = ((SimpleTree)myTree).select(this, new SimpleNodeVisitor() {
public boolean accept(SimpleNode simpleNode) {
if (simpleNode instanceof FilteringTreeStructure.Node) {
FilteringTreeStructure.Node node = (FilteringTreeStructure.Node)simpleNode;
return node.getDelegate().equals(toSelect.get());
}
else {
return false;
}
}
}, true);
}
if (adjustSelection && toSelect != null) {
final FilteringTreeStructure.Node nodeToSelect = getFilteredStructure().getVisibleNodeFor(toSelect);
if (!wasSelected && isSimpleTree()) {
((SimpleTree)myTree).select(this, new SimpleNodeVisitor() {
public boolean accept(SimpleNode simpleNode) {
if (simpleNode instanceof FilteringTreeStructure.Node) {
final boolean isRoot = getTreeStructure().getRootElement() == simpleNode;
if (isRoot && !myTree.isRootVisible()) return false;
FilteringTreeStructure.Node node = (FilteringTreeStructure.Node)simpleNode;
if (isSelectable(node.getDelegate())) {
return true;
if (nodeToSelect != null) {
select(nodeToSelect, new Runnable() {
public void run() {
if (getSelectedElements().contains(nodeToSelect)) {
myLastSuccessfulSelect = getOriginalNode(nodeToSelect);
}
result.setDone();
}
}
else {
return false;
}
return false;
});
}
}, true);
}
if (!wasSelected && myLastSuccessfulSelect != null && isSimpleTree()) {
wasSelected = ((SimpleTree)myTree).select(this, new SimpleNodeVisitor() {
public boolean accept(SimpleNode simpleNode) {
if (simpleNode instanceof FilteringTreeStructure.Node) {
Object object = ((FilteringTreeStructure.Node)simpleNode).getDelegate();
return myLastSuccessfulSelect.equals(object);
}
return false;
}
}, true);
if (wasSelected) {
myLastSuccessfulSelect = getSelected();
} else {
result.setDone();
}
}
else if (wasSelected) {
myLastSuccessfulSelect = getSelected();
}
}
});
return result;
}
private FilteringTreeStructure getFilteredStructure() {
return ((FilteringTreeStructure)getTreeStructure());
}
//todo kirillk
private boolean isSimpleTree() {
return myTree instanceof SimpleTree;
@@ -194,7 +156,7 @@ public class FilteringTreeBuilder extends AbstractTreeBuilder {
}
public FilteringTreeStructure.Node getVisibleNodeFor(Object nodeObject) {
return ((FilteringTreeStructure)getTreeStructure()).getVisibleNodeFor(nodeObject);
return getFilteredStructure().getVisibleNodeFor(nodeObject);
}
public Object getOriginalNode(Object node) {
@@ -79,6 +79,11 @@ public class FilteringTreeStructure extends AbstractTreeStructure {
return myDelegate;
}
@Override
public String toString() {
return String.valueOf(getDelegate());
}
@Override
public boolean isContentHighlighted() {
if (myDelegate instanceof SimpleNode) {
@@ -152,7 +157,7 @@ public class FilteringTreeStructure extends AbstractTreeStructure {
}
public Object[] getEqualityObjects() {
return NONE;
return new Object[] {myDelegate};
}
}
@@ -20,6 +20,7 @@ import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.util.Disposer;
import com.intellij.util.Alarm;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
@@ -227,7 +228,7 @@ public class MergingUpdateQueue implements Runnable, Disposable, Activatable {
};
if (myExecuteInDispatchThread && invokeLaterIfNotDispatch && !ApplicationManager.getApplication().isDispatchThread()) {
ApplicationManager.getApplication().invokeLater(toRun, ModalityState.NON_MODAL);
UIUtil.invokeLaterIfNeeded(toRun);
}
else {
toRun.run();