TreeUi: infinite update for some cases if fixed
[fedora-idea.git] / platform-api / src / com / intellij / ide / util / treeView / AbstractTreeUpdater.java
blob84c5c293941226dacc3e11aa99484404f69f9e64
1 /*
2 * Copyright 2000-2007 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.
17 package com.intellij.ide.util.treeView;
19 import com.intellij.openapi.Disposable;
20 import com.intellij.openapi.application.Application;
21 import com.intellij.openapi.application.ApplicationManager;
22 import com.intellij.openapi.diagnostic.Logger;
23 import com.intellij.openapi.progress.ProcessCanceledException;
24 import com.intellij.openapi.util.ActionCallback;
25 import com.intellij.openapi.util.Disposer;
26 import com.intellij.util.ui.UIUtil;
27 import com.intellij.util.ui.update.MergingUpdateQueue;
28 import com.intellij.util.ui.update.UiNotifyConnector;
29 import com.intellij.util.ui.update.Update;
30 import org.jetbrains.annotations.NotNull;
32 import javax.swing.*;
33 import javax.swing.tree.DefaultMutableTreeNode;
34 import java.util.*;
36 public class AbstractTreeUpdater implements Disposable {
37 private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.treeView.AbstractTreeUpdater");
39 private final LinkedList<TreeUpdatePass> myNodeQueue = new LinkedList<TreeUpdatePass>();
40 private final AbstractTreeBuilder myTreeBuilder;
41 private final List<Runnable> myRunAfterUpdate = new ArrayList<Runnable>();
42 private Runnable myRunBeforeUpdate;
43 private final MergingUpdateQueue myUpdateQueue;
45 private long myUpdateCount;
47 public AbstractTreeUpdater(AbstractTreeBuilder treeBuilder) {
48 myTreeBuilder = treeBuilder;
49 final JTree tree = myTreeBuilder.getTree();
50 myUpdateQueue = new MergingUpdateQueue("UpdateQueue", 300, tree.isShowing(), tree);
51 final UiNotifyConnector uiNotifyConnector = new UiNotifyConnector(tree, myUpdateQueue);
52 Disposer.register(this, myUpdateQueue);
53 Disposer.register(this, uiNotifyConnector);
56 /**
57 * @param delay update delay in milliseconds.
59 public void setDelay(int delay) {
60 myUpdateQueue.setMergingTimeSpan(delay);
63 public boolean hasNodesToUpdate() {
64 return myNodeQueue.size() > 0;
67 public void dispose() {
70 public synchronized void addSubtreeToUpdate(@NotNull DefaultMutableTreeNode rootNode) {
71 addSubtreeToUpdate(new TreeUpdatePass(rootNode));
74 public synchronized void addSubtreeToUpdate(@NotNull TreeUpdatePass toAdd) {
75 if (LOG.isDebugEnabled()) {
76 LOG.debug("addSubtreeToUpdate:" + toAdd.getNode());
79 assert !toAdd.isExpired();
81 for (Iterator<TreeUpdatePass> iterator = myNodeQueue.iterator(); iterator.hasNext();) {
82 final TreeUpdatePass passInQueue = iterator.next();
83 if (passInQueue.getNode() == toAdd.getNode()) {
84 toAdd.expire();
85 return;
86 } else if (toAdd.getNode().isNodeAncestor(passInQueue.getNode())) {
87 toAdd.expire();
88 return;
89 } else if (passInQueue.getNode().isNodeAncestor(toAdd.getNode())) {
90 iterator.remove();
91 passInQueue.expire();
95 long newUpdateCount = myUpdateCount + 1;
97 final AbstractTreeUi ui = myTreeBuilder.getUi();
98 final Collection<TreeUpdatePass> yielding = ui.getYeildingPasses();
99 for (Iterator<TreeUpdatePass> iterator = yielding.iterator(); iterator.hasNext();) {
100 TreeUpdatePass eachYielding = iterator.next();
102 final DefaultMutableTreeNode eachNode = eachYielding.getCurrentNode();
103 if (eachNode != null) {
104 if (eachNode.isNodeAncestor(toAdd.getNode())) {
105 toAdd.expire();
106 } else {
107 eachYielding.setSheduleStamp(newUpdateCount);
112 if (toAdd.isExpired()) return;
115 myNodeQueue.add(toAdd);
117 myUpdateCount = newUpdateCount;
118 toAdd.setSheduleStamp(myUpdateCount);
120 queue(new Update("ViewUpdate") {
121 public boolean isExpired() {
122 return myTreeBuilder.isDisposed();
125 public void run() {
126 if (myTreeBuilder.getTreeStructure().hasSomethingToCommit()) {
127 queue(this);
128 return;
130 myTreeBuilder.getTreeStructure().commit();
131 try {
132 performUpdate();
134 catch(ProcessCanceledException e) {
135 throw e;
136 } catch(RuntimeException e) {
137 LOG.error(myTreeBuilder.getClass().getName(), e);
143 private void queue(Update update) {
144 myUpdateQueue.queue(update);
148 * @deprecated use addSubtreeToUpdate instead
149 * @param node
151 protected void updateSubtree(DefaultMutableTreeNode node) {
152 myTreeBuilder.updateSubtree(node);
155 public synchronized void performUpdate() {
156 if (myRunBeforeUpdate != null){
157 myRunBeforeUpdate.run();
158 myRunBeforeUpdate = null;
161 while(!myNodeQueue.isEmpty()){
162 final TreeUpdatePass eachPass = myNodeQueue.removeFirst();
163 beforeUpdate(eachPass).doWhenDone(new Runnable() {
164 public void run() {
165 myTreeBuilder.getUi().updateSubtree(eachPass);
170 if (myRunAfterUpdate != null) {
171 final Runnable runnable = new Runnable() {
172 public void run() {
173 List<Runnable> runAfterUpdate = null;
174 synchronized (myRunAfterUpdate) {
175 if (!myRunAfterUpdate.isEmpty()) {
176 runAfterUpdate = new ArrayList<Runnable>(myRunAfterUpdate);
177 myRunAfterUpdate.clear();
180 if (runAfterUpdate != null) {
181 for (Runnable r : runAfterUpdate) {
182 r.run();
188 invokeLater(runnable);
192 protected void invokeLater(Runnable runnable) {
193 final Application app = ApplicationManager.getApplication();
194 if (app != null) {
195 app.invokeLater(runnable);
196 } else {
197 UIUtil.invokeAndWaitIfNeeded(runnable);
201 protected ActionCallback beforeUpdate(TreeUpdatePass pass) {
202 return new ActionCallback.Done();
205 public boolean addSubtreeToUpdateByElement(Object element) {
206 if (LOG.isDebugEnabled()) {
207 LOG.debug("addSubtreeToUpdateByElement:" + element);
210 DefaultMutableTreeNode node = myTreeBuilder.getNodeForElement(element);
211 if (node != null){
212 addSubtreeToUpdate(node);
213 return true;
215 else{
216 return false;
220 public void cancelAllRequests(){
221 myNodeQueue.clear();
222 myUpdateQueue.cancelAllUpdates();
225 public void runAfterUpdate(final Runnable runnable) {
226 if (runnable == null) return;
227 synchronized (myRunAfterUpdate) {
228 myRunAfterUpdate.add(runnable);
232 public synchronized void runBeforeUpdate(final Runnable runnable) {
233 myRunBeforeUpdate = runnable;
236 public long getUpdateCount() {
237 return myUpdateCount;
240 public boolean isRerunNeededFor(TreeUpdatePass pass) {
241 return pass.getUpdateStamp() < getUpdateCount();