/* * Copyright 2003-2015 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 jetbrains.mps.ide.projectPane.logicalview; import jetbrains.mps.ide.ThreadUtils; import jetbrains.mps.ide.ui.tree.MPSTree; import jetbrains.mps.ide.ui.tree.MPSTreeNode; import jetbrains.mps.ide.ui.tree.smodel.SNodeTreeNode; import jetbrains.mps.project.Project; import jetbrains.mps.smodel.DependencyRecorder; import jetbrains.mps.smodel.ModelReadRunnable; import jetbrains.mps.smodel.SNodeUtil; import jetbrains.mps.smodel.event.SModelChildEvent; import jetbrains.mps.smodel.event.SModelEvent; import jetbrains.mps.smodel.event.SModelEventVisitorAdapter; import jetbrains.mps.smodel.event.SModelPropertyEvent; import jetbrains.mps.smodel.event.SModelReferenceEvent; import jetbrains.mps.smodel.event.SModelRootEvent; import jetbrains.mps.util.IterableUtil; import org.jetbrains.mps.openapi.model.SModel; import org.jetbrains.mps.openapi.model.SNode; import javax.swing.tree.DefaultTreeModel; import java.util.Enumeration; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; public abstract class SNodeTreeUpdater<T extends MPSTreeNode> { protected final Project myProject; protected T myTreeNode; private DependencyRecorder<SNodeTreeNode> myDependencyRecorder; public SNodeTreeUpdater(Project project, T treeNode) { myProject = project; myTreeNode = treeNode; } public void setDependencyRecorder(DependencyRecorder recorder) { myDependencyRecorder = recorder; } public abstract SModel getSModelDescriptor(); public abstract void updateNodesWithChangedPackages(Set<SNode> nodesWithChangedPackages); public abstract void addAndRemoveRoots(Set<SNode> removedRoots, Set<SNode> addedRoots); protected MPSTree getTree() { return myTreeNode.getTree(); } protected boolean showPropertiesAndReferences() { return false; } public void addAndRemoveVisibleChildren(Set<SNode> removedNodes, Set<SNode> addedNodes) { if (getTree() == null) return; DefaultTreeModel treeModel = (DefaultTreeModel) getTree().getModel(); for (SNode removed : removedNodes) { SNodeTreeNode node = (SNodeTreeNode) myTreeNode.findDescendantWith(removed.getNodeId().toString()); if (node == null) continue; treeModel.removeNodeFromParent(node); } outer: for (SNode added : addedNodes) { if (added.getModel() == null) continue; if (added.getParent() == null) continue; SNodeTreeNode parent = (SNodeTreeNode) myTreeNode.findDescendantWith(added.getParent().getNodeId().toString()); if (parent == null) continue; if (!parent.isInitialized()) continue; SNode parentNode = parent.getSNode(); int indexof = IterableUtil.asList(parentNode.getChildren()).indexOf(added); Enumeration children = parent.children(); while(children.hasMoreElements()){ Object child = children.nextElement(); if (!(child instanceof SNodeTreeNode)) continue; SNodeTreeNode childNode = (SNodeTreeNode) child; int index = IterableUtil.asList(parentNode.getChildren()).indexOf(childNode.getSNode()); if (index <= indexof) continue; SNodeTreeNode newTreeNode = new SNodeTreeNode(added, added.getRoleInParent()); treeModel.insertNodeInto(newTreeNode, parent, treeModel.getIndexOfChild(parent, childNode)); continue outer; } treeModel.insertNodeInto(new SNodeTreeNode(added, added.getRoleInParent()), parent, parent.getChildCount()); } } public void updateChangedPresentations(Set<SNode> nodesWithChangedPresentations) { if (getTree() == null) return; DefaultTreeModel treeModel = (DefaultTreeModel) getTree().getModel(); for (SNode node : nodesWithChangedPresentations) { SNodeTreeNode treeNode = (SNodeTreeNode) myTreeNode.findDescendantWith(node.getNodeId().toString()); if (treeNode == null) continue; if (node.getModel() != null && node.getParent() == null) { MPSTreeNode parentTreeNode = (MPSTreeNode) treeNode.getParent(); int currentIndex = parentTreeNode.getIndex(treeNode); int newIndex = -1; for (int i = 0; i < parentTreeNode.getChildCount(); i++) { if (i == currentIndex) continue; if (!(parentTreeNode.getChildAt(i) instanceof SNodeTreeNode)) continue; SNodeTreeNode child = (SNodeTreeNode) parentTreeNode.getChildAt(i); String rp = node.toString(); String cp = child.getSNode().toString(); if (rp.compareTo(cp) < 0) { newIndex = i; if (newIndex > currentIndex) { newIndex--; } break; } } if (newIndex == -1) { newIndex = parentTreeNode.getChildCount() - 1; } if (currentIndex != newIndex) { treeModel.removeNodeFromParent(treeNode); treeModel.insertNodeInto(treeNode, parentTreeNode, newIndex); } } if (treeNode.isInitialized() && showPropertiesAndReferences() && treeNode.getChildCount() > 0) { MPSTreeNode propsNode = (MPSTreeNode) treeNode.getChildAt(0); propsNode.update(); propsNode.init(); } treeNode.renewPresentation(); } } public void updateChangedRefs(Set<SNode> nodesWithChangedRefs) { if (!showPropertiesAndReferences()) return; for (SNode sourceNode : nodesWithChangedRefs) { MPSTreeNode nodeTreeNode = myTreeNode.findDescendantWith(sourceNode.getNodeId().toString()); if (nodeTreeNode == null || !nodeTreeNode.isInitialized() || nodeTreeNode.getChildCount() < 2) return; MPSTreeNode refsNode = (MPSTreeNode) nodeTreeNode.getChildAt(1); refsNode.update(); refsNode.init(); } } public void updateAncestorsPresentationInTree() { myTreeNode.updateAncestorsPresentationInTree(); } public void eventsHappenedInCommand(final List<SModelEvent> events) { final Runnable action = new UpdateRunnable(events); ThreadUtils.runInUIThreadNoWait(new ModelReadRunnable(myProject.getModelAccess(), action)); } private class UpdateRunnable implements Runnable { private final List<SModelEvent> myEvents; public UpdateRunnable(List<SModelEvent> events) { myEvents = events; } @Override public void run() { if (myProject.isDisposed()) return; final Set<SNode> addedRoots = new LinkedHashSet<SNode>(); final Set<SNode> removedRoots = new LinkedHashSet<SNode>(); final Set<SNode> addedNodes = new LinkedHashSet<SNode>(); final Set<SNode> removedNodes = new LinkedHashSet<SNode>(); final Set<SNode> nodesWithChangedPresentations = new LinkedHashSet<SNode>(); final Set<SNode> nodesWithChangedPackages = new LinkedHashSet<SNode>(); final Set<SNode> nodesWithChangedRefs = new LinkedHashSet<SNode>(); final Set<SNode> changedNodes = new LinkedHashSet<SNode>(); for (SModelEvent event : myEvents) { event.accept(new SModelEventVisitorAdapter() { @Override public void visitRootEvent(SModelRootEvent event) { changedNodes.add(event.getRoot()); if (event.isAdded()) { addedRoots.add(event.getRoot()); removedRoots.remove(event.getRoot()); } if (event.isRemoved()) { removedRoots.add(event.getRoot()); addedRoots.remove(event.getRoot()); } } @Override public void visitChildEvent(SModelChildEvent event) { changedNodes.add(event.getParent()); changedNodes.add(event.getChild()); if (event.isAdded()) { addedNodes.add(event.getChild()); } if (event.isRemoved()) { removedNodes.add(event.getChild()); } } @Override public void visitPropertyEvent(SModelPropertyEvent event) { changedNodes.add(event.getNode()); nodesWithChangedPresentations.add(event.getNode()); if (SNodeUtil.property_BaseConcept_virtualPackage.equals(event.getProperty()) && event.getNode().getModel() != null && event.getNode().getParent() == null) { nodesWithChangedPackages.add(event.getNode()); } } @Override public void visitReferenceEvent(SModelReferenceEvent event) { changedNodes.add(event.getReference().getSourceNode()); nodesWithChangedRefs.add(event.getReference().getSourceNode()); } }); } Set<SNodeTreeNode> treeNodesToUpdate = new LinkedHashSet<SNodeTreeNode>(); for (SNode changedNode : changedNodes) { if (myDependencyRecorder != null) { treeNodesToUpdate.addAll(myDependencyRecorder.getDependOn(changedNode)); } } for (SNodeTreeNode n : treeNodesToUpdate) { nodesWithChangedPresentations.add(n.getSNode()); } addAndRemoveRoots(removedRoots, addedRoots); addAndRemoveVisibleChildren(removedNodes, addedNodes); updateChangedPresentations(nodesWithChangedPresentations); updateChangedRefs(nodesWithChangedRefs); updateNodesWithChangedPackages(nodesWithChangedPackages); updateAncestorsPresentationInTree(); } } }