IDEADEV-41145 On deleting a file two packages are selected in the Project tree
[fedora-idea.git] / platform / platform-api / src / com / intellij / ide / util / treeView / UpdaterTreeState.java
blob885d45b5e4a327015dd1af87bf5ab2bf76066b2e
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.openapi.util.ActionCallback;
19 import com.intellij.openapi.util.Condition;
20 import com.intellij.util.ArrayUtil;
21 import com.intellij.util.Function;
22 import org.jetbrains.annotations.Nullable;
24 import javax.swing.*;
25 import javax.swing.tree.DefaultMutableTreeNode;
26 import javax.swing.tree.TreePath;
27 import java.util.*;
29 public class UpdaterTreeState {
31 private final AbstractTreeUi myUi;
32 protected WeakHashMap<Object, Object> myToSelect = new WeakHashMap<Object, Object>();
33 protected WeakHashMap<Object, Condition> myAdjustedSelection = new WeakHashMap<Object, Condition>();
34 protected WeakHashMap<Object, Object> myDisposedElements = new WeakHashMap<Object, Object>();
35 protected WeakHashMap<Object, Object> myToExpand = new WeakHashMap<Object, Object>();
36 private int myProcessingCount;
38 private boolean myCanRunRestore = true;
40 private WeakHashMap<Object, Object> myAdjustmentCause2Adjustment = new WeakHashMap<Object, Object>();
42 public UpdaterTreeState(AbstractTreeUi ui) {
43 myUi = ui;
45 final JTree tree = myUi.getTree();
46 putAll(addPaths(tree.getSelectionPaths()), myToSelect);
47 putAll(addPaths(tree.getExpandedDescendants(new TreePath(tree.getModel().getRoot()))), myToExpand);
50 private static void putAll(final Set<Object> source, final Map<Object, Object> target) {
51 for (Object o : source) {
52 target.put(o, o);
56 private Set<Object> addPaths(Object[] elements) {
57 Set<Object> set = new HashSet<Object>();
58 if (elements != null) {
59 set.addAll(Arrays.asList(elements));
62 return addPaths(set);
65 private Set<Object> addPaths(Enumeration elements) {
66 ArrayList<Object> elementArray = new ArrayList<Object>();
67 if (elements != null) {
68 while (elements.hasMoreElements()) {
69 Object each = elements.nextElement();
70 elementArray.add(each);
74 return addPaths(elementArray);
77 private Set<Object> addPaths(Collection elements) {
78 Set<Object> target = new HashSet<Object>();
80 if (elements != null) {
81 for (Object each : elements) {
82 final Object node = ((TreePath)each).getLastPathComponent();
83 if (node instanceof DefaultMutableTreeNode) {
84 final Object descriptor = ((DefaultMutableTreeNode)node).getUserObject();
85 if (descriptor instanceof NodeDescriptor) {
86 final Object element = myUi.getElementFromDescriptor((NodeDescriptor)descriptor);
87 if (element != null) {
88 target.add(element);
94 return target;
97 public Object[] getToSelect() {
98 return myToSelect.keySet().toArray(new Object[myToSelect.size()]);
101 public Object[] getToExpand() {
102 return myToExpand.keySet().toArray(new Object[myToExpand.size()]);
105 public boolean process(Runnable runnable) {
106 try {
107 setProcessingNow(true);
108 runnable.run();
110 finally {
111 setProcessingNow(false);
114 return isEmpty();
117 public boolean isEmpty() {
118 return myToExpand.isEmpty() && myToSelect.isEmpty() && myAdjustedSelection.isEmpty();
122 public boolean isProcessingNow() {
123 return myProcessingCount > 0;
126 public void addAll(final UpdaterTreeState state) {
127 myToExpand.putAll(state.myToExpand);
129 Object[] toSelect = state.getToSelect();
130 for (Object each : toSelect) {
131 if (!myAdjustedSelection.containsKey(each)) {
132 myToSelect.put(each, each);
136 myCanRunRestore = state.myCanRunRestore;
139 public boolean restore(@Nullable DefaultMutableTreeNode actionNode) {
140 if (isProcessingNow() || !myCanRunRestore || myUi.hasNodesToUpdate()) return false;
142 invalidateToSelectWithRefsToParent(actionNode);
144 setProcessingNow(true);
146 final Object[] toSelect = getToSelect();
147 final Object[] toExpand = getToExpand();
150 final Map<Object, Condition> adjusted = new WeakHashMap<Object, Condition>();
151 adjusted.putAll(myAdjustedSelection);
153 clearSelection();
154 clearExpansion();
156 final Set<Object> originallySelected = myUi.getSelectedElements();
158 myUi._select(toSelect, new Runnable() {
159 public void run() {
160 processUnsuccessfulSelections(toSelect, new Function<Object, Object>() {
161 public Object fun(final Object o) {
162 if (myUi.getTree().isRootVisible() || !myUi.getTreeStructure().getRootElement().equals(o)) {
163 addSelection(o);
165 return o;
167 }, originallySelected);
169 processAjusted(adjusted, originallySelected).doWhenDone(new Runnable() {
170 public void run() {
171 myUi.expand(toExpand, new Runnable() {
172 public void run() {
173 myUi.clearUpdaterState();
174 setProcessingNow(false);
176 }, true);
180 }, false, true, true, false);
182 return true;
185 private void invalidateToSelectWithRefsToParent(DefaultMutableTreeNode actionNode) {
186 if (actionNode != null) {
187 Object readyElement = myUi.getElementFor(actionNode);
188 if (readyElement != null) {
189 Iterator<Object> toSelect = myToSelect.keySet().iterator();
190 while (toSelect.hasNext()) {
191 Object eachToSelect = toSelect.next();
192 if (readyElement.equals(myUi.getTreeStructure().getParentElement(eachToSelect))) {
193 List<Object> children = myUi.getLoadedChildrenFor(readyElement);
194 if (!children.contains(eachToSelect)) {
195 toSelect.remove();
196 if (!myToSelect.containsKey(readyElement) && !myUi.getSelectedElements().contains(eachToSelect)) {
197 addAdjustedSelection(eachToSelect, Condition.FALSE, null);
206 void beforeSubtreeUpdate() {
207 myCanRunRestore = true;
210 private void processUnsuccessfulSelections(final Object[] toSelect, Function<Object, Object> restore, Set<Object> originallySelected) {
211 final Set<Object> selected = myUi.getSelectedElements();
213 boolean wasFullyRejected = false;
214 if (toSelect.length > 0 && selected.size() > 0 && !originallySelected.containsAll(selected)) {
215 final Set<Object> successfulSelections = new HashSet<Object>();
216 successfulSelections.addAll(Arrays.asList(toSelect));
218 successfulSelections.retainAll(selected);
219 wasFullyRejected = successfulSelections.size() == 0;
220 } else if (selected.size() == 0 && originallySelected.size() == 0) {
221 wasFullyRejected = true;
224 if (wasFullyRejected && selected.size() > 0) return;
226 for (Object eachToSelect : toSelect) {
227 if (!selected.contains(eachToSelect)) {
228 restore.fun(eachToSelect);
233 private ActionCallback processAjusted(final Map<Object, Condition> adjusted, final Set<Object> originallySelected) {
234 final ActionCallback result = new ActionCallback();
236 final Set<Object> allSelected = myUi.getSelectedElements();
238 Set<Object> toSelect = new HashSet<Object>();
239 for (Object each : adjusted.keySet()) {
240 if (adjusted.get(each).value(each)) continue;
242 for (final Object eachSelected : allSelected) {
243 if (isParentOrSame(each, eachSelected)) continue;
244 toSelect.add(each);
246 if (allSelected.size() == 0) {
247 toSelect.add(each);
251 final Object[] newSelection = ArrayUtil.toObjectArray(toSelect);
253 if (newSelection.length > 0) {
254 myUi._select(newSelection, new Runnable() {
255 public void run() {
256 final Set<Object> hangByParent = new HashSet<Object> ();
257 processUnsuccessfulSelections(newSelection, new Function<Object, Object>() {
258 public Object fun(final Object o) {
259 if (myUi.isInStructure(o) && !adjusted.get(o).value(o)) {
260 hangByParent.add(o);
261 } else {
262 addAdjustedSelection(o, adjusted.get(o), null);
264 return null;
266 }, originallySelected);
268 processHangByParent(hangByParent).notify(result);
270 }, true, true, true);
271 } else {
272 result.setDone();
275 return result;
278 private ActionCallback processHangByParent(Set<Object> elements) {
279 if (elements.size() == 0) return new ActionCallback.Done();
281 ActionCallback result = new ActionCallback(elements.size());
282 for (Iterator<Object> iterator = elements.iterator(); iterator.hasNext();) {
283 Object hangElement = iterator.next();
284 if (!myAdjustmentCause2Adjustment.containsKey(hangElement)) {
285 processHangByParent(hangElement).notify(result);
286 } else {
287 result.setDone();
290 return result;
293 private ActionCallback processHangByParent(Object each) {
294 ActionCallback result = new ActionCallback();
295 processNextHang(each, result);
296 return result;
299 private void processNextHang(Object element, final ActionCallback callback) {
300 if (element == null || myUi.getSelectedElements().contains(element)) {
301 callback.setDone();
302 } else {
303 final Object nextElement = myUi.getTreeStructure().getParentElement(element);
304 if (nextElement == null) {
305 callback.setDone();
306 } else {
307 myUi.select(nextElement, new Runnable() {
308 public void run() {
309 processNextHang(nextElement, callback);
311 }, true);
316 private boolean isParentOrSame(Object parent, Object child) {
317 Object eachParent = child;
318 while (eachParent != null) {
319 if (parent.equals(eachParent)) return true;
320 eachParent = myUi.getTreeStructure().getParentElement(eachParent);
323 return false;
326 public void clearExpansion() {
327 myToExpand.clear();
330 public void clearSelection() {
331 myToSelect.clear();
332 myAdjustedSelection = new WeakHashMap<Object, Condition>();
335 public void addSelection(final Object element) {
336 myToSelect.put(element, element);
339 public void addAdjustedSelection(final Object element, Condition isExpired, @Nullable Object adjustmentCause) {
340 myAdjustedSelection.put(element, isExpired);
341 if (adjustmentCause != null) {
342 myAdjustmentCause2Adjustment.put(adjustmentCause, element);
346 @Override
347 public String toString() {
348 return "UpdaterState toSelect" + Arrays.asList(myToSelect) + " toExpand=" + Arrays.asList(myToExpand) + " processingNow=" + isProcessingNow() + " canRun=" + myCanRunRestore;
351 public void setProcessingNow(boolean processingNow) {
352 if (processingNow) {
353 myProcessingCount++;
354 } else {
355 myProcessingCount--;
357 if (!isProcessingNow()) {
358 myUi.maybeReady();
362 public void removeFromSelection(Object element) {
363 myToSelect.remove(element);
364 myAdjustedSelection.remove(element);