diff --git a/java/compiler/impl/src/com/intellij/build/BuildTreeConsoleView.java b/java/compiler/impl/src/com/intellij/build/BuildTreeConsoleView.java index c0ce7fc7da72..fddfa02f5456 100644 --- a/java/compiler/impl/src/com/intellij/build/BuildTreeConsoleView.java +++ b/java/compiler/impl/src/com/intellij/build/BuildTreeConsoleView.java @@ -17,6 +17,7 @@ package com.intellij.build; import com.intellij.build.events.*; import com.intellij.build.events.impl.FailureImpl; +import com.intellij.concurrency.JobScheduler; import com.intellij.execution.filters.Filter; import com.intellij.execution.filters.HyperlinkInfo; import com.intellij.execution.filters.TextConsoleBuilderFactory; @@ -46,6 +47,7 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.text.DateFormatUtil; import com.intellij.util.ui.ColumnInfo; import com.intellij.util.ui.UIUtil; +import com.intellij.util.ui.update.Update; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -63,6 +65,8 @@ import javax.swing.tree.TreePath; import java.awt.*; import java.util.*; import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -77,12 +81,14 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon private final SimpleTreeBuilder myBuilder; private final Map nodesMap = ContainerUtil.newConcurrentMap(); private final ExecutionNodeProgressAnimator myProgressAnimator; + private Set myRequests = Collections.synchronizedSet(new HashSet()); private final Project myProject; private final SimpleTreeStructure myTreeStructure; private final DetailsHandler myDetailsHandler; private final TableColumn myTimeColumn; private volatile int myTimeColumnWidth; + private final AtomicBoolean myDisposed = new AtomicBoolean(); public BuildTreeConsoleView(Project project) { myProject = project; @@ -104,7 +110,7 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon } } }; - final ExecutionNode rootNode = new ExecutionNode(myProject, null); + final ExecutionNode rootNode = new ExecutionNode(myProject); rootNode.setAutoExpandNode(true); final ListTreeTableModelOnColumns model = new ListTreeTableModelOnColumns(new DefaultMutableTreeNode(rootNode), COLUMNS); @@ -180,9 +186,6 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon Disposer.register(this, myBuilder); myBuilder.initRootNode(); myBuilder.updateFromRoot(); - myBuilder.expand(rootNode, null); - - myProgressAnimator = new ExecutionNodeProgressAnimator(myBuilder); JPanel myContentPanel = new JPanel(); myContentPanel.setLayout(new CardLayout()); @@ -209,6 +212,8 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon myDetailsHandler = new DetailsHandler(myProject, tree, myThreeComponentsSplitter); myThreeComponentsSplitter.setLastComponent(myDetailsHandler.getComponent()); myPanel.add(myThreeComponentsSplitter, BorderLayout.CENTER); + + myProgressAnimator = new ExecutionNodeProgressAnimator(this); } private ExecutionNode getRootElement() { @@ -297,6 +302,11 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon @Override public void dispose() { + myDisposed.set(true); + } + + public boolean isDisposed() { + return myDisposed.get(); } @Override @@ -306,7 +316,7 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon if (event instanceof StartEvent) { ExecutionNode rootElement = getRootElement(); if (currentNode == null) { - currentNode = event instanceof StartBuildEvent ? rootElement : new ExecutionNode(myProject, myProgressAnimator); + currentNode = event instanceof StartBuildEvent ? rootElement : new ExecutionNode(myProject); currentNode.setAutoExpandNode(currentNode == rootElement || parentNode == rootElement); nodesMap.put(event.getId(), currentNode); } @@ -322,12 +332,13 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon String buildTitle = ((StartBuildEvent)event).getBuildTitle(); currentNode.setTitle(buildTitle); currentNode.setAutoExpandNode(true); + myProgressAnimator.startMovie(); } } else { currentNode = nodesMap.get(event.getId()); if (currentNode == null && event instanceof ProgressBuildEvent) { - currentNode = new ExecutionNode(myProject, myProgressAnimator); + currentNode = new ExecutionNode(myProject); nodesMap.put(event.getId(), currentNode); if (parentNode != null) { parentNode.add(currentNode); @@ -354,8 +365,12 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon myTimeColumnWidth = timeColumnWidth; } } - - myBuilder.queueUpdateFrom(currentNode, false, false); + else { + scheduleUpdate(currentNode); + if (event instanceof StartEvent) { + myProgressAnimator.addNode(currentNode); + } + } if (event instanceof FinishBuildEvent) { String aHint = event.getHint(); @@ -371,7 +386,7 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon JTree tree = myBuilder.getTree(); if (tree != null && !tree.isRootVisible()) { ExecutionNode rootElement = getRootElement(); - ExecutionNode resultNode = new ExecutionNode(myProject, null); + ExecutionNode resultNode = new ExecutionNode(myProject); resultNode.setName(StringUtil.toTitleCase(rootElement.getName())); resultNode.setHint(rootElement.getHint()); resultNode.setEndTime(rootElement.getEndTime()); @@ -379,9 +394,25 @@ public class BuildTreeConsoleView implements ConsoleView, DataProvider, BuildCon resultNode.setResult(rootElement.getResult()); resultNode.setTooltip(rootElement.getTooltip()); rootElement.add(resultNode); - myBuilder.queueUpdateFrom(resultNode, false, false); + + scheduleUpdate(resultNode); } } + myProgressAnimator.stopMovie(); + myBuilder.updateFromRoot(); + } + } + + void scheduleUpdate(ExecutionNode executionNode) { + final Update update = new Update(executionNode) { + @Override + public void run() { + myRequests.remove(this); + myBuilder.queueUpdateFrom(executionNode, false, true); + } + }; + if (myRequests.add(update)) { + JobScheduler.getScheduler().schedule(update, 100, TimeUnit.MILLISECONDS); } } diff --git a/java/compiler/impl/src/com/intellij/build/ExecutionNode.java b/java/compiler/impl/src/com/intellij/build/ExecutionNode.java index 9abe8e4448cf..6abbe870e1b6 100644 --- a/java/compiler/impl/src/com/intellij/build/ExecutionNode.java +++ b/java/compiler/impl/src/com/intellij/build/ExecutionNode.java @@ -38,8 +38,6 @@ import java.util.List; */ public class ExecutionNode extends CachingSimpleNode { private final List myChildrenList = ContainerUtil.newSmartList(); - @Nullable - private final ExecutionNodeProgressAnimator myAnimator; private long startTime; private long endTime; @Nullable @@ -52,9 +50,8 @@ public class ExecutionNode extends CachingSimpleNode { private EventResult myResult; private boolean myAutoExpandNode; - public ExecutionNode(Project aProject, @Nullable ExecutionNodeProgressAnimator animator) { + public ExecutionNode(Project aProject) { super(aProject, null); - myAnimator = animator; } @Override @@ -84,15 +81,6 @@ public class ExecutionNode extends CachingSimpleNode { if (tooltip != null) { presentation.setTooltip(tooltip); } - - if (myAnimator != null) { - if (isRunning()) { - myAnimator.addNode(this); - } - else { - myAnimator.removeNode(this); - } - } } @Override diff --git a/java/compiler/impl/src/com/intellij/build/ExecutionNodeProgressAnimator.java b/java/compiler/impl/src/com/intellij/build/ExecutionNodeProgressAnimator.java index ca279126ce43..c51487f2497f 100644 --- a/java/compiler/impl/src/com/intellij/build/ExecutionNodeProgressAnimator.java +++ b/java/compiler/impl/src/com/intellij/build/ExecutionNodeProgressAnimator.java @@ -16,25 +16,22 @@ package com.intellij.build; import com.intellij.icons.AllIcons; -import com.intellij.ide.util.treeView.AbstractTreeBuilder; import com.intellij.openapi.Disposable; import com.intellij.openapi.util.Disposer; import com.intellij.util.Alarm; -import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.NotNull; +import com.intellij.util.SmartList; +import org.jetbrains.annotations.Nullable; import javax.swing.*; -import javax.swing.tree.DefaultMutableTreeNode; -import java.util.HashSet; +import java.util.Iterator; import java.util.List; -import java.util.Set; /** * @author Vladislav.Soroka */ public class ExecutionNodeProgressAnimator implements Runnable, Disposable { private static final int FRAMES_COUNT = 8; - private static final int MOVIE_TIME = 1600; + private static final int MOVIE_TIME = 1200; private static final int FRAME_TIME = MOVIE_TIME / FRAMES_COUNT; public static final Icon[] FRAMES = new Icon[FRAMES_COUNT]; @@ -42,12 +39,13 @@ public class ExecutionNodeProgressAnimator implements Runnable, Disposable { private long myLastInvocationTime = -1; private Alarm myAlarm; - private Set myNodes = new HashSet<>(); - private AbstractTreeBuilder myTreeBuilder; + private List myNodes = new SmartList<>(); + private BuildTreeConsoleView myTreeView; - public ExecutionNodeProgressAnimator(AbstractTreeBuilder builder) { - Disposer.register(builder, this); - init(builder); + public ExecutionNodeProgressAnimator(BuildTreeConsoleView treeConsoleView) { + Disposer.register(treeConsoleView, this); + myAlarm = new Alarm(); + myTreeView = treeConsoleView; } static { @@ -69,67 +67,56 @@ public class ExecutionNodeProgressAnimator implements Runnable, Disposable { return FRAMES[getCurrentFrameIndex()]; } - /** - * Initializes animator: creates alarm and sets tree builder - * - * @param treeBuilder tree builder - */ - protected void init(final AbstractTreeBuilder treeBuilder) { - myAlarm = new Alarm(); - myTreeBuilder = treeBuilder; - } - public void run() { if (!myNodes.isEmpty()) { final long time = System.currentTimeMillis(); // optimization: // we shouldn't repaint if this frame was painted in current interval if (time - myLastInvocationTime >= FRAME_TIME) { - repaintSubTree(); + repaintTree(); myLastInvocationTime = time; } } scheduleRepaint(); } - public void addNode(@NotNull ExecutionNode node) { - if (myNodes.add(node) && myNodes.size() == 1) { - scheduleRepaint(); - } + public void addNode(@Nullable final ExecutionNode currentNode) { + myNodes.add(currentNode); } - public void removeNode(@NotNull ExecutionNode node) { - if (myNodes.remove(node) && myNodes.isEmpty()) { - repaintSubTree(); - if (myAlarm != null) { - myAlarm.cancelAllRequests(); - } - } + public void startMovie() { + scheduleRepaint(); + } + + public void stopMovie() { + repaintTree(); + myNodes.clear(); + cancelAlarm(); } public void dispose() { - myTreeBuilder = null; + myTreeView = null; myNodes.clear(); + cancelAlarm(); + } + + private void cancelAlarm() { if (myAlarm != null) { myAlarm.cancelAllRequests(); myAlarm = null; } } - private void repaintSubTree() { - if (myTreeBuilder == null || myTreeBuilder.isDisposed()) return; + private void repaintTree() { + if (myTreeView == null || myTreeView.isDisposed()) return; - List toRemove = ContainerUtil.newSmartList(); - for (ExecutionNode node : myNodes) { - DefaultMutableTreeNode treeNode = myTreeBuilder.getUi().getNodeForElement(node, false); - if (treeNode != null) { - myTreeBuilder.queueUpdateFrom(node, false, false); - } - else { - toRemove.add(node); + for (Iterator iterator = myNodes.iterator(); iterator.hasNext(); ) { + ExecutionNode node = iterator.next(); + myTreeView.scheduleUpdate(node); + if (!node.isRunning()) { + iterator.remove(); } } - myNodes.removeAll(toRemove); } private void scheduleRepaint() { @@ -137,8 +124,6 @@ public class ExecutionNodeProgressAnimator implements Runnable, Disposable { return; } myAlarm.cancelAllRequests(); - if (!myNodes.isEmpty()) { - myAlarm.addRequest(this, FRAME_TIME); - } + myAlarm.addRequest(this, FRAME_TIME); } }