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
;
33 import javax
.swing
.tree
.DefaultMutableTreeNode
;
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
);
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()) {
86 } else if (toAdd
.getNode().isNodeAncestor(passInQueue
.getNode())) {
89 } else if (passInQueue
.getNode().isNodeAncestor(toAdd
.getNode())) {
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())) {
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();
126 if (myTreeBuilder
.getTreeStructure().hasSomethingToCommit()) {
130 myTreeBuilder
.getTreeStructure().commit();
134 catch(ProcessCanceledException 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
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() {
165 myTreeBuilder
.getUi().updateSubtree(eachPass
);
170 if (myRunAfterUpdate
!= null) {
171 final Runnable runnable
= new Runnable() {
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
) {
188 invokeLater(runnable
);
192 protected void invokeLater(Runnable runnable
) {
193 final Application app
= ApplicationManager
.getApplication();
195 app
.invokeLater(runnable
);
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
);
212 addSubtreeToUpdate(node
);
220 public void cancelAllRequests(){
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();