IDEADEV-41812 - New project view: File selection is lost after rename
[fedora-idea.git] / platform / platform-api / src / com / intellij / ide / util / treeView / UpdaterTreeState.java
blob31b5f1b70ef1e5dabf809e51d6e2415ca02d7a6f
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 this(ui, false);
46 public UpdaterTreeState(AbstractTreeUi ui, boolean isEmpty) {
47 myUi = ui;
49 if (!isEmpty) {
50 final JTree tree = myUi.getTree();
51 putAll(addPaths(tree.getSelectionPaths()), myToSelect);
52 putAll(addPaths(tree.getExpandedDescendants(new TreePath(tree.getModel().getRoot()))), myToExpand);
56 private static void putAll(final Set<Object> source, final Map<Object, Object> target) {
57 for (Object o : source) {
58 target.put(o, o);
62 private Set<Object> addPaths(Object[] elements) {
63 Set<Object> set = new HashSet<Object>();
64 if (elements != null) {
65 set.addAll(Arrays.asList(elements));
68 return addPaths(set);
71 private Set<Object> addPaths(Enumeration elements) {
72 ArrayList<Object> elementArray = new ArrayList<Object>();
73 if (elements != null) {
74 while (elements.hasMoreElements()) {
75 Object each = elements.nextElement();
76 elementArray.add(each);
80 return addPaths(elementArray);
83 private Set<Object> addPaths(Collection elements) {
84 Set<Object> target = new HashSet<Object>();
86 if (elements != null) {
87 for (Object each : elements) {
88 final Object node = ((TreePath)each).getLastPathComponent();
89 if (node instanceof DefaultMutableTreeNode) {
90 final Object descriptor = ((DefaultMutableTreeNode)node).getUserObject();
91 if (descriptor instanceof NodeDescriptor) {
92 final Object element = myUi.getElementFromDescriptor((NodeDescriptor)descriptor);
93 if (element != null) {
94 target.add(element);
100 return target;
103 public Object[] getToSelect() {
104 return myToSelect.keySet().toArray(new Object[myToSelect.size()]);
107 public Object[] getToExpand() {
108 return myToExpand.keySet().toArray(new Object[myToExpand.size()]);
111 public boolean process(Runnable runnable) {
112 try {
113 setProcessingNow(true);
114 runnable.run();
116 finally {
117 setProcessingNow(false);
120 return isEmpty();
123 public boolean isEmpty() {
124 return myToExpand.isEmpty() && myToSelect.isEmpty() && myAdjustedSelection.isEmpty();
128 public boolean isProcessingNow() {
129 return myProcessingCount > 0;
132 public void addAll(final UpdaterTreeState state) {
133 myToExpand.putAll(state.myToExpand);
135 Object[] toSelect = state.getToSelect();
136 for (Object each : toSelect) {
137 if (!myAdjustedSelection.containsKey(each)) {
138 myToSelect.put(each, each);
142 myCanRunRestore = state.myCanRunRestore;
145 public boolean restore(@Nullable DefaultMutableTreeNode actionNode) {
146 if (isProcessingNow() || !myCanRunRestore || myUi.hasNodesToUpdate()) {
147 return false;
150 invalidateToSelectWithRefsToParent(actionNode);
152 setProcessingNow(true);
154 final Object[] toSelect = getToSelect();
155 final Object[] toExpand = getToExpand();
158 final Map<Object, Condition> adjusted = new WeakHashMap<Object, Condition>();
159 adjusted.putAll(myAdjustedSelection);
161 clearSelection();
162 clearExpansion();
164 final Set<Object> originallySelected = myUi.getSelectedElements();
166 myUi._select(toSelect, new Runnable() {
167 public void run() {
168 processUnsuccessfulSelections(toSelect, new Function<Object, Object>() {
169 public Object fun(final Object o) {
170 if (myUi.getTree().isRootVisible() || !myUi.getTreeStructure().getRootElement().equals(o)) {
171 addSelection(o);
173 return o;
175 }, originallySelected);
177 processAjusted(adjusted, originallySelected).doWhenDone(new Runnable() {
178 public void run() {
179 myUi.expand(toExpand, new Runnable() {
180 public void run() {
181 myUi.clearUpdaterState();
182 setProcessingNow(false);
184 }, true);
188 }, false, true, true, false);
190 return true;
193 private void invalidateToSelectWithRefsToParent(DefaultMutableTreeNode actionNode) {
194 if (actionNode != null) {
195 Object readyElement = myUi.getElementFor(actionNode);
196 if (readyElement != null) {
197 Iterator<Object> toSelect = myToSelect.keySet().iterator();
198 while (toSelect.hasNext()) {
199 Object eachToSelect = toSelect.next();
200 if (readyElement.equals(myUi.getTreeStructure().getParentElement(eachToSelect))) {
201 List<Object> children = myUi.getLoadedChildrenFor(readyElement);
202 if (!children.contains(eachToSelect)) {
203 toSelect.remove();
204 if (!myToSelect.containsKey(readyElement) && !myUi.getSelectedElements().contains(eachToSelect)) {
205 addAdjustedSelection(eachToSelect, Condition.FALSE, null);
214 void beforeSubtreeUpdate() {
215 myCanRunRestore = true;
218 private void processUnsuccessfulSelections(final Object[] toSelect, Function<Object, Object> restore, Set<Object> originallySelected) {
219 final Set<Object> selected = myUi.getSelectedElements();
221 boolean wasFullyRejected = false;
222 if (toSelect.length > 0 && selected.size() > 0 && !originallySelected.containsAll(selected)) {
223 final Set<Object> successfulSelections = new HashSet<Object>();
224 successfulSelections.addAll(Arrays.asList(toSelect));
226 successfulSelections.retainAll(selected);
227 wasFullyRejected = successfulSelections.size() == 0;
228 } else if (selected.size() == 0 && originallySelected.size() == 0) {
229 wasFullyRejected = true;
232 if (wasFullyRejected && selected.size() > 0) return;
234 for (Object eachToSelect : toSelect) {
235 if (!selected.contains(eachToSelect)) {
236 restore.fun(eachToSelect);
241 private ActionCallback processAjusted(final Map<Object, Condition> adjusted, final Set<Object> originallySelected) {
242 final ActionCallback result = new ActionCallback();
244 final Set<Object> allSelected = myUi.getSelectedElements();
246 Set<Object> toSelect = new HashSet<Object>();
247 for (Object each : adjusted.keySet()) {
248 if (adjusted.get(each).value(each)) continue;
250 for (final Object eachSelected : allSelected) {
251 if (isParentOrSame(each, eachSelected)) continue;
252 toSelect.add(each);
254 if (allSelected.size() == 0) {
255 toSelect.add(each);
259 final Object[] newSelection = ArrayUtil.toObjectArray(toSelect);
261 if (newSelection.length > 0) {
262 myUi._select(newSelection, new Runnable() {
263 public void run() {
264 final Set<Object> hangByParent = new HashSet<Object> ();
265 processUnsuccessfulSelections(newSelection, new Function<Object, Object>() {
266 public Object fun(final Object o) {
267 if (myUi.isInStructure(o) && !adjusted.get(o).value(o)) {
268 hangByParent.add(o);
269 } else {
270 addAdjustedSelection(o, adjusted.get(o), null);
272 return null;
274 }, originallySelected);
276 processHangByParent(hangByParent).notify(result);
278 }, true, true, true);
279 } else {
280 result.setDone();
283 return result;
286 private ActionCallback processHangByParent(Set<Object> elements) {
287 if (elements.size() == 0) return new ActionCallback.Done();
289 ActionCallback result = new ActionCallback(elements.size());
290 for (Iterator<Object> iterator = elements.iterator(); iterator.hasNext();) {
291 Object hangElement = iterator.next();
292 if (!myAdjustmentCause2Adjustment.containsKey(hangElement)) {
293 processHangByParent(hangElement).notify(result);
294 } else {
295 result.setDone();
298 return result;
301 private ActionCallback processHangByParent(Object each) {
302 ActionCallback result = new ActionCallback();
303 processNextHang(each, result);
304 return result;
307 private void processNextHang(Object element, final ActionCallback callback) {
308 if (element == null || myUi.getSelectedElements().contains(element)) {
309 callback.setDone();
310 } else {
311 final Object nextElement = myUi.getTreeStructure().getParentElement(element);
312 if (nextElement == null) {
313 callback.setDone();
314 } else {
315 myUi.select(nextElement, new Runnable() {
316 public void run() {
317 processNextHang(nextElement, callback);
319 }, true);
324 private boolean isParentOrSame(Object parent, Object child) {
325 Object eachParent = child;
326 while (eachParent != null) {
327 if (parent.equals(eachParent)) return true;
328 eachParent = myUi.getTreeStructure().getParentElement(eachParent);
331 return false;
334 public void clearExpansion() {
335 myToExpand.clear();
338 public void clearSelection() {
339 myToSelect.clear();
340 myAdjustedSelection = new WeakHashMap<Object, Condition>();
343 public void addSelection(final Object element) {
344 myToSelect.put(element, element);
347 public void addAdjustedSelection(final Object element, Condition isExpired, @Nullable Object adjustmentCause) {
348 myAdjustedSelection.put(element, isExpired);
349 if (adjustmentCause != null) {
350 myAdjustmentCause2Adjustment.put(adjustmentCause, element);
354 @Override
355 public String toString() {
356 return "UpdaterState toSelect" + Arrays.asList(myToSelect) + " toExpand=" + Arrays.asList(myToExpand) + " processingNow=" + isProcessingNow() + " canRun=" + myCanRunRestore;
359 public void setProcessingNow(boolean processingNow) {
360 if (processingNow) {
361 myProcessingCount++;
362 } else {
363 myProcessingCount--;
365 if (!isProcessingNow()) {
366 myUi.maybeReady();
370 public void removeFromSelection(Object element) {
371 myToSelect.remove(element);
372 myAdjustedSelection.remove(element);