diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleAbstractSyncTreeFilterAction.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleAbstractSyncTreeFilterAction.java index d819627d55c0..8e0398356451 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleAbstractSyncTreeFilterAction.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/action/GradleAbstractSyncTreeFilterAction.java @@ -1,12 +1,16 @@ package org.jetbrains.plugins.gradle.action; -import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.ToggleAction; import com.intellij.openapi.editor.colors.EditorColorsManager; +import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.options.colors.AttributesDescriptor; import com.intellij.util.ui.ColorIcon; import org.jetbrains.annotations.NotNull; +import org.jetbrains.plugins.gradle.sync.GradleProjectStructureTreeModel; +import org.jetbrains.plugins.gradle.ui.GradleDataKeys; +import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNode; +import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNodeFilter; import javax.swing.*; import java.awt.*; @@ -17,10 +21,10 @@ import java.awt.*; */ public abstract class GradleAbstractSyncTreeFilterAction extends ToggleAction { - @NotNull private final AttributesDescriptor myDescriptor; + @NotNull private final MyFilter myFilter; protected GradleAbstractSyncTreeFilterAction(@NotNull AttributesDescriptor descriptor) { - myDescriptor = descriptor; + myFilter = new MyFilter(descriptor.getKey()); getTemplatePresentation().setText(descriptor.getDisplayName()); final Color color = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(descriptor.getKey()).getForegroundColor(); getTemplatePresentation().setIcon(new ColorIcon(new JLabel("").getFont().getSize(), color)); @@ -28,13 +32,39 @@ public abstract class GradleAbstractSyncTreeFilterAction extends ToggleAction { @Override public boolean isSelected(AnActionEvent e) { - // TODO den implement - return false; + final GradleProjectStructureTreeModel model = GradleDataKeys.SYNC_TREE_MODEL.getData(e.getDataContext()); + if (model == null) { + return false; + } + + return model.hasFilter(myFilter); } @Override public void setSelected(AnActionEvent e, boolean state) { - // TODO den implement - System.out.println("GradleAbstractSyncTreeFilterAction.setSelected(): " + state); + final GradleProjectStructureTreeModel treeModel = GradleDataKeys.SYNC_TREE_MODEL.getData(e.getDataContext()); + if (treeModel == null) { + return; + } + if (state) { + treeModel.addFilter(myFilter); + } + else { + treeModel.removeFilter(myFilter); + } + } + + private static class MyFilter implements GradleProjectStructureNodeFilter { + + @NotNull private final TextAttributesKey myKey; + + MyFilter(@NotNull TextAttributesKey key) { + myKey = key; + } + + @Override + public boolean isVisible(@NotNull GradleProjectStructureNode node) { + return myKey.equals(node.getDescriptor().getAttributes()); + } } } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureChangesPanel.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureChangesPanel.java index 3d72ad01668b..cd2f9c898edf 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureChangesPanel.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureChangesPanel.java @@ -12,13 +12,10 @@ import com.intellij.openapi.util.Disposer; import com.intellij.ui.awt.RelativePoint; import com.intellij.ui.treeStructure.Tree; import com.intellij.util.Alarm; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.config.GradleToolWindowPanel; -import org.jetbrains.plugins.gradle.diff.GradleProjectStructureChange; import org.jetbrains.plugins.gradle.ui.GradleDataKeys; import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNode; import org.jetbrains.plugins.gradle.ui.GradleUiListener; @@ -33,7 +30,6 @@ import java.awt.*; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.util.ArrayList; -import java.util.Collection; import java.util.List; /** @@ -61,20 +57,6 @@ public class GradleProjectStructureChangesPanel extends GradleToolWindowPanel { public GradleProjectStructureChangesPanel(@NotNull Project project, @NotNull GradleProjectStructureContext context) { super(project, GradleConstants.TOOL_WINDOW_TOOLBAR_PLACE); myContext = context; - context.getChangesModel().addListener(new GradleProjectStructureChangeListener() { - @Override - public void onChanges(@NotNull final Collection oldChanges, - @NotNull final Collection currentChanges) - { - UIUtil.invokeLaterIfNeeded(new Runnable() { - @Override - public void run() { - myTreeModel.processObsoleteChanges(ContainerUtil.subtract(oldChanges, currentChanges)); - myTreeModel.processCurrentChanges(currentChanges); - } - }); - } - }); myToolbarControls.add(new GradleProjectStructureFiltersPanel()); initContent(); } @@ -89,7 +71,6 @@ public class GradleProjectStructureChangesPanel extends GradleToolWindowPanel { protected JComponent buildContent() { JPanel result = new JPanel(new GridBagLayout()); myTreeModel = new GradleProjectStructureTreeModel(getProject(), myContext); - myTreeModel.processCurrentChanges(myContext.getChangesModel().getChanges()); myTree = new Tree(myTreeModel); applyInitialAppearance(myTree, (DefaultMutableTreeNode)myTreeModel.getRoot()); @@ -265,6 +246,9 @@ public class GradleProjectStructureChangesPanel extends GradleToolWindowPanel { if (GradleDataKeys.SYNC_TREE.is(dataId)) { return myTree; } + else if (GradleDataKeys.SYNC_TREE_MODEL.is(dataId)) { + return myTreeModel; + } else if (GradleDataKeys.SYNC_TREE_SELECTED_NODE.is(dataId)) { TreePath[] paths = myTree.getSelectionPaths(); if (paths == null) { diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureTreeModel.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureTreeModel.java index 1bb6991fb5ae..fa434b7429e6 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureTreeModel.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/sync/GradleProjectStructureTreeModel.java @@ -4,17 +4,22 @@ import com.intellij.openapi.editor.colors.TextAttributesKey; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.*; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.hash.HashMap; +import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; import org.jetbrains.plugins.gradle.config.GradleTextAttributes; import org.jetbrains.plugins.gradle.diff.*; import org.jetbrains.plugins.gradle.model.GradleEntityOwner; import org.jetbrains.plugins.gradle.model.gradle.GradleModule; import org.jetbrains.plugins.gradle.model.id.*; import org.jetbrains.plugins.gradle.model.intellij.ModuleAwareContentRoot; -import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNodeComparator; import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNode; +import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNodeComparator; import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNodeDescriptor; +import org.jetbrains.plugins.gradle.ui.GradleProjectStructureNodeFilter; import org.jetbrains.plugins.gradle.util.GradleBundle; import org.jetbrains.plugins.gradle.util.GradleConstants; import org.jetbrains.plugins.gradle.util.GradleProjectStructureContext; @@ -49,24 +54,57 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel { private final Map> myModules = new HashMap>(); - private final TreeNode[] myNodeHolder = new TreeNode[1]; - private final int[] myIndexHolder = new int[1]; - private final NodeListener myNodeListener = new NodeListener(); - private final ObsoleteChangesDispatcher myObsoleteChangesDispatcher = new ObsoleteChangesDispatcher(); - private final NewChangesDispatcher myNewChangesDispatcher = new NewChangesDispatcher(); + private final Set myFilters = new HashSet(); + private final TreeNode[] myNodeHolder = new TreeNode[1]; + private final int[] myIndexHolder = new int[1]; + private final NodeListener myNodeListener = new NodeListener(); + private final ObsoleteChangesDispatcher myObsoleteChangesDispatcher = new ObsoleteChangesDispatcher(); + private final NewChangesDispatcher myNewChangesDispatcher = new NewChangesDispatcher(); @NotNull private final Project myProject; @NotNull private final PlatformFacade myPlatformFacade; @NotNull private final GradleProjectStructureHelper myProjectStructureHelper; @NotNull private final Comparator> myNodeComparator; + @NotNull private final GradleProjectStructureChangesModel myChangesModel; + + private Comparator myChangesComparator; + private boolean myProcessChangesAtTheSameThread; public GradleProjectStructureTreeModel(@NotNull Project project, @NotNull GradleProjectStructureContext context) { super(null); myProject = project; myPlatformFacade = context.getPlatformFacade(); myProjectStructureHelper = context.getProjectStructureHelper(); + myChangesModel = context.getChangesModel(); myNodeComparator = new GradleProjectStructureNodeComparator(context); rebuild(); + + context.getChangesModel().addListener(new GradleProjectStructureChangeListener() { + @Override + public void onChanges(@NotNull final Collection oldChanges, + @NotNull final Collection currentChanges) + { + final Runnable task = new Runnable() { + @Override + public void run() { + Collection c = ContainerUtil.subtract(oldChanges, currentChanges); + if (myChangesComparator != null) { + List toSort = new ArrayList(c); + Collections.sort(toSort, myChangesComparator); + c = toSort; + } + processObsoleteChanges(c); + processCurrentChanges(currentChanges); + } + }; + if (myProcessChangesAtTheSameThread) { + task.run(); + } + else { + UIUtil.invokeLaterIfNeeded(task); + } + } + }); } public void rebuild() { @@ -121,10 +159,70 @@ public class GradleProjectStructureTreeModel extends DefaultTreeModel { dependenciesNode.add(dependency); } } + processCurrentChanges(myChangesModel.getChanges()); + filterNodes(root); setRoot(root); } + @TestOnly + public void setProcessChangesAtTheSameThread(boolean processChangesAtTheSameThread) { + myProcessChangesAtTheSameThread = processChangesAtTheSameThread; + } + + @TestOnly + public void setChangesComparator(@Nullable Comparator changesComparator) { + myChangesComparator = changesComparator; + } + + private void filterNodes(@NotNull GradleProjectStructureNode root) { + if (myFilters.isEmpty()) { + return; + } + Deque> toRemove = new ArrayDeque>(); + Stack> toProcess = new Stack>(); + toProcess.push(root); + while (!toProcess.isEmpty()) { + final GradleProjectStructureNode current = toProcess.pop(); + toRemove.add(current); + for (GradleProjectStructureNodeFilter filter : myFilters) { + if (filter.isVisible(current)) { + toRemove.remove(current); + // Keep all nodes up to the hierarchy. + for (GradleProjectStructureNode parent = current.getParent(); parent != null; parent = parent.getParent()) { + if (!toRemove.remove(parent)) { + break; + } + } + break; + } + } + for (GradleProjectStructureNode child : current) { + toProcess.push(child); + } + } + for (GradleProjectStructureNode node = toRemove.pollLast(); node != null; node = toRemove.pollLast()) { + final GradleProjectStructureNode parent = node.getParent(); + if (parent != null) { + parent.remove(node); + } + } + } + + public void addFilter(@NotNull GradleProjectStructureNodeFilter filter) { + myFilters.add(filter); + rebuild(); + } + + public boolean hasFilter(@NotNull GradleProjectStructureNodeFilter filter) { + return myFilters.contains(filter); + } + + public void removeFilter(@NotNull GradleProjectStructureNodeFilter filter) { + myFilters.remove(filter); + rebuild(); + } + @NotNull private static String getContentRootNodeName(@NotNull GradleContentRootId id, boolean singleRoot) { final String name = GradleBundle.message("gradle.import.structure.tree.node.content.root"); diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleDataKeys.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleDataKeys.java index b247cfe5e696..f739382b1171 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleDataKeys.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleDataKeys.java @@ -2,6 +2,7 @@ package org.jetbrains.plugins.gradle.ui; import com.intellij.openapi.actionSystem.DataKey; import com.intellij.ui.treeStructure.Tree; +import org.jetbrains.plugins.gradle.sync.GradleProjectStructureTreeModel; import java.util.Collection; @@ -13,6 +14,9 @@ public class GradleDataKeys { /** Key for obtaining 'sync project structure' tree. */ public static final DataKey SYNC_TREE = DataKey.create("gradle.sync.tree"); + + /** Key for obtaining 'sync project structure' tree model. */ + public static final DataKey SYNC_TREE_MODEL = DataKey.create("gradle.sync.tree.model"); /** Key for obtaining currently selected nodes at the gradle 'sync project structure' tree. */ public static final DataKey>> SYNC_TREE_SELECTED_NODE diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleProjectStructureNode.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleProjectStructureNode.java index 2f30188ee2d2..55163671bfd2 100644 --- a/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleProjectStructureNode.java +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleProjectStructureNode.java @@ -100,8 +100,15 @@ public class GradleProjectStructureNode extends Defaul @Override public void remove(MutableTreeNode aChild) { + boolean b = mySkipNotification; + mySkipNotification = true; final int index = getIndex(aChild); - super.remove(aChild); + try { + super.remove(aChild); + } + finally { + mySkipNotification = b; + } onNodeRemoved((GradleProjectStructureNode)aChild, index); } diff --git a/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleProjectStructureNodeFilter.java b/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleProjectStructureNodeFilter.java new file mode 100644 index 000000000000..0923f5619091 --- /dev/null +++ b/plugins/gradle/src/org/jetbrains/plugins/gradle/ui/GradleProjectStructureNodeFilter.java @@ -0,0 +1,12 @@ +package org.jetbrains.plugins.gradle.ui; + +import org.jetbrains.annotations.NotNull; + +/** + * @author Denis Zhdanov + * @since 3/11/12 11:40 AM + */ +public interface GradleProjectStructureNodeFilter { + + boolean isVisible(@NotNull GradleProjectStructureNode node); +} diff --git a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/testutil/AbstractGradleTest.groovy b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/testutil/AbstractGradleTest.groovy index 895bef7eed6d..c9b4080a27af 100644 --- a/plugins/gradle/testSources/org/jetbrains/plugins/gradle/testutil/AbstractGradleTest.groovy +++ b/plugins/gradle/testSources/org/jetbrains/plugins/gradle/testutil/AbstractGradleTest.groovy @@ -1,19 +1,17 @@ package org.jetbrains.plugins.gradle.testutil import com.intellij.openapi.project.Project -import com.intellij.util.containers.ContainerUtil import org.jetbrains.plugins.gradle.model.id.GradleEntityIdMapper -import org.jetbrains.plugins.gradle.sync.GradleProjectStructureChangeListener import org.jetbrains.plugins.gradle.sync.GradleProjectStructureChangesModel import org.jetbrains.plugins.gradle.sync.GradleProjectStructureHelper import org.jetbrains.plugins.gradle.sync.GradleProjectStructureTreeModel +import org.jetbrains.plugins.gradle.util.GradleProjectStructureContext import org.junit.Before import org.picocontainer.MutablePicoContainer import org.picocontainer.defaults.DefaultPicoContainer import org.jetbrains.plugins.gradle.diff.* import static org.junit.Assert.fail -import org.jetbrains.plugins.gradle.util.GradleProjectStructureContext /** * @author Denis Zhdanov @@ -28,7 +26,6 @@ public abstract class AbstractGradleTest { def changesBuilder def treeChecker def container - private Closure changesComparator @Before public void setUp() { @@ -61,28 +58,18 @@ public abstract class AbstractGradleTest { @SuppressWarnings("GroovyAssignabilityCheck") protected def init(map = [:]) { treeModel = container.getComponentInstance(GradleProjectStructureTreeModel) as GradleProjectStructureTreeModel - changesModel.addListener({ old, current -> - treeModel.processObsoleteChanges(sortChanges(ContainerUtil.subtract(old, current))); - treeModel.processCurrentChanges(sortChanges(current)) - } as GradleProjectStructureChangeListener) + treeModel.processChangesAtTheSameThread = true; setState(map, false) treeModel.rebuild() changesModel.update(gradle.project) } - def sortChanges(changes) { - if (changesComparator) { - return changes.toList().sort(changesComparator) - } - return changes - } - protected def setState(map, update = true) { map.intellij?.delegate = intellij map.intellij?.call() map.gradle?.delegate = gradle map.gradle?.call() - changesComparator = map.changesSorter + treeModel.changesComparator = map.changesSorter as Comparator if (update) { changesModel.update(gradle.project) }