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
.Activatable
;
28 import com
.intellij
.util
.ui
.update
.MergingUpdateQueue
;
29 import com
.intellij
.util
.ui
.update
.UiNotifyConnector
;
30 import com
.intellij
.util
.ui
.update
.Update
;
31 import com
.intellij
.util
.Alarm
;
32 import com
.intellij
.ui
.treeStructure
.treetable
.TreeTableTree
;
33 import org
.jetbrains
.annotations
.NotNull
;
36 import javax
.swing
.tree
.DefaultMutableTreeNode
;
39 public class AbstractTreeUpdater
implements Disposable
, Activatable
{
40 private static final Logger LOG
= Logger
.getInstance("#com.intellij.ide.util.treeView.AbstractTreeUpdater");
42 private final LinkedList
<TreeUpdatePass
> myNodeQueue
= new LinkedList
<TreeUpdatePass
>();
43 private final AbstractTreeBuilder myTreeBuilder
;
44 private final List
<Runnable
> myRunAfterUpdate
= new ArrayList
<Runnable
>();
45 private Runnable myRunBeforeUpdate
;
46 private final MergingUpdateQueue myUpdateQueue
;
48 private long myUpdateCount
;
50 public AbstractTreeUpdater(AbstractTreeBuilder treeBuilder
) {
51 myTreeBuilder
= treeBuilder
;
52 final JTree tree
= myTreeBuilder
.getTree();
53 final JComponent component
= tree
instanceof TreeTableTree?
((TreeTableTree
)tree
).getTreeTable() : tree
;
54 myUpdateQueue
= new MergingUpdateQueue("UpdateQueue", 300, component
.isShowing(), component
) {
56 protected Alarm
createAlarm(Alarm
.ThreadToUse thread
, Disposable parent
) {
57 return new Alarm(thread
, parent
) {
59 protected boolean isEdt() {
60 return AbstractTreeUpdater
.this.isEdt();
65 final UiNotifyConnector uiNotifyConnector
= new UiNotifyConnector(component
, myUpdateQueue
);
66 Disposer
.register(this, myUpdateQueue
);
67 Disposer
.register(this, uiNotifyConnector
);
71 * @param delay update delay in milliseconds.
73 public void setDelay(int delay
) {
74 myUpdateQueue
.setMergingTimeSpan(delay
);
77 public void setPassThroughMode(boolean passThroughMode
) {
78 myUpdateQueue
.setPassThrough(passThroughMode
);
81 public void setModalityStateComponent(JComponent c
) {
82 myUpdateQueue
.setModalityStateComponent(c
);
85 public boolean hasNodesToUpdate() {
86 return myNodeQueue
.size() > 0 || !myUpdateQueue
.isEmpty();
89 public void dispose() {
92 public synchronized void addSubtreeToUpdate(@NotNull DefaultMutableTreeNode rootNode
) {
93 addSubtreeToUpdate(new TreeUpdatePass(rootNode
).setUpdateStamp(-1));
96 public synchronized void addSubtreeToUpdate(@NotNull TreeUpdatePass toAdd
) {
97 if (LOG
.isDebugEnabled()) {
98 LOG
.debug("addSubtreeToUpdate:" + toAdd
.getNode());
102 assert !toAdd
.isExpired();
104 for (Iterator
<TreeUpdatePass
> iterator
= myNodeQueue
.iterator(); iterator
.hasNext();) {
105 final TreeUpdatePass passInQueue
= iterator
.next();
108 if (passInQueue
== toAdd
) {
110 } else if (passInQueue
.getNode() == toAdd
.getNode()) {
113 } else if (toAdd
.getNode().isNodeAncestor(passInQueue
.getNode())) {
116 } else if (passInQueue
.getNode().isNodeAncestor(toAdd
.getNode())) {
118 passInQueue
.expire();
122 long newUpdateCount
= toAdd
.getUpdateStamp() == -1 ? myUpdateCount
: myUpdateCount
+ 1;
124 final AbstractTreeUi ui
= myTreeBuilder
.getUi();
125 final Collection
<TreeUpdatePass
> yielding
= ui
.getYeildingPasses();
126 for (Iterator
<TreeUpdatePass
> iterator
= yielding
.iterator(); iterator
.hasNext();) {
127 TreeUpdatePass eachYielding
= iterator
.next();
129 final DefaultMutableTreeNode eachNode
= eachYielding
.getCurrentNode();
130 if (eachNode
!= null) {
131 if (eachNode
.isNodeAncestor(toAdd
.getNode())) {
134 eachYielding
.setUpdateStamp(newUpdateCount
);
139 if (toAdd
.isExpired()) return;
142 myNodeQueue
.add(toAdd
);
144 myUpdateCount
= newUpdateCount
;
145 toAdd
.setUpdateStamp(myUpdateCount
);
147 queue(new Update("ViewUpdate") {
148 public boolean isExpired() {
149 return myTreeBuilder
.isDisposed();
153 if (myTreeBuilder
.getTreeStructure().hasSomethingToCommit()) {
154 myTreeBuilder
.getTreeStructure().commit();
161 catch(ProcessCanceledException e
) {
163 } catch(RuntimeException e
) {
164 LOG
.error(myTreeBuilder
.getClass().getName(), e
);
170 private void queue(Update update
) {
171 myUpdateQueue
.queue(update
);
175 * @deprecated use addSubtreeToUpdate instead
178 protected void updateSubtree(DefaultMutableTreeNode node
) {
179 myTreeBuilder
.updateSubtree(node
);
182 public synchronized void performUpdate() {
183 if (myRunBeforeUpdate
!= null){
184 myRunBeforeUpdate
.run();
185 myRunBeforeUpdate
= null;
189 while(!myNodeQueue
.isEmpty()){
190 if (isInPostponeMode()) break;
193 final TreeUpdatePass eachPass
= myNodeQueue
.removeFirst();
195 beforeUpdate(eachPass
).doWhenDone(new Runnable() {
197 myTreeBuilder
.getUi().updateSubtreeNow(eachPass
);
203 myTreeBuilder
.getUi().maybeReady();
205 if (myRunAfterUpdate
!= null) {
206 final Runnable runnable
= new Runnable() {
208 List
<Runnable
> runAfterUpdate
= null;
209 synchronized (myRunAfterUpdate
) {
210 if (!myRunAfterUpdate
.isEmpty()) {
211 runAfterUpdate
= new ArrayList
<Runnable
>(myRunAfterUpdate
);
212 myRunAfterUpdate
.clear();
215 if (runAfterUpdate
!= null) {
216 for (Runnable r
: runAfterUpdate
) {
223 invokeLater(runnable
);
227 protected void invokeLater(Runnable runnable
) {
228 final Application app
= ApplicationManager
.getApplication();
230 app
.invokeLater(runnable
);
232 UIUtil
.invokeAndWaitIfNeeded(runnable
);
236 protected ActionCallback
beforeUpdate(TreeUpdatePass pass
) {
237 return new ActionCallback
.Done();
240 public boolean addSubtreeToUpdateByElement(Object element
) {
241 if (LOG
.isDebugEnabled()) {
242 LOG
.debug("addSubtreeToUpdateByElement:" + element
);
245 DefaultMutableTreeNode node
= myTreeBuilder
.getNodeForElement(element
);
247 addSubtreeToUpdate(node
);
255 public void cancelAllRequests(){
257 myUpdateQueue
.cancelAllUpdates();
260 public void runAfterUpdate(final Runnable runnable
) {
261 if (runnable
== null) return;
262 synchronized (myRunAfterUpdate
) {
263 myRunAfterUpdate
.add(runnable
);
267 public synchronized void runBeforeUpdate(final Runnable runnable
) {
268 myRunBeforeUpdate
= runnable
;
271 public long getUpdateCount() {
272 return myUpdateCount
;
275 public boolean isRerunNeededFor(TreeUpdatePass pass
) {
276 return pass
.getUpdateStamp() < getUpdateCount();
279 public boolean isInPostponeMode() {
280 return !myUpdateQueue
.isActive() && !myUpdateQueue
.isPassThrough();
283 public void showNotify() {
284 myUpdateQueue
.showNotify();
287 public void hideNotify() {
288 myUpdateQueue
.hideNotify();
291 protected boolean isEdt() {
292 return Alarm
.isEventDispatchThread();
296 public String
toString() {
297 return "AbstractTreeUpdater updateCount=" + myUpdateCount
+ " queue=[" + myUpdateQueue
.toString() + "] " + " nodeQueue=" + myNodeQueue
;
300 public void flush() {
301 myUpdateQueue
.sendFlush();