From 49df96a49572fbf2be0e47f821f70f51a0bcf68e Mon Sep 17 00:00:00 2001 From: "Denis.Zhdanov" Date: Wed, 31 Oct 2012 16:42:46 +0400 Subject: [PATCH] IDEA-19061 Integrate the Rearranger-plugin into core-IDE Re-organizing arrangement UI - tree -> list --- .../options/JavaArrangementPanel.java | 4 +- .../codeStyle/arrangement/JavaRearranger.java | 2 +- ...stractArrangementRuleEditingModelTest.java | 98 - .../ArrangementConfigUtilTest.groovy | 507 ----- ...rrangementRuleEditingModelBuilderTest.java | 77 - .../ArrangementRuleEditingModelImplTest.java | 197 -- .../arrangement/ArrangementConditionInfo.java | 71 + .../arrangement/ArrangementUtil.java | 109 +- .../CompositeArrangementEntryMatcher.java | 34 +- ...aultArrangementEntryMatcherSerializer.java | 18 +- .../match/StdArrangementEntryMatcher.java | 7 +- .../ArrangementCompositeMatchCondition.java | 34 +- .../HierarchicalArrangementConditionNode.java | 57 - .../arrangement/ArrangementUtilTest.java | 91 - ...ArrangementEntryMatcherSerializerTest.java | 7 +- .../StandardArrangementEntryMatcherTest.java | 3 +- .../ArrangementChangeConditionCallback.java | 31 + .../arrangement/ArrangementConfigUtil.java | 471 ----- .../arrangement/ArrangementConstants.java | 4 +- .../ArrangementEditorTreeNode.java | 8 +- .../ArrangementGroupingRulesManager.java | 152 -- .../ArrangementRuleEditingModel.java | 84 - .../ArrangementRuleEditingModelBuilder.java | 123 -- .../ArrangementRuleEditingModelImpl.java | 284 --- .../arrangement/ArrangementRuleEditor.java | 103 +- .../arrangement/ArrangementRuleTree.java | 1846 ++++++++--------- .../arrangement/ArrangementSettingsPanel.java | 57 +- .../arrangement/ArrangementTreeNode.java | 121 -- .../newui/ArrangementMatchingRulesList.java | 103 + .../newui/ArrangementMatchingRulesPanel.java | 64 + .../ArrangementRepresentationAware.java} | 14 +- .../newui/ArrangementSettingsPanel2.java | 132 ++ .../node/ArrangementCheckBoxNode.java | 11 +- .../node/ArrangementComboBoxNode.java | 9 +- .../node/ArrangementSectionNode.java | 5 +- ...rrangementAndMatchConditionComponent.java} | 76 +- ...rangementAtomMatchConditionComponent.java} | 51 +- ...ementEditIconMatchConditionComponent.java} | 19 +- ...ementGroupingMatchConditionComponent.java} | 13 +- ...> ArrangementMatchConditionComponent.java} | 21 +- .../ArrangementMatchNodeComponentFactory.java | 116 +- .../arrangement/AbstractRearrangerTest.groovy | 2 +- 42 files changed, 1598 insertions(+), 3638 deletions(-) delete mode 100644 java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/AbstractArrangementRuleEditingModelTest.java delete mode 100644 java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/ArrangementConfigUtilTest.groovy delete mode 100644 java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelBuilderTest.java delete mode 100644 java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelImplTest.java create mode 100644 platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/ArrangementConditionInfo.java delete mode 100644 platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/model/HierarchicalArrangementConditionNode.java delete mode 100644 platform/lang-api/testSources/com/intellij/psi/codeStyle/arrangement/ArrangementUtilTest.java create mode 100644 platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementChangeConditionCallback.java delete mode 100644 platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementGroupingRulesManager.java delete mode 100644 platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModel.java delete mode 100644 platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelBuilder.java delete mode 100644 platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelImpl.java delete mode 100644 platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementTreeNode.java create mode 100644 platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/newui/ArrangementMatchingRulesList.java create mode 100644 platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/newui/ArrangementMatchingRulesPanel.java rename platform/{lang-api/src/com/intellij/psi/codeStyle/arrangement/ArrangementOperator.java => lang-impl/src/com/intellij/application/options/codeStyle/arrangement/newui/ArrangementRepresentationAware.java} (71%) create mode 100644 platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/newui/ArrangementSettingsPanel2.java rename platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/{ArrangementAndMatchNodeComponent.java => ArrangementAndMatchConditionComponent.java} (66%) rename platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/{ArrangementAtomMatchNodeComponent.java => ArrangementAtomMatchConditionComponent.java} (82%) rename platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/{ArrangementEditIconMatchNodeComponent.java => ArrangementEditIconMatchConditionComponent.java} (89%) rename platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/{ArrangementGroupingMatchNodeComponent.java => ArrangementGroupingMatchConditionComponent.java} (86%) rename platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/{ArrangementMatchNodeComponent.java => ArrangementMatchConditionComponent.java} (86%) diff --git a/java/java-impl/src/com/intellij/application/options/JavaArrangementPanel.java b/java/java-impl/src/com/intellij/application/options/JavaArrangementPanel.java index 00739c884670..dfaebf60e652 100644 --- a/java/java-impl/src/com/intellij/application/options/JavaArrangementPanel.java +++ b/java/java-impl/src/com/intellij/application/options/JavaArrangementPanel.java @@ -15,7 +15,7 @@ */ package com.intellij.application.options; -import com.intellij.application.options.codeStyle.arrangement.ArrangementSettingsPanel; +import com.intellij.application.options.codeStyle.arrangement.newui.ArrangementSettingsPanel2; import com.intellij.ide.highlighter.JavaFileType; import com.intellij.lang.java.JavaLanguage; import com.intellij.openapi.fileTypes.FileType; @@ -26,7 +26,7 @@ import org.jetbrains.annotations.NotNull; * @author Denis Zhdanov * @since 8/8/12 8:57 AM */ -public class JavaArrangementPanel extends ArrangementSettingsPanel { +public class JavaArrangementPanel extends ArrangementSettingsPanel2 { public JavaArrangementPanel(@NotNull CodeStyleSettings settings) { super(settings, JavaLanguage.INSTANCE); diff --git a/java/java-impl/src/com/intellij/psi/codeStyle/arrangement/JavaRearranger.java b/java/java-impl/src/com/intellij/psi/codeStyle/arrangement/JavaRearranger.java index 19e339512436..02e3e29555c4 100644 --- a/java/java-impl/src/com/intellij/psi/codeStyle/arrangement/JavaRearranger.java +++ b/java/java-impl/src/com/intellij/psi/codeStyle/arrangement/JavaRearranger.java @@ -154,7 +154,7 @@ public class JavaRearranger implements Rearranger, return; } - ArrangementCompositeMatchCondition composite = new ArrangementCompositeMatchCondition(ArrangementOperator.AND); + ArrangementCompositeMatchCondition composite = new ArrangementCompositeMatchCondition(); for (Object condition : conditions) { composite.addOperand(new ArrangementAtomMatchCondition(ArrangementUtil.parseType(condition), condition)); } diff --git a/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/AbstractArrangementRuleEditingModelTest.java b/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/AbstractArrangementRuleEditingModelTest.java deleted file mode 100644 index a5352a0e5bb4..000000000000 --- a/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/AbstractArrangementRuleEditingModelTest.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.application.options.codeStyle.arrangement; - -import com.intellij.openapi.util.Pair; -import com.intellij.psi.codeStyle.arrangement.JavaRearranger; -import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule; -import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType; -import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier; -import com.intellij.psi.codeStyle.arrangement.match.StdArrangementEntryMatcher; -import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition; -import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition; -import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingType; -import com.intellij.ui.treeStructure.Tree; -import com.intellij.util.containers.ContainerUtilRt; -import gnu.trove.TIntIntHashMap; -import gnu.trove.TIntObjectHashMap; -import org.jetbrains.annotations.NotNull; -import org.junit.Before; - -import javax.swing.*; -import javax.swing.tree.TreePath; -import java.util.Arrays; -import java.util.List; -import java.util.Set; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -/** - * @author Denis Zhdanov - * @since 8/16/12 11:04 AM - */ -public abstract class AbstractArrangementRuleEditingModelTest { - - @NotNull protected ArrangementRuleEditingModelBuilder myBuilder; - @NotNull protected JTree myTree; - @NotNull protected ArrangementTreeNode myRoot; - @NotNull protected TIntObjectHashMap myRowMappings; - @NotNull protected List> myGroupingRules; - - @Before - public void setUp() { - myBuilder = new ArrangementRuleEditingModelBuilder(); - myRoot = new ArrangementTreeNode(null); - myTree = new Tree(myRoot); - myTree.expandPath(new TreePath(myRoot)); - myRowMappings = new TIntObjectHashMap(); - myGroupingRules = ContainerUtilRt.newArrayList(); - myGroupingRules.add(ContainerUtilRt.newHashSet( - atom(ArrangementEntryType.FIELD), atom(ArrangementEntryType.METHOD) - )); - } - - protected void configure(@NotNull ArrangementMatchCondition matchCondition) { - Pair pair = myBuilder.build( - new StdArrangementMatchRule(new StdArrangementEntryMatcher(matchCondition)), myTree, myRoot, null, myGroupingRules - ); - assertNotNull(pair); - myRowMappings.put(pair.first.getRow(), pair.first); - } - - protected static ArrangementAtomMatchCondition atom(@NotNull Object condition) { - final ArrangementSettingType type; - if (condition instanceof ArrangementEntryType) { - type = ArrangementSettingType.TYPE; - } - else if (condition instanceof ArrangementModifier) { - type = ArrangementSettingType.MODIFIER; - } - else { - throw new IllegalArgumentException(String.format("Unexpected condition of class %s: %s", condition.getClass(), condition)); - } - return new ArrangementAtomMatchCondition(type, condition); - } - - protected void checkRows(int... rows) { - for (int row : rows) { - assertTrue( - String.format("Expected to find mappings for rows %s. Actual: %s", Arrays.toString(rows), Arrays.toString(myRowMappings.keys())), - myRowMappings.containsKey(row) - ); - } - } -} diff --git a/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/ArrangementConfigUtilTest.groovy b/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/ArrangementConfigUtilTest.groovy deleted file mode 100644 index eb5ce889cda3..000000000000 --- a/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/ArrangementConfigUtilTest.groovy +++ /dev/null @@ -1,507 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.application.options.codeStyle.arrangement - -import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingType -import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition -import gnu.trove.TIntIntHashMap -import org.jetbrains.annotations.NotNull -import org.jetbrains.annotations.Nullable -import org.junit.Test - -import javax.swing.tree.DefaultTreeModel - -import static org.junit.Assert.assertEquals -/** - * @author Denis Zhdanov - * @since 8/17/12 1:12 PM - */ -@SuppressWarnings(["GroovyAssignabilityCheck", "GroovyVariableNotAssigned"]) -class ArrangementConfigUtilTest { - - @Test - void replace() { - // Init. - def one; - def four; - def initial = new TreeNodeBuilder(). - '0' { -one = '1' { - '2' { - '3'() -four = '4'() - '5'()} - '6'()} - } - - // Modify. - def replacement = new TreeNodeBuilder(). - '1' { - '4'() - } - def rowMappings = doReplace(one, four, replacement) - - // Check - def expected = new TreeNodeBuilder(). - '0' { - '1' { - '2' { - '3'()} - '4'() - '2' { - '5'()} - '6'()} - } - assertNodesEqual(expected, initial) - checkRowMappings([5 : 6, 6 : 7], rowMappings) - } - - @Test - void replaceWithMergeToNodeAbove() { - // Init. - def from; - def to; - def initial = new TreeNodeBuilder(). - '0' { - '1'() { - '2'() - '3'()} -from = '4' { -to = '5'() - '6'()} - } - - // Modify. - def replacement = new TreeNodeBuilder(). - '1' { - '5'() - } - def rowMappings = doReplace(from, to, replacement) - - // Check. - def expected = new TreeNodeBuilder(). - '0' { - '1' { - '2'() - '3'() - '5'()} - '4' { - '6'()} - } - assertNodesEqual(expected, initial) - checkRowMappings([:], rowMappings) - } - - @Test - void replaceWithTwoLevelMergeToNodeAbove() { - // Init. - def node; - def initial = new TreeNodeBuilder(). - '0' { - '1'() { - '2'() - '3'()} -node = '4'() - '5'() { - '6'()} - } - - // Modify. - def replacement = new TreeNodeBuilder(). - '1' { - '4'() - } - def rowMappings = doReplace(node, node, replacement) - - // Check. - def expected = new TreeNodeBuilder(). - '0' { - '1' { - '2'() - '3'() - '4'()} - '5' { - '6'()} - } - assertNodesEqual(expected, initial) - checkRowMappings([:], rowMappings) - } - - @Test - void replaceWithTwoLevelMergeToNodeBelow() { - // Init. - def from; - def to; - def initial = new TreeNodeBuilder(). - '0' { -from = '1'() { - '2'() -to = '3'()} - '4'() { - '5'() } - } - - // Modify. - def replacement = new TreeNodeBuilder(). - '4' { - '3'() - } - def rowMappings = doReplace(from, to, replacement) - - // Check. - def expected = new TreeNodeBuilder(). - '0' { - '1' { - '2'()} - '4' { - '3'() - '5'()} - } - assertNodesEqual(expected, initial) - checkRowMappings([:], rowMappings) - } - - @Test - void replaceFirstChildWithMergeBelow() { - // Init. - def from; - def to; - def initial = new TreeNodeBuilder(). - '0' { -from = '1'() { -to = '2'()} - '3'() { - '4'() } - '1' { - '5'() - '6'()} - } - - // Modify. - def replacement = new TreeNodeBuilder(). - '3' { - '2'() - } - def rowMappings = doReplace(from, to, replacement) - - // Check. - def expected = new TreeNodeBuilder(). - '0' { - '3' { - '2'() - '4'()} - '1' { - '5'() - '6'()} - } - assertNodesEqual(expected, initial) - checkRowMappings([ 4 : 3, 5 : 4, 6 : 5, 7 : 6 ], rowMappings) - } - - @Test - void addWithoutMergeAbove() { - def initial = new TreeNodeBuilder(). - '0' { - '1'() - '2' { - '3' { - '4'() - } - } - '5'() - } - - def toAdd = new TreeNodeBuilder(). - '2' { - '3' { - '6'() - } - } - - def expected = new TreeNodeBuilder(). - '0' { - '2' { - '3' { - '6'() - } - } - '1'() - '2' { - '3' { - '4'() - } - } - '5'() - } - - doInsert(initial, 0, toAdd) - assertNodesEqual(expected, initial) - } - - @Test - void addWithMergeAbove() { - def initial = new TreeNodeBuilder(). - '0' { - '1'() - '2' { - '3' { - '4'() - } - } - '5'() - } - - def toAdd = new TreeNodeBuilder(). - '2' { - '3' { - '6'() - } - } - - def expected = new TreeNodeBuilder(). - '0' { - '1'() - '2' { - '3' { - '6'() - '4'() - } - } - '5'() - } - - doInsert(initial, 1, toAdd) - assertNodesEqual(expected, initial) - } - - @Test - void addWithMergeBelow() { - def initial = new TreeNodeBuilder(). - '0' { - '1'() - '2' { - '3' { - '4'() - } - } - '5'() - } - - def toAdd = new TreeNodeBuilder(). - '2' { - '3' { - '6'() - } - } - - def expected = new TreeNodeBuilder(). - '0' { - '1'() - '2' { - '3' { - '4'() - '6'() - } - } - '5'() - } - - doInsert(initial, 2, toAdd) - assertNodesEqual(expected, initial) - } - - @Test - void addWithoutMergeBelow() { - def initial = new TreeNodeBuilder(). - '0' { - '1'() - '2' { - '3' { - '4'() - } - } - '5'() - } - - def toAdd = new TreeNodeBuilder(). - '2' { - '3' { - '6'() - } - } - - def expected = new TreeNodeBuilder(). - '0' { - '1'() - '2' { - '3' { - '4'() - } - } - '5'() - '2' { - '3' { - '6'() - } - } - } - - doInsert(initial, 3, toAdd) - assertNodesEqual(expected, initial) - } - - @Test - void removeFirst() { - def from; - def to; - def initial = new TreeNodeBuilder(). - '0' { -from = '1' { -to = '2'() - '3'()} - '4'() - } - - def expected = new TreeNodeBuilder(). - '0' { - '1' { - '3'()} - '4'() - } - - def rowMappings = doRemove(from, to) - assertNodesEqual(expected, initial) - checkRowMappings([ 3 : 2, 4 : 3 ], rowMappings) - } - - @Test - void removeWithMerge() { - def from; - def to; - def initial = new TreeNodeBuilder(). - '0' { - '1' { - '2'()} -from = '3'() { -to = '4'()} - '1' { - '5'()} - } - - def expected = new TreeNodeBuilder(). - '0' { - '1' { - '2'() - '5'()} - } - - def rowMappings = doRemove(from, to) - assertNodesEqual(expected, initial) - checkRowMappings([ 6 : 3 ], rowMappings) - } - - @Test - void removeSingleNodeWithMerge() { - def node; - def initial = new TreeNodeBuilder(). - '0' { - '1' { - '2'()} -node = '3'() - '1' { - '4'()} - } - - def expected = new TreeNodeBuilder(). - '0' { - '1' { - '2'() - '4'()} - } - - def rowMappings = doRemove(node, node) - assertNodesEqual(expected, initial) - checkRowMappings([ 5 : 3 ], rowMappings) - } - - @Test - void removeWithSingleNodeBelowMerge() { - def node; - def initial = new TreeNodeBuilder(). - '0' { - '1' { - '2'()} -node = '3'() - '1'() - } - - def expected = new TreeNodeBuilder(). - '0' { - '1' { - '2'()} - '1'() - } - - def rowMappings = doRemove(node, node) - assertNodesEqual(expected, initial) - checkRowMappings([ 4 : 3 ], rowMappings) - } - - private static def doReplace(from, to, replacement) { - ArrangementConfigUtil.replace(from, to, replacement, new DefaultTreeModel(ArrangementConfigUtil.getRoot(from)), true) - } - - private static def doInsert(parent, i, child) { - ArrangementConfigUtil.insert(parent, i, child, new DefaultTreeModel(ArrangementConfigUtil.getRoot(parent))) - } - - private static def doRemove(from, to) { - ArrangementConfigUtil.remove(from, to, new DefaultTreeModel(ArrangementConfigUtil.getRoot(from)), true) - } - - private static void assertNodesEqual(@NotNull ArrangementTreeNode expected, @NotNull ArrangementTreeNode actual) { - assertEquals(expected.userObject, actual.userObject) - assertEquals(expected.childCount, actual.childCount) - for (i in 0.. expected, @NotNull TIntIntHashMap actual) { - assertEquals(expected.size(), actual.size()) - expected.each {key, value -> assertEquals(value, actual.get(key)) } - } -} - -public class TreeNodeBuilder extends BuilderSupport { - - @Override - protected Object createNode(Object name) { - def result = new ArrangementTreeNode(new ArrangementAtomMatchCondition(ArrangementSettingType.MODIFIER, name)) - currentNode?.add(result) - result - } - - @Nullable - ArrangementTreeNode getCurrentNode() { - getCurrent() as ArrangementTreeNode - } - - @Override protected void setParent(Object parent, Object child) { } - @Override protected Object createNode(Object name, Object value) { throw new UnsupportedOperationException() } - @Override protected Object createNode(Object name, Map attributes) { throw new UnsupportedOperationException() } - @Override protected Object createNode(Object name, Map attributes, Object value) { throw new UnsupportedOperationException() } -} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelBuilderTest.java b/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelBuilderTest.java deleted file mode 100644 index d313c7d3ec5d..000000000000 --- a/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelBuilderTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.application.options.codeStyle.arrangement; - -import org.junit.Test; - -import static com.intellij.psi.codeStyle.arrangement.ArrangementUtil.and; -import static com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType.FIELD; -import static com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier.*; -import static org.junit.Assert.*; - -/** - * @author Denis Zhdanov - * @since 08/15/2012 - */ -public class ArrangementRuleEditingModelBuilderTest extends AbstractArrangementRuleEditingModelTest { - - @Test - public void mapToTheSameLayer() { - configure(and(atom(PUBLIC), atom(STATIC))); - checkRows(1); - ArrangementRuleEditingModel model = myRowMappings.get(1); - assertTrue(model.hasCondition(PUBLIC)); - assertTrue(model.hasCondition(STATIC)); - assertFalse(model.hasCondition(PRIVATE)); - assertEquals(1, myRoot.getChildCount()); - assertEquals(and(atom(PUBLIC), atom(STATIC)), myRoot.getFirstChild().getBackingCondition()); - } - - @Test - public void splitIntoTwoLayers() { - configure(and(atom(FIELD), atom(PUBLIC), atom(STATIC))); - - checkRows(2); - - ArrangementTreeNode fieldUiNode = myRoot.getFirstChild(); - assertNotNull(fieldUiNode); - assertEquals(atom(FIELD), fieldUiNode.getBackingCondition()); - - ArrangementTreeNode modifiersUiNode = fieldUiNode.getFirstChild(); - assertNotNull(modifiersUiNode); - assertEquals(and(atom(PUBLIC), atom(STATIC)), modifiersUiNode.getBackingCondition()); - } - - @Test - public void addToExistingLayer() { - configure(and(atom(PUBLIC), atom(STATIC), atom(FIELD))); - configure(and(atom(PRIVATE), atom(FIELD))); - - checkRows(2, 3); - - ArrangementTreeNode fieldUiNode = myRoot.getFirstChild(); - assertNotNull(fieldUiNode); - assertEquals(atom(FIELD), fieldUiNode.getBackingCondition()); - - ArrangementTreeNode publicStaticUiNode = fieldUiNode.getFirstChild(); - assertNotNull(publicStaticUiNode); - assertEquals(and(atom(PUBLIC), atom(STATIC)), publicStaticUiNode.getBackingCondition()); - - ArrangementTreeNode privateUiNode = fieldUiNode.getLastChild(); - assertNotNull(privateUiNode); - assertEquals(atom(PRIVATE), privateUiNode.getBackingCondition()); - } -} diff --git a/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelImplTest.java b/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelImplTest.java deleted file mode 100644 index 3f6475c9d266..000000000000 --- a/java/java-tests/testSrc/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelImplTest.java +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.application.options.codeStyle.arrangement; - -import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition; -import org.jetbrains.annotations.NotNull; -import org.junit.Test; - -import static com.intellij.psi.codeStyle.arrangement.ArrangementUtil.and; -import static com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType.FIELD; -import static com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier.PUBLIC; -import static com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier.STATIC; -import static org.junit.Assert.*; - -/** - * @author Denis Zhdanov - * @since 8/16/12 10:07 AM - */ -public class ArrangementRuleEditingModelImplTest extends AbstractArrangementRuleEditingModelTest { - - @Test - public void addConditionToSameLevel() { - configure(atom(PUBLIC)); - ArrangementRuleEditingModel model = myRowMappings.get(1); - assertNotNull(model); - model.addAndCondition(atom(STATIC)); - - ArrangementTreeNode child = myRoot.getFirstChild(); - assertNotNull(child); - ArrangementMatchCondition expectedMatchCondition = and(atom(PUBLIC), atom(STATIC)); - assertEquals(expectedMatchCondition, child.getBackingCondition()); - assertEquals(expectedMatchCondition, model.getCondition()); - } - - @Test - public void buildNewSingleLevel() { - configure(atom(PUBLIC)); - ArrangementRuleEditingModelImpl model = myRowMappings.get(1); - assertNotNull(model); - assertEquals(1, myRowMappings.size()); - - model.addAndCondition(atom(FIELD)); - - assertEquals(2, model.getRow()); - assertEquals(and(atom(FIELD), atom(PUBLIC)), model.getCondition()); - - ArrangementTreeNode fieldNode = myRoot.getFirstChild(); - assertNotNull(fieldNode); - assertEquals(atom(FIELD), fieldNode.getBackingCondition()); - - ArrangementTreeNode publicNode = fieldNode.getFirstChild(); - assertNotNull(publicNode); - assertEquals(atom(PUBLIC), publicNode.getBackingCondition()); - } - - @Test - public void addConditionToSameNestedLevel() { - configure(and(atom(FIELD), atom(PUBLIC))); - ArrangementRuleEditingModel model = myRowMappings.get(2); - assertNotNull(model); - assertEquals(1, myRowMappings.size()); - - model.addAndCondition(atom(STATIC)); - - assertEquals(1, myRowMappings.size()); - assertSame(model, myRowMappings.get(2)); - assertEquals(and(atom(FIELD), atom(PUBLIC), atom(STATIC)), model.getCondition()); - - ArrangementTreeNode fieldNode = myRoot.getFirstChild(); - assertNotNull(fieldNode); - assertEquals(atom(FIELD), fieldNode.getBackingCondition()); - - ArrangementTreeNode modifiersNode = fieldNode.getFirstChild(); - assertNotNull(modifiersNode); - assertEquals(and(atom(PUBLIC), atom(STATIC)), modifiersNode.getBackingCondition()); - } - - @Test - public void removeAndKeepAllLevels() { - configure(and(atom(FIELD), atom(PUBLIC), atom(STATIC))); - ArrangementRuleEditingModelImpl model = myRowMappings.get(2); - assertNotNull(model); - assertEquals(1, myRowMappings.size()); - - model.removeAndCondition(atom(PUBLIC)); - - assertEquals(1, myRowMappings.size()); - assertSame(model, myRowMappings.get(2)); - assertEquals(and(atom(FIELD), atom(STATIC)), model.getCondition()); - - ArrangementTreeNode fieldNode = myRoot.getFirstChild(); - assertNotNull(fieldNode); - assertEquals(atom(FIELD), fieldNode.getBackingCondition()); - - ArrangementTreeNode modifiersNode = fieldNode.getFirstChild(); - assertNotNull(modifiersNode); - assertEquals(atom(STATIC), modifiersNode.getBackingCondition()); - - checkModelTreeNodesConsistency(model); - } - - @Test - public void removeLastRowCondition() { - configure(and(atom(FIELD), atom(PUBLIC))); - ArrangementRuleEditingModelImpl model = myRowMappings.get(2); - assertNotNull(model); - assertEquals(1, myRowMappings.size()); - - model.removeAndCondition(atom(PUBLIC)); - - assertEquals(1, model.getRow()); - assertEquals(atom(FIELD), model.getCondition()); - - ArrangementTreeNode fieldNode = myRoot.getFirstChild(); - assertNotNull(fieldNode); - assertEquals(atom(FIELD), fieldNode.getBackingCondition()); - - assertEquals(0, fieldNode.getChildCount()); - } - - @Test - public void removeFirstRowConditionFromMultiChildrenParent() { - configure(and(atom(FIELD), atom(PUBLIC))); - configure(and(atom(FIELD), atom(STATIC))); - - ArrangementRuleEditingModelImpl modelToChange = myRowMappings.get(2); - assertNotNull(modelToChange); - - ArrangementRuleEditingModelImpl siblingModel = myRowMappings.get(3); - assertNotNull(siblingModel); - assertEquals(2, myRowMappings.size()); - - modelToChange.removeAndCondition(atom(PUBLIC)); - - assertSame(3, siblingModel.getRow()); - assertEquals(and(atom(FIELD), atom(STATIC)), siblingModel.getCondition()); - - ArrangementTreeNode fieldNode = myRoot.getFirstChild(); - assertNotNull(fieldNode); - assertEquals(atom(FIELD), fieldNode.getBackingCondition()); - - ArrangementTreeNode staticNode = fieldNode.getFirstChild(); - assertNotNull(staticNode); - assertEquals(atom(STATIC), staticNode.getBackingCondition()); - - assertNull(staticNode.getNextNode()); - } - - @Test - public void removeLastRowConditionFromMultiChildrenParent() { - configure(and(atom(FIELD), atom(PUBLIC))); - configure(and(atom(FIELD), atom(STATIC))); - - assertEquals(2, myRowMappings.size()); - ArrangementRuleEditingModel siblingModel = myRowMappings.get(2); - assertNotNull(siblingModel); - - ArrangementRuleEditingModel modelToChange = myRowMappings.get(3); - assertNotNull(modelToChange); - - modelToChange.removeAndCondition(atom(STATIC)); - - assertEquals(2, myRowMappings.size()); - assertSame(siblingModel, myRowMappings.get(2)); - assertEquals(and(atom(FIELD), atom(PUBLIC)), siblingModel.getCondition()); - - ArrangementTreeNode fieldNode = myRoot.getFirstChild(); - assertNotNull(fieldNode); - assertEquals(atom(FIELD), fieldNode.getBackingCondition()); - - ArrangementTreeNode publicNode = fieldNode.getFirstChild(); - assertNotNull(publicNode); - assertEquals(atom(PUBLIC), publicNode.getBackingCondition()); - - assertNull(publicNode.getNextNode()); - } - - private void checkModelTreeNodesConsistency(@NotNull ArrangementRuleEditingModelImpl model) { - model.refreshTreeNodes(); - ArrangementTreeNode root = ArrangementConfigUtil.getRoot(model.getBottomMost()); - assertSame(myRoot, root); - assertSame(root, ArrangementConfigUtil.getRoot(model.getTopMost())); - } -} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/ArrangementConditionInfo.java b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/ArrangementConditionInfo.java new file mode 100644 index 000000000000..7825b47a5f96 --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/ArrangementConditionInfo.java @@ -0,0 +1,71 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.psi.codeStyle.arrangement; + +import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition; +import com.intellij.psi.codeStyle.arrangement.model.ArrangementCompositeMatchCondition; +import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition; +import com.intellij.util.containers.ContainerUtilRt; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Iterator; +import java.util.Set; + +/** + * @author Denis Zhdanov + * @since 10/31/12 12:30 PM + */ +public class ArrangementConditionInfo { + + @NotNull private final Set myAtomConditions = ContainerUtilRt.newHashSet(); + @NotNull private final Set myConditions = ContainerUtilRt.newHashSet(); + + public void addAtomCondition(@NotNull ArrangementAtomMatchCondition condition) { + myAtomConditions.add(condition); + myConditions.add(condition.getValue()); + } + + public boolean hasCondition(@NotNull Object condition) { + return myConditions.contains(condition); + } + + public void removeCondition(@NotNull Object condition) { + if (!myConditions.remove(condition)) { + return; + } + for (Iterator iterator = myAtomConditions.iterator(); iterator.hasNext(); ) { + ArrangementAtomMatchCondition c = iterator.next(); + if (c.getValue().equals(condition)) { + iterator.remove(); + break; + } + } + } + + @Nullable + public ArrangementMatchCondition buildCondition() { + if (myAtomConditions.isEmpty()) { + return null; + } + if (myAtomConditions.size() == 1) { + return myAtomConditions.iterator().next(); + } + else { + return new ArrangementCompositeMatchCondition(myAtomConditions); + } + } +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/ArrangementUtil.java b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/ArrangementUtil.java index 1ae9ca86d1ca..6815eeb2ff48 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/ArrangementUtil.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/ArrangementUtil.java @@ -26,8 +26,6 @@ import org.jdom.Element; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; import java.util.Set; /** @@ -63,8 +61,8 @@ public class ArrangementUtil { //endregion @NotNull - public static ArrangementMatchCondition and(@NotNull ArrangementMatchCondition... nodes) { - final ArrangementCompositeMatchCondition result = new ArrangementCompositeMatchCondition(ArrangementOperator.AND); + public static ArrangementMatchCondition combine(@NotNull ArrangementMatchCondition... nodes) { + final ArrangementCompositeMatchCondition result = new ArrangementCompositeMatchCondition(); final ArrangementMatchConditionVisitor visitor = new ArrangementMatchConditionVisitor() { @Override public void visit(@NotNull ArrangementAtomMatchCondition node) { @@ -73,13 +71,8 @@ public class ArrangementUtil { @Override public void visit(@NotNull ArrangementCompositeMatchCondition node) { - if (node.getOperator() == ArrangementOperator.AND) { - for (ArrangementMatchCondition operand : node.getOperands()) { - operand.invite(this); - } - } - else { - result.addOperand(node); + for (ArrangementMatchCondition operand : node.getOperands()) { + operand.invite(this); } } }; @@ -157,81 +150,6 @@ public class ArrangementUtil { } } - @SuppressWarnings("AssignmentToForLoopParameter") - @NotNull - public static HierarchicalArrangementConditionNode group(@NotNull ArrangementMatchCondition condition, - @NotNull List> groupingRules) - { - if (groupingRules.isEmpty()) { - // No grouping rules have been provided, use a flat structure. - return new HierarchicalArrangementConditionNode(condition); - } - - final List conditions = new ArrayList(); - condition.invite(new ArrangementMatchConditionVisitor() { - @Override - public void visit(@NotNull ArrangementAtomMatchCondition condition) { - conditions.add(condition); - } - - @Override - public void visit(@NotNull ArrangementCompositeMatchCondition condition) { - if (condition.getOperator() == ArrangementOperator.AND && conditions.isEmpty() /* Don't process nested composite conditions*/) { - for (ArrangementMatchCondition operand : condition.getOperands()) { - conditions.add(operand); - } - } - else { - conditions.add(condition); - } - } - }); - if (conditions.isEmpty()) { - return new HierarchicalArrangementConditionNode(condition); - } - - HierarchicalArrangementConditionNode result = null; - for (Set rules : groupingRules) { - for (ArrangementMatchCondition rule : rules) { - for (int i = 0; i < conditions.size(); i++) { - ArrangementMatchCondition c = conditions.get(i); - if (rule.equals(c)) { - conditions.remove(i--); - HierarchicalArrangementConditionNode node = new HierarchicalArrangementConditionNode(c); - if (result == null) { - result = node; - } - else { - result.setChild(node); - } - } - } - } - } - - if (!conditions.isEmpty()) { - HierarchicalArrangementConditionNode node; - if (conditions.size() == 1) { - node = new HierarchicalArrangementConditionNode(conditions.get(0)); - } - else { - ArrangementCompositeMatchCondition c = new ArrangementCompositeMatchCondition(ArrangementOperator.AND); - for (ArrangementMatchCondition operand : conditions) { - c.addOperand(operand); - } - node = new HierarchicalArrangementConditionNode(c); - } - if (result == null) { - result = node; - } - else { - result.setChild(node); - } - } - assert result != null; - return result; - } - public static Set flatten(@NotNull Iterable> data) { Set result = ContainerUtilRt.newHashSet(); for (Iterable i : data) { @@ -241,4 +159,23 @@ public class ArrangementUtil { } return result; } + + @NotNull + public static ArrangementConditionInfo extractConditions(@NotNull ArrangementMatchCondition condition) { + final ArrangementConditionInfo result = new ArrangementConditionInfo(); + condition.invite(new ArrangementMatchConditionVisitor() { + @Override + public void visit(@NotNull ArrangementAtomMatchCondition condition) { + result.addAtomCondition(condition); + } + + @Override + public void visit(@NotNull ArrangementCompositeMatchCondition condition) { + for (ArrangementMatchCondition operand : condition.getOperands()) { + operand.invite(this); + } + } + }); + return result; + } } diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/match/CompositeArrangementEntryMatcher.java b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/match/CompositeArrangementEntryMatcher.java index fef86b98a399..1cd335326a2e 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/match/CompositeArrangementEntryMatcher.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/match/CompositeArrangementEntryMatcher.java @@ -16,7 +16,6 @@ package com.intellij.psi.codeStyle.arrangement.match; import com.intellij.psi.codeStyle.arrangement.ArrangementEntry; -import com.intellij.psi.codeStyle.arrangement.ArrangementOperator; import org.jetbrains.annotations.NotNull; import java.util.*; @@ -28,35 +27,19 @@ import java.util.*; public class CompositeArrangementEntryMatcher implements ArrangementEntryMatcher { @NotNull private final Set myMatchers = new HashSet(); - @NotNull private final ArrangementOperator myOperator; - public CompositeArrangementEntryMatcher(@NotNull ArrangementOperator operator, @NotNull ArrangementEntryMatcher... matchers) { - myOperator = operator; + public CompositeArrangementEntryMatcher(@NotNull ArrangementEntryMatcher... matchers) { myMatchers.addAll(Arrays.asList(matchers)); } @Override public boolean isMatched(@NotNull ArrangementEntry entry) { for (ArrangementEntryMatcher matcher : myMatchers) { - boolean matched = matcher.isMatched(entry); - if (matched && myOperator == ArrangementOperator.OR) { - return true; - } - else if (!matched && myOperator == ArrangementOperator.AND) { + if (!matcher.isMatched(entry)) { return false; } } - return myOperator == ArrangementOperator.AND; - } - - @NotNull - public ArrangementOperator getOperator() { - return myOperator; - } - - @NotNull - public Collection getMatchers() { - return myMatchers; + return true; } public void addMatcher(@NotNull ArrangementEntryMatcher rule) { @@ -65,9 +48,7 @@ public class CompositeArrangementEntryMatcher implements ArrangementEntryMatcher @Override public int hashCode() { - int result = myMatchers.hashCode(); - result = 31 * result + myOperator.hashCode(); - return result; + return myMatchers.hashCode(); } @Override @@ -77,14 +58,11 @@ public class CompositeArrangementEntryMatcher implements ArrangementEntryMatcher CompositeArrangementEntryMatcher matcher = (CompositeArrangementEntryMatcher)o; - if (!myMatchers.equals(matcher.myMatchers)) return false; - if (myOperator != matcher.myOperator) return false; - - return true; + return myMatchers.equals(matcher.myMatchers); } @Override public String toString() { - return String.format("%s of those: %s", myOperator == ArrangementOperator.AND ? "all" : "any", myMatchers); + return String.format("all of those: %s", myMatchers); } } diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/match/DefaultArrangementEntryMatcherSerializer.java b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/match/DefaultArrangementEntryMatcherSerializer.java index cd4d6a698356..e0de01da9ec3 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/match/DefaultArrangementEntryMatcherSerializer.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/match/DefaultArrangementEntryMatcherSerializer.java @@ -16,7 +16,6 @@ package com.intellij.psi.codeStyle.arrangement.match; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.psi.codeStyle.arrangement.ArrangementOperator; import com.intellij.psi.codeStyle.arrangement.model.*; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.HashSet; @@ -72,14 +71,13 @@ public class DefaultArrangementEntryMatcherSerializer { } }; - private static final Logger LOG = Logger.getInstance("#" + DefaultArrangementEntryMatcherSerializer.class.getName()); - private static final Set COMPOSITE_OPERATORS = new HashSet(); + private static final Logger LOG = Logger.getInstance("#" + DefaultArrangementEntryMatcherSerializer.class.getName()); + + @NotNull private static final String COMPOSITE_CONDITION_NAME = "AND"; + private static final Set ATOM_SETTINGS_TYPES = new HashSet(); static { - for (ArrangementOperator operator : ArrangementOperator.values()) { - COMPOSITE_OPERATORS.add(operator.toString()); - } for (ArrangementSettingType type : ArrangementSettingType.values()) { ATOM_SETTINGS_TYPES.add(type.toString()); } @@ -115,7 +113,7 @@ public class DefaultArrangementEntryMatcherSerializer { @Nullable private static ArrangementMatchCondition deserializeCondition(@NotNull Element matcherElement) { String name = matcherElement.getName(); - if (!COMPOSITE_OPERATORS.contains(name)) { + if (!COMPOSITE_CONDITION_NAME.equals(name)) { if (ATOM_SETTINGS_TYPES.contains(name)) { return deserializeAtomCondition(matcherElement); } @@ -123,13 +121,13 @@ public class DefaultArrangementEntryMatcherSerializer { LOG.warn(String.format( "Can't deserialize an arrangement entry matcher from matchElement with name '%s'. Reason: only the following elements" + "are supported: %s and %s", - name, COMPOSITE_OPERATORS, ATOM_SETTINGS_TYPES + name, COMPOSITE_CONDITION_NAME, ATOM_SETTINGS_TYPES )); return null; } } else { - ArrangementCompositeMatchCondition composite = new ArrangementCompositeMatchCondition(ArrangementOperator.valueOf(name)); + ArrangementCompositeMatchCondition composite = new ArrangementCompositeMatchCondition(); for (Object child : matcherElement.getChildren()) { ArrangementMatchCondition deserialised = deserializeCondition((Element)child); if (deserialised != null) { @@ -175,7 +173,7 @@ public class DefaultArrangementEntryMatcherSerializer { @Override public void visit(@NotNull ArrangementCompositeMatchCondition condition) { - Element composite = new Element(condition.getOperator().toString()); + Element composite = new Element(COMPOSITE_CONDITION_NAME); if (result == null) { result = composite; } diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/match/StdArrangementEntryMatcher.java b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/match/StdArrangementEntryMatcher.java index f0090494a3be..8dddc51d3378 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/match/StdArrangementEntryMatcher.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/match/StdArrangementEntryMatcher.java @@ -16,7 +16,6 @@ package com.intellij.psi.codeStyle.arrangement.match; import com.intellij.psi.codeStyle.arrangement.ArrangementEntry; -import com.intellij.psi.codeStyle.arrangement.ArrangementOperator; import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition; import com.intellij.psi.codeStyle.arrangement.model.ArrangementCompositeMatchCondition; import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition; @@ -93,7 +92,6 @@ public class StdArrangementEntryMatcher implements ArrangementEntryMatcher { @NotNull private final Set myTypes = EnumSet.noneOf(ArrangementEntryType.class); @NotNull private final Set myModifiers = EnumSet.noneOf(ArrangementModifier.class); - private ArrangementOperator myOperator; private boolean nestedComposite; @Override @@ -110,7 +108,6 @@ public class StdArrangementEntryMatcher implements ArrangementEntryMatcher { @Override public void visit(@NotNull ArrangementCompositeMatchCondition condition) { if (!nestedComposite) { - myOperator = condition.getOperator(); nestedComposite = true; for (ArrangementMatchCondition c : condition.getOperands()) { c.invite(this); @@ -126,7 +123,7 @@ public class StdArrangementEntryMatcher implements ArrangementEntryMatcher { public ArrangementEntryMatcher getMatcher() { ByTypeArrangementEntryMatcher byType = myTypes.isEmpty() ? null : new ByTypeArrangementEntryMatcher(myTypes); ByModifierArrangementEntryMatcher byModifiers = myModifiers.isEmpty() ? null : new ByModifierArrangementEntryMatcher(myModifiers); - if (byType == null && byModifiers == null && (myOperator == null || myMatchers.isEmpty())) { + if (byType == null && byModifiers == null && myMatchers.isEmpty()) { return ArrangementEntryMatcher.EMPTY; } if (myMatchers.isEmpty() && (byType == null ^ byModifiers == null)) { @@ -136,7 +133,7 @@ public class StdArrangementEntryMatcher implements ArrangementEntryMatcher { return myMatchers.get(0); } else { - CompositeArrangementEntryMatcher result = new CompositeArrangementEntryMatcher(myOperator); + CompositeArrangementEntryMatcher result = new CompositeArrangementEntryMatcher(); for (ArrangementEntryMatcher matcher : myMatchers) { result.addMatcher(matcher); } diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/model/ArrangementCompositeMatchCondition.java b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/model/ArrangementCompositeMatchCondition.java index 912d6b26c521..9d29a0d1671f 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/model/ArrangementCompositeMatchCondition.java +++ b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/model/ArrangementCompositeMatchCondition.java @@ -16,10 +16,10 @@ package com.intellij.psi.codeStyle.arrangement.model; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.psi.codeStyle.arrangement.ArrangementOperator; import com.intellij.util.containers.HashSet; import org.jetbrains.annotations.NotNull; +import java.util.Collection; import java.util.Set; /** @@ -33,10 +33,12 @@ import java.util.Set; public class ArrangementCompositeMatchCondition implements ArrangementMatchCondition { @NotNull private final Set myOperands = new HashSet(); - @NotNull private final ArrangementOperator myOperator; - - public ArrangementCompositeMatchCondition(@NotNull ArrangementOperator operator) { - myOperator = operator; + + public ArrangementCompositeMatchCondition() { + } + + public ArrangementCompositeMatchCondition(@NotNull Collection conditions) { + myOperands.addAll(conditions); } @NotNull @@ -54,11 +56,6 @@ public class ArrangementCompositeMatchCondition implements ArrangementMatchCondi myOperands.remove(condition); } - @NotNull - public ArrangementOperator getOperator() { - return myOperator; - } - @Override public void invite(@NotNull ArrangementMatchConditionVisitor visitor) { visitor.visit(this); @@ -67,7 +64,7 @@ public class ArrangementCompositeMatchCondition implements ArrangementMatchCondi @NotNull @Override public ArrangementCompositeMatchCondition clone() { - ArrangementCompositeMatchCondition result = new ArrangementCompositeMatchCondition(myOperator); + ArrangementCompositeMatchCondition result = new ArrangementCompositeMatchCondition(); for (ArrangementMatchCondition operand : myOperands) { result.addOperand(operand.clone()); } @@ -76,9 +73,7 @@ public class ArrangementCompositeMatchCondition implements ArrangementMatchCondi @Override public int hashCode() { - int result = myOperands.hashCode(); - result = 31 * result + myOperator.hashCode(); - return result; + return myOperands.hashCode(); } @Override @@ -92,18 +87,11 @@ public class ArrangementCompositeMatchCondition implements ArrangementMatchCondi ArrangementCompositeMatchCondition setting = (ArrangementCompositeMatchCondition)o; - if (!myOperands.equals(setting.myOperands)) { - return false; - } - if (myOperator != setting.myOperator) { - return false; - } - - return true; + return myOperands.equals(setting.myOperands); } @Override public String toString() { - return String.format("(%s)", StringUtil.join(myOperands, myOperator == ArrangementOperator.AND ? " and " : " or ")); + return String.format("(%s)", StringUtil.join(myOperands, " and ")); } } diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/model/HierarchicalArrangementConditionNode.java b/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/model/HierarchicalArrangementConditionNode.java deleted file mode 100644 index be36f853ba78..000000000000 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/model/HierarchicalArrangementConditionNode.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.psi.codeStyle.arrangement.model; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * {@link #getCurrent() Wraps} {@link ArrangementMatchCondition} in order to allow to build {@link #getChild() hierarchy} - * from a plain sequence of them - *

- * Not thread-safe. - * - * @author Denis Zhdanov - * @since 8/8/12 1:23 PM - */ -public class HierarchicalArrangementConditionNode { - - @NotNull private final ArrangementMatchCondition myCurrent; - @Nullable private HierarchicalArrangementConditionNode myChild; - - public HierarchicalArrangementConditionNode(@NotNull ArrangementMatchCondition current) { - myCurrent = current; - } - - @NotNull - public ArrangementMatchCondition getCurrent() { - return myCurrent; - } - - @Nullable - public HierarchicalArrangementConditionNode getChild() { - return myChild; - } - - public void setChild(@Nullable HierarchicalArrangementConditionNode child) { - myChild = child; - } - - @Override - public String toString() { - return myCurrent.toString(); - } -} diff --git a/platform/lang-api/testSources/com/intellij/psi/codeStyle/arrangement/ArrangementUtilTest.java b/platform/lang-api/testSources/com/intellij/psi/codeStyle/arrangement/ArrangementUtilTest.java deleted file mode 100644 index 4325a6e6799f..000000000000 --- a/platform/lang-api/testSources/com/intellij/psi/codeStyle/arrangement/ArrangementUtilTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.psi.codeStyle.arrangement; - -import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType; -import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier; -import com.intellij.psi.codeStyle.arrangement.model.*; -import org.junit.Test; - -import java.util.Collections; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -/** - * @author Denis Zhdanov - * @since 9/12/12 8:10 PM - */ -public class ArrangementUtilTest { - - @Test - public void oneLevelGroupingAndMultipleNonGroupedNodes() { - ArrangementCompositeMatchCondition condition = new ArrangementCompositeMatchCondition(ArrangementOperator.AND); - ArrangementMatchCondition abstractCondition = - new ArrangementAtomMatchCondition(ArrangementSettingType.MODIFIER, ArrangementModifier.ABSTRACT); - ArrangementMatchCondition methodCondition - = new ArrangementAtomMatchCondition(ArrangementSettingType.TYPE, ArrangementEntryType.METHOD); - ArrangementMatchCondition publicCondition = - new ArrangementAtomMatchCondition(ArrangementSettingType.MODIFIER, ArrangementModifier.PUBLIC); - condition.addOperand(abstractCondition); - condition.addOperand(methodCondition); - condition.addOperand(publicCondition); - - HierarchicalArrangementConditionNode grouped = - ArrangementUtil.group(condition, Collections.singletonList(Collections.singleton(methodCondition))); - assertEquals(methodCondition, grouped.getCurrent()); - - HierarchicalArrangementConditionNode child = grouped.getChild(); - assertNotNull(child); - ArrangementCompositeMatchCondition expectedChildCondition = new ArrangementCompositeMatchCondition(ArrangementOperator.AND); - expectedChildCondition.addOperand(abstractCondition); - expectedChildCondition.addOperand(publicCondition); - assertEquals(expectedChildCondition, child.getCurrent()); - - assertNull(child.getChild()); - } - - @Test - public void oneLevelGroupingAndSingleNonGroupedNode() { - ArrangementCompositeMatchCondition condition = new ArrangementCompositeMatchCondition(ArrangementOperator.AND); - ArrangementMatchCondition abstractCondition = - new ArrangementAtomMatchCondition(ArrangementSettingType.MODIFIER, ArrangementModifier.ABSTRACT); - ArrangementMatchCondition methodCondition - = new ArrangementAtomMatchCondition(ArrangementSettingType.TYPE, ArrangementEntryType.METHOD); - condition.addOperand(abstractCondition); - condition.addOperand(methodCondition); - - HierarchicalArrangementConditionNode grouped = - ArrangementUtil.group(condition, Collections.singletonList(Collections.singleton(methodCondition))); - assertEquals(methodCondition, grouped.getCurrent()); - - HierarchicalArrangementConditionNode child = grouped.getChild(); - assertNotNull(child); - assertEquals(abstractCondition, child.getCurrent()); - - assertNull(child.getChild()); - } - - @Test - public void groupSingleNode() { - ArrangementMatchCondition condition = new ArrangementAtomMatchCondition(ArrangementSettingType.MODIFIER, ArrangementModifier.ABSTRACT); - HierarchicalArrangementConditionNode grouped = - ArrangementUtil.group(condition, Collections.singletonList(Collections.singleton(condition))); - assertEquals(condition, grouped.getCurrent()); - assertNull(grouped.getChild()); - } -} diff --git a/platform/lang-api/testSources/com/intellij/psi/codeStyle/arrangement/match/DefaultArrangementEntryMatcherSerializerTest.java b/platform/lang-api/testSources/com/intellij/psi/codeStyle/arrangement/match/DefaultArrangementEntryMatcherSerializerTest.java index 487a8179389b..9b7bcac2bbf0 100644 --- a/platform/lang-api/testSources/com/intellij/psi/codeStyle/arrangement/match/DefaultArrangementEntryMatcherSerializerTest.java +++ b/platform/lang-api/testSources/com/intellij/psi/codeStyle/arrangement/match/DefaultArrangementEntryMatcherSerializerTest.java @@ -27,7 +27,6 @@ import java.util.EnumSet; import java.util.List; import java.util.Set; -import static com.intellij.psi.codeStyle.arrangement.ArrangementOperator.AND; import static com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType.CLASS; import static com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType.FIELD; import static com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType.METHOD; @@ -52,12 +51,12 @@ public class DefaultArrangementEntryMatcherSerializerTest { @Test public void compositeMatchers() { - ArrangementCompositeMatchCondition condition = new ArrangementCompositeMatchCondition(AND); + ArrangementCompositeMatchCondition condition = new ArrangementCompositeMatchCondition(); condition.addOperand(new ArrangementAtomMatchCondition(TYPE, METHOD)); condition.addOperand(new ArrangementAtomMatchCondition(MODIFIER, SYNCHRONIZED)); doTest(condition); - condition = new ArrangementCompositeMatchCondition(AND); + condition = new ArrangementCompositeMatchCondition(); condition.addOperand(new ArrangementAtomMatchCondition(TYPE, FIELD)); condition.addOperand(new ArrangementAtomMatchCondition(MODIFIER, PUBLIC)); condition.addOperand(new ArrangementAtomMatchCondition(MODIFIER, STATIC)); @@ -67,7 +66,7 @@ public class DefaultArrangementEntryMatcherSerializerTest { @Test public void conditionsOrder() { // Inspired by IDEA-91826. - ArrangementCompositeMatchCondition condition = new ArrangementCompositeMatchCondition(AND); + ArrangementCompositeMatchCondition condition = new ArrangementCompositeMatchCondition(); ArrangementEntryType typeToPreserve = FIELD; Set modifiersToPreserve = EnumSet.of(PUBLIC, STATIC, FINAL); condition.addOperand(new ArrangementAtomMatchCondition(TYPE, typeToPreserve)); diff --git a/platform/lang-api/testSources/com/intellij/psi/codeStyle/arrangement/match/StandardArrangementEntryMatcherTest.java b/platform/lang-api/testSources/com/intellij/psi/codeStyle/arrangement/match/StandardArrangementEntryMatcherTest.java index 9293badd7e30..8da5909f9f4d 100644 --- a/platform/lang-api/testSources/com/intellij/psi/codeStyle/arrangement/match/StandardArrangementEntryMatcherTest.java +++ b/platform/lang-api/testSources/com/intellij/psi/codeStyle/arrangement/match/StandardArrangementEntryMatcherTest.java @@ -18,7 +18,6 @@ package com.intellij.psi.codeStyle.arrangement.match; import static org.junit.Assert.*; import static org.junit.Assert.assertEquals; -import com.intellij.psi.codeStyle.arrangement.ArrangementOperator; import com.intellij.psi.codeStyle.arrangement.ModifierAwareArrangementEntry; import com.intellij.psi.codeStyle.arrangement.TypeAwareArrangementEntry; import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition; @@ -80,7 +79,7 @@ public class StandardArrangementEntryMatcherTest { @Test public void compositeAndCondition() { - ArrangementCompositeMatchCondition condition = new ArrangementCompositeMatchCondition(ArrangementOperator.AND); + ArrangementCompositeMatchCondition condition = new ArrangementCompositeMatchCondition(); condition.addOperand(new ArrangementAtomMatchCondition(ArrangementSettingType.TYPE, ArrangementEntryType.FIELD)); condition.addOperand(new ArrangementAtomMatchCondition(ArrangementSettingType.MODIFIER, ArrangementModifier.PUBLIC)); diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementChangeConditionCallback.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementChangeConditionCallback.java new file mode 100644 index 000000000000..c924a2b4d71f --- /dev/null +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementChangeConditionCallback.java @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.application.options.codeStyle.arrangement; + +import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule; +import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Denis Zhdanov + * @since 10/31/12 1:06 PM + */ +public interface ArrangementChangeConditionCallback { + + @Nullable + StdArrangementMatchRule onChange(@NotNull StdArrangementMatchRule rule, @Nullable ArrangementMatchCondition newCondition); +} diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementConfigUtil.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementConfigUtil.java index b4b02a12158a..63c73b7cfbfe 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementConfigUtil.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementConfigUtil.java @@ -15,22 +15,15 @@ */ package com.intellij.application.options.codeStyle.arrangement; -import com.intellij.openapi.util.Pair; import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType; import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier; import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition; import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingType; -import com.intellij.psi.codeStyle.arrangement.model.HierarchicalArrangementConditionNode; import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware; -import com.intellij.util.containers.Stack; -import gnu.trove.TIntIntHashMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.TreeNode; -import javax.swing.tree.TreePath; import java.awt.*; import java.util.*; import java.util.List; @@ -119,468 +112,4 @@ public class ArrangementConfigUtil { } return null; } - - public static int getDepth(@NotNull HierarchicalArrangementConditionNode node) { - HierarchicalArrangementConditionNode child = node.getChild(); - return child == null ? 1 : 1 + getDepth(child); - } - - @NotNull - public static HierarchicalArrangementConditionNode getLast(@NotNull HierarchicalArrangementConditionNode node) { - HierarchicalArrangementConditionNode result = node; - for (HierarchicalArrangementConditionNode child = node.getChild(); child != null; child = child.getChild()) { - result = child; - } - return result; - } - - @SuppressWarnings("ConstantConditions") - @NotNull - public static ArrangementTreeNode getLastBefore(@NotNull ArrangementTreeNode start, @NotNull ArrangementTreeNode stop) - throws IllegalArgumentException - { - ArrangementTreeNode result = start; - for (ArrangementTreeNode n = start.getParent(); n != stop; n = n.getParent()) { - if (n == null) { - throw new IllegalArgumentException(String.format( - "Non-crossing paths detected - start: %s, stop: %s", new TreePath(start), new TreePath(stop) - )); - } - result = n; - } - return result; - } - - @NotNull - public static ArrangementTreeNode getLast(@NotNull final ArrangementTreeNode node) { - ArrangementTreeNode result = node; - int childCount = result.getChildCount(); - while (childCount > 0) { - result = result.getChildAt(childCount - 1); - } - return result; - } - - public static int distance(@NotNull TreeNode parent, @NotNull TreeNode child) { - if (parent == child) { - return 1; - } - int result = 1; - for (TreeNode n = child; n != null && n != parent; n = n.getParent()) { - result++; - } - return result; - } - - /** - * @param uiParentNode UI tree node which should hold UI nodes created for representing given settings node; - * null as an indication that we want to create a standalone nodes hierarchy - * @param conditionNode settings node which should be represented at the UI tree denoted by the given UI tree node - * @param model tree model to use for the tree modification - * @return pair {@code (bottom-most leaf node created; number of rows created)} - */ - @NotNull - public static Pair map(@Nullable ArrangementTreeNode uiParentNode, - @NotNull HierarchicalArrangementConditionNode conditionNode, - @Nullable DefaultTreeModel model) - { - ArrangementTreeNode uiNode = null; - int rowsCreated = 0; - if (uiParentNode != null && uiParentNode.getChildCount() > 0) { - ArrangementTreeNode child = uiParentNode.getChildAt(uiParentNode.getChildCount() - 1); - if (conditionNode.getCurrent().equals(child.getBackingCondition())) { - uiNode = child; - } - } - if (uiNode == null) { - uiNode = new ArrangementTreeNode(conditionNode.getCurrent()); - if (uiParentNode != null) { - if (model == null) { - uiParentNode.add(uiNode); - } - else { - model.insertNodeInto(uiNode, uiParentNode, uiParentNode.getChildCount()); - } - } - rowsCreated++; - } - ArrangementTreeNode leaf = uiNode; - HierarchicalArrangementConditionNode childConditionNode = conditionNode.getChild(); - if (childConditionNode != null) { - Pair pair = map(uiNode, childConditionNode, model); - leaf = pair.first; - rowsCreated += pair.second; - } - return Pair.create(leaf, rowsCreated); - } - - /** - * Utility method which helps to replace node sub-hierarchy identified by the given start and end nodes (inclusive) by - * a sub-hierarchy which is denoted by the given root. - * - * @param from indicates start of the node sub-hierarchy (top-most node) to be replaced (inclusive) - * @param to indicates end of the node sub-hierarchy (bottom-most node) to be replaced (inclusive) - * @param replacement root of the node sub-hierarchy which should replace the one identified by the given 'start' and 'end' nodes - * @param treeModel model which should hold ui nodes - * @param rootVisible determines if the root should be count during rows calculations - * @return collection of row changes at the form {@code 'old row -> new row'} (all rows are zero-based) - */ - public static TIntIntHashMap replace(@NotNull ArrangementTreeNode from, - @NotNull ArrangementTreeNode to, - @NotNull ArrangementTreeNode replacement, - @NotNull DefaultTreeModel treeModel, - boolean rootVisible) - { - return doReplace(from, to, replacement, treeModel, rootVisible); - } - - - - @SuppressWarnings("AssignmentToForLoopParameter") - @NotNull - private static TIntIntHashMap doReplace(@NotNull ArrangementTreeNode from, - @NotNull ArrangementTreeNode to, - @Nullable ArrangementTreeNode replacement, - @NotNull DefaultTreeModel treeModel, - boolean rootVisible) - { - // The algorithm looks as follows: - // 1. Cut sub-hierarchy which belongs to the given 'from' root and is located below the 'to -> from' path; - // 2. Remove 'to -> from' sub-hierarchy' by going bottom-up and stopping as soon as a current node has a child over than one - // from the sub-hierarchy to remove; - // 3. Add 'replacement' sub-hierarchy starting after the 'from' index at its parent; - // 4. Add sub-hierarchy cut at the 1) starting after the 'replacement' sub-hierarchy index; - // Example: - // Initial: - // 0 - // |_1 - // |_2 - // | |_3 - // | |_4 - // | |_5 - // | - // |_6 - // Let's say we want to replace the sub-hierarchy '1 -> 2 -> 4' by the sub-hierarchy '1 -> 4'. The algorithm in action: - // 1. Cut bottom sub-hierarchy: - // Current: Cut: - // 0 1 - // |_1 |_2 - // |_2 | |_5 - // |_3 | - // |_4 |_6 - // - // 2. Remove target sub-hierarchy: - // Current: - // 0 - // |_1 - // |_2 <-- stop at this node because it has a child node '3' which doesn't belong to the '1 -> 2 -> 4' - // |_3 - // 3. Add 'replacement' sub-hierarchy: - // Current: - // 0 - // |_1 <-- re-use this node for '1 -> 4' addition - // |_2 - // | |_3 - // | - // |_4 - // 4. Add 'bottom' sub-hierarchy: - // Current: - // 0 - // |_1 <-- re-use this node either for '1 -> 2 -> 5' or '1 -> 6' addition - // |_2 - // | |_3 - // | - // |_4 - // | - // |_2 - // | |_5 - // | - // |_6 - // - // Note: we need to have a notion of 'equal nodes' for node re-usage. It's provided by comparing node user objects. - - markRows(from, rootVisible); - final ArrangementTreeNode root = from.getParent(); - assert root != null; - - @Nullable ArrangementTreeNode cutHierarchy = cutSubHierarchy(root, treeModel, to); - - int childCountBefore = root.getChildCount(); - - for (ArrangementTreeNode current = to; current != root;) { - ArrangementTreeNode parent = current.getParent(); - treeModel.removeNodeFromParent(current); - current = parent; - if (parent == null || parent.getChildCount() > 0) { - break; - } - } - - int insertionIndex = root.getChildCount() < childCountBefore ? childCountBefore - 1 : childCountBefore; - if (replacement != null) { - insert(root, insertionIndex, replacement, treeModel); - } - if (cutHierarchy != null) { - insert(root, root.getChildCount(), cutHierarchy, treeModel); - } - - return collectRowChangesAndUnmark(root, rootVisible); - } - - /** - * Removes all nodes which lay below the path identified by the given 'top' and 'bottom' nodes from the tree structure and returns them. - * - * @param topMost top most node marker, i.e. all cut nodes descend from this node - * @param treeModel tree model which manages the nodes - * @param bottomMost bottom most node after which all other nodes should be cut - * @return removed nodes below the target path if any; null otherwise - */ - @Nullable - public static ArrangementTreeNode cutSubHierarchy(@NotNull ArrangementTreeNode topMost, - @NotNull DefaultTreeModel treeModel, - @NotNull ArrangementTreeNode bottomMost) - { - ArrangementTreeNode cutHierarchy = null; - for (ArrangementTreeNode current = bottomMost; current != null && current != topMost; current = current.getParent()) { - ArrangementTreeNode parent = current.getParent(); - assert parent != null; - int i = parent.getIndex(current); - int childCount = parent.getChildCount(); - if (i >= childCount - 1) { - continue; - } - ArrangementTreeNode parentCopy = parent.copy(); - if (parent.getChildCount() > 0) { - parentCopy.resetRow(); - } - if (cutHierarchy != null) { - parentCopy.add(cutHierarchy); - } - for (int j = i + 1; j < childCount; j++) { - ArrangementTreeNode child = parent.getChildAt(i + 1); - treeModel.removeNodeFromParent(child); - parentCopy.add(child); - } - cutHierarchy = parentCopy; - } - return cutHierarchy; - } - - /** - * Enriches every node at the hierarchy denoted by the given node by information about it's row. - * - * @param node reference to the target hierarchy - * @param rootVisible determines if the root should be count during rows calculations - */ - public static void markRows(@NotNull ArrangementTreeNode node, boolean rootVisible) { - ArrangementTreeNode root = getRoot(node); - int row = rootVisible ? 0 : -1; - Stack nodes = new Stack(); - nodes.push(root); - while (!nodes.isEmpty()) { - ArrangementTreeNode n = nodes.pop(); - n.markRow(row++); - for (int i = n.getChildCount() - 1; i >= 0; i--) { - nodes.push(n.getChildAt(i)); - } - } - } - - @NotNull - public static ArrangementTreeNode getRoot(@NotNull ArrangementTreeNode node) { - ArrangementTreeNode root = node; - for (ArrangementTreeNode n = root; n != null; n = n.getParent()) { - root = n; - } - return root; - } - - /** - * Processes hierarchy denoted by the given node assuming that every node there contains information about its initial row - * (see {@link #markRows(ArrangementTreeNode, boolean)}). - *

- * Collects all row changes and returns them. All row information is dropped from the nodes during the current method processing. - * - * @param node reference to the target nodes hierarchy - * @return collection of row changes at the form {@code 'old row -> new row'} (all rows are zero-based) - */ - @NotNull - public static TIntIntHashMap collectRowChangesAndUnmark(@NotNull ArrangementTreeNode node, boolean rootVisible) { - @NotNull TIntIntHashMap changes = new TIntIntHashMap(); - ArrangementTreeNode root = getRoot(node); - int row = rootVisible ? 0 : -1; - Stack nodes = new Stack(); - nodes.push(root); - while (!nodes.isEmpty()) { - ArrangementTreeNode n = nodes.pop(); - if (n.isRowSet() && n.getRow() != row) { - changes.put(n.getRow(), row); - } - n.resetRow(); - row++; - for (int i = n.getChildCount() - 1; i >= 0; i--) { - nodes.push(n.getChildAt(i)); - } - } - return changes; - } - - /** - * Allows to map given node to its row at the hierarchy. - * - * @param node target node - * @param rootVisible determines if the root should be count on rows calculation - * @return given node's row at the nodes hierarchy (0-based) - */ - public static int getRow(@NotNull ArrangementTreeNode node, boolean rootVisible) { - ArrangementTreeNode root = getRoot(node); - int row = rootVisible ? 0 : -1; - Stack nodes = new Stack(); - nodes.push(root); - while (!nodes.isEmpty()) { - ArrangementTreeNode n = nodes.pop(); - if (n == node) { - return row; - } - row++; - for (int i = n.getChildCount() - 1; i >= 0; i--) { - nodes.push(n.getChildAt(i)); - } - } - - StringBuilder buffer = new StringBuilder(); - String separator = "->"; - for (TreeNode n = node; n != null; n = n.getParent()) { - buffer.append(n).append(separator); - } - buffer.setLength(buffer.length() - separator.length()); - throw new RuntimeException("Invalid ArrangementTreeNode detected: " + buffer.toString()); - } - - /** - * Inserts given child to the given parent re-using existing nodes under the parent sub-hierarchy if possible. - *

- * Example: - *

-   *   parent:  0         to-insert: 2     
-   *            |_1                  |_3   
-   *            |_2                    |_6 
-   *            | |_3                      
-   *            |   |_4                    
-   *            |_5                        
-   *   -------------------------------------------------------------------------------------------------
-   *  | index:  |       0             |       1             |       2             |       3             |
-   *  |-------------------------------------------------------------------------------------------------
-   *  | result: |       0             |       0             |       0             |       0             |
-   *  |         |       |_2           |       |_1           |       |_1           |       |_1           |
-   *  |         |       | |_3         |       |_2           |       |_2           |       |_2           |
-   *  |         |       |   |_6       |       | |_3         |       | |_3         |       | |_3         |
-   *  |         |       |_1           |       |   |_6       |       |   |_4       |       |   |_4       |
-   *  |         |       |_2           |       |   |_4       |       |   |_6       |       |_5           |
-   *  |         |       | |_3         |       |_5           |       |_5           |       |_2           |
-   *  |         |       |   |_4       |                     |                     |         |_3         |
-   *  |         |       |_5           |                     |                     |           |_6       |
-   * 
- *

- * - * @param parent parent node to insert into - * @param index insertion index to use for the given parent node - * @param child node to insert to the given parent node at the given insertion index - * @param treeModel model which should hold UI nodes - * @return true if given child node has been merged to the existing node; false otherwise - */ - public static void insert(@NotNull final ArrangementTreeNode parent, - final int index, - @NotNull final ArrangementTreeNode child, - @NotNull DefaultTreeModel treeModel) - { - ArrangementTreeNode root = getRoot(parent); - List toInsert = new ArrayList(); - if (hasEqualSetting(root, child)) { - for (int i = 0; i < child.getChildCount(); i++) { - toInsert.add(child.getChildAt(i)); - } - } - else { - toInsert.add(child); - } - int i = index; - for (ArrangementTreeNode node : toInsert) { - doInsert(parent, i++, node, treeModel); - } - } - - - private static boolean doInsert(@NotNull final ArrangementTreeNode parent, - final int index, - @NotNull final ArrangementTreeNode child, - @NotNull DefaultTreeModel treeModel) - { - if (parent.getChildCount() < index) { - treeModel.insertNodeInto(child, parent, parent.getChildCount()); - return false; - } - - if (child.getChildCount() <= 0) { - // Don't merge the last child. - treeModel.insertNodeInto(child, parent, index); - return false; - } - - boolean anchorAbove = false; - ArrangementTreeNode mergeCandidate = null; - if (index > 0) { - mergeCandidate = parent.getChildAt(index - 1); - if (mergeCandidate.getChildCount() <= 0 /* don't merge into leaf node*/ || !hasEqualSetting(mergeCandidate, child)) { - mergeCandidate = null; - } - } - - if (index < parent.getChildCount()) { - ArrangementTreeNode n = parent.getChildAt(index); - if (hasEqualSetting(n, child)) { - mergeCandidate = n; - anchorAbove = true; - } - } - - if (mergeCandidate == null) { - treeModel.insertNodeInto(child, parent, index); - return false; - } - - for (int i = 0, limit = child.getChildCount(); i < limit; i++) { - insert(mergeCandidate, anchorAbove ? 0 : mergeCandidate.getChildCount(), child.getChildAt(0), treeModel); - } - return true; - } - - private static boolean hasEqualSetting(@NotNull ArrangementTreeNode node1, @NotNull ArrangementTreeNode node2) { - ArrangementMatchCondition matchCondition1 = node1.getBackingCondition(); - ArrangementMatchCondition matchCondition2 = node2.getBackingCondition(); - if (matchCondition1 == null) { - return matchCondition2 == null; - } - else { - return matchCondition1.equals(matchCondition2); - } - } - - /** - * Removes target sub-hierarchy from the tree. - * - * @param from indicates start of the node sub-hierarchy (top-most node) to be replaced (inclusive) - * @param to indicates end of the node sub-hierarchy (bottom-most node) to be replaced (inclusive) - * @param treeModel model which should hold ui nodes - * @param rootVisible determines if the root should be count during rows calculations - * @return collection of row changes at the form {@code 'old row -> new row'} (all rows are zero-based) - */ - @NotNull - public static TIntIntHashMap remove(@NotNull final ArrangementTreeNode from, - @NotNull final ArrangementTreeNode to, - @NotNull DefaultTreeModel treeModel, - boolean rootVisible) - { - return doReplace(from, to, null, treeModel, rootVisible); - } } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementConstants.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementConstants.java index 2a121b55d33f..b2c8243646d7 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementConstants.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementConstants.java @@ -17,7 +17,6 @@ package com.intellij.application.options.codeStyle.arrangement; import com.intellij.openapi.actionSystem.DataKey; import com.intellij.util.Consumer; -import com.intellij.util.Function; import com.intellij.util.NotNullFunction; import org.jetbrains.annotations.NonNls; @@ -33,8 +32,9 @@ public class ArrangementConstants { @NonNls public static final String RULE_EDITOR_PLACE = "Arrangement.RuleEditor.Place"; @NonNls public static final String RULE_EDITOR_TOOL_WINDOW_PLACE = "Arrangement.RuleEditor.ToolWindow.Place"; @NonNls public static final String RULE_TREE_PLACE = "Arrangement.RuleTree.Place"; - + public static final int HORIZONTAL_PADDING = 8; + public static final int HORIZONTAL_GAP = 8; public static final boolean LOG_RULE_MODIFICATION = Boolean.parseBoolean(System.getProperty("log.arrangement.rule.modification")); diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementEditorTreeNode.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementEditorTreeNode.java index 1e2d499a6782..081740ee078a 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementEditorTreeNode.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementEditorTreeNode.java @@ -28,7 +28,7 @@ import java.awt.*; * @author Denis Zhdanov * @since 10/30/12 12:10 PM */ -public class ArrangementEditorTreeNode extends ArrangementTreeNode implements ArrangementRepresentationAwareNode { +public class ArrangementEditorTreeNode /*extends ArrangementTreeNode implements ArrangementRepresentationAwareNode*/ { private static final int STEPS_NUMBER = 10; @@ -41,7 +41,7 @@ public class ArrangementEditorTreeNode extends ArrangementTreeNode implements Ar private int myStep = 1; public ArrangementEditorTreeNode(@NotNull ArrangementRuleEditor editor, @NotNull DefaultTreeModel treeModel, int availableWidth) { - super(null); + //super(null); myTreeModel = treeModel; myAvailableWidth = availableWidth; editor.applyAvailableWidth(availableWidth - ArrangementConstants.HORIZONTAL_PADDING); @@ -99,11 +99,11 @@ public class ArrangementEditorTreeNode extends ArrangementTreeNode implements Ar public void changeState() { myStep++; - myTreeModel.nodeChanged(this); + //myTreeModel.nodeChanged(this); } @NotNull - @Override + //@Override public JComponent getRenderer() { return myRenderer; } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementGroupingRulesManager.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementGroupingRulesManager.java deleted file mode 100644 index e12c11c15c99..000000000000 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementGroupingRulesManager.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.application.options.codeStyle.arrangement; - -import com.intellij.application.options.codeStyle.arrangement.node.ArrangementCheckBoxNode; -import com.intellij.application.options.codeStyle.arrangement.node.ArrangementComboBoxNode; -import com.intellij.application.options.codeStyle.arrangement.node.ArrangementSectionNode; -import com.intellij.openapi.application.ApplicationBundle; -import com.intellij.psi.codeStyle.arrangement.group.ArrangementGroupingRule; -import com.intellij.psi.codeStyle.arrangement.group.ArrangementGroupingType; -import com.intellij.psi.codeStyle.arrangement.order.ArrangementEntryOrderType; -import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware; -import com.intellij.util.Consumer; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.MutableTreeNode; -import javax.swing.tree.TreeNode; -import java.util.ArrayList; -import java.util.List; - -/** - * This class is responsible for representing {@link ArrangementGroupingRule grouping rules} at tree structure. - *

- * Thread-safe. - * - * @author Denis Zhdanov - * @since 9/27/12 2:36 PM - */ -public class ArrangementGroupingRulesManager { - - @NotNull public static final ArrangementGroupingRulesManager INSTANCE = new ArrangementGroupingRulesManager(); - - @SuppressWarnings("MethodMayBeStatic") - @Nullable - public MutableTreeNode buildAvailableRules(@NotNull ArrangementStandardSettingsAware settingsFilter, - @NotNull ArrangementNodeDisplayManager displayManager) - { - ArrangementSectionNode result = new ArrangementSectionNode(ApplicationBundle.message("arrangement.settings.section.groups")); - String orderLabel = ApplicationBundle.message("arrangement.settings.label.order") + ":"; - for (ArrangementGroupingType groupingType : ArrangementGroupingType.values()) { - if (!settingsFilter.isEnabled(groupingType, null)) { - continue; - } - final ArrangementCheckBoxNode groupingNode - = new ArrangementCheckBoxNode(displayManager, groupingType); - result.add(groupingNode); - List supportedOrderTypes = new ArrayList(); - for (ArrangementEntryOrderType orderType : ArrangementEntryOrderType.values()) { - if (settingsFilter.isEnabled(groupingType, orderType)) { - supportedOrderTypes.add(orderType); - } - } - if (supportedOrderTypes.isEmpty()) { - continue; - } - - final ArrangementComboBoxNode orderTypeNode - = new ArrangementComboBoxNode(displayManager, orderLabel, supportedOrderTypes); - groupingNode.add(orderTypeNode); - groupingNode.setChangeCallback(new Consumer>() { - @Override - public void consume(ArrangementCheckBoxNode node) { - orderTypeNode.setEnabled(groupingNode.isSelected()); - } - }); - groupingNode.setSelected(true); - } - return result; - } - - @SuppressWarnings({"MethodMayBeStatic", "unchecked"}) - public void applyRules(@NotNull List rules, - @NotNull MutableTreeNode groupsRoot, - @NotNull DefaultTreeModel model) - { - for (int i = 0; i < groupsRoot.getChildCount(); i++) { - ArrangementCheckBoxNode child = (ArrangementCheckBoxNode)groupsRoot.getChildAt(i); - child.setSelected(false); - } - for (int i = 0; i < rules.size(); i++) { - ArrangementGroupingRule rule = rules.get(i); - int index = getIndex(groupsRoot, rule.getRule()); - if (index < 0) { - return; - } - - ArrangementCheckBoxNode child = (ArrangementCheckBoxNode)groupsRoot.getChildAt(index); - child.setSelected(true); - - if (i != index) { - model.removeNodeFromParent(child); - int insertionIndex = i < index ? i : i - 1; - model.insertNodeInto(child, groupsRoot, insertionIndex); - } - - if (child.getChildCount() > 0) { - ArrangementComboBoxNode orderTypeNode = - (ArrangementComboBoxNode)child.getChildAt(0); - orderTypeNode.setSelectedValue(rule.getOrderType()); - } - } - } - - private static int getIndex(@NotNull TreeNode root, @NotNull Object data) { - for (int i = 0; i < root.getChildCount(); i++) { - TreeNode child = root.getChildAt(i); - if (child instanceof ArrangementCheckBoxNode && data.equals(((ArrangementCheckBoxNode)child).getValue())) { - return i; - } - } - return -1; - } - - @SuppressWarnings({"unchecked", "MethodMayBeStatic"}) - @NotNull - public List buildRules(@NotNull TreeNode groupsRoot) { - List result = new ArrayList(); - for (int i = 0; i < groupsRoot.getChildCount(); i++) { - ArrangementCheckBoxNode groupNode = - (ArrangementCheckBoxNode)groupsRoot.getChildAt(i); - if (!groupNode.isSelected()) { - continue; - } - ArrangementGroupingType groupingType = groupNode.getValue(); - if (groupNode.getChildCount() < 1) { - result.add(new ArrangementGroupingRule(groupingType)); - continue; - } - - ArrangementComboBoxNode orderNode = - (ArrangementComboBoxNode)groupNode.getChildAt(0); - ArrangementEntryOrderType orderType = orderNode.getSelectedValue(); - result.add(new ArrangementGroupingRule(groupingType, orderType)); - } - return result; - } -} diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModel.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModel.java deleted file mode 100644 index b34e809cafc3..000000000000 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModel.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.application.options.codeStyle.arrangement; - -import com.intellij.psi.codeStyle.arrangement.ArrangementOperator; -import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule; -import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType; -import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier; -import com.intellij.psi.codeStyle.arrangement.match.StdArrangementEntryMatcher; -import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition; -import com.intellij.psi.codeStyle.arrangement.model.ArrangementCompositeMatchCondition; -import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition; -import org.jetbrains.annotations.NotNull; - -/** - * Combines and encapsulates information about arrangement matcher rules representation (tree nodes) and - * underlying {@link ArrangementMatchCondition data model}. - *

- * Implementations of this interface are not obliged to be thread-safe. - * - * @author Denis Zhdanov - * @since 8/15/12 1:04 PM - */ -public interface ArrangementRuleEditingModel { - - @NotNull - StdArrangementMatchRule EMPTY_RULE = new StdArrangementMatchRule(new StdArrangementEntryMatcher( - new ArrangementCompositeMatchCondition(ArrangementOperator.AND) - )); - - @NotNull - ArrangementMatchCondition getCondition(); - - @NotNull - StdArrangementMatchRule getRule(); - - /** - * Asks current model to destroy itself. - *

- * The key concern here is to perform necessary tree modification. - */ - void destroy(); - - /** - * Allows to answer if current model has a registered condition for the given key. A key is expected to be one of the standard - * keys, e.g. {@link ArrangementEntryType type}, {@link ArrangementModifier modifier} etc. - * - * @param key target key - * @return true if current model has a registered mapping for the given key; - * false otherwise - */ - boolean hasCondition(@NotNull Object key); - - void addAndCondition(@NotNull ArrangementAtomMatchCondition condition); - - void removeAndCondition(@NotNull ArrangementMatchCondition condition); - - /** - * We need to be able to replace one condition by another. Most of the time it can be simulated by - * {@link #removeAndCondition(ArrangementMatchCondition) 'remove old'} and - * {@link #addAndCondition(ArrangementAtomMatchCondition) 'add new'} actions sequence but that doesn't work when - * {@link #getRule() underlying condition} has the only atom condition. Removing it eliminates the condition at all. - * - * @param from condition which should be replaced - * @param to replacement condition - * @throws IllegalArgumentException when given 'from' condition is not a part of the - * {@link #getRule() underlying match condition} - */ - void replaceCondition(@NotNull ArrangementAtomMatchCondition from, @NotNull ArrangementAtomMatchCondition to) - throws IllegalArgumentException; -} diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelBuilder.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelBuilder.java deleted file mode 100644 index ffaede37e13c..000000000000 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelBuilder.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.application.options.codeStyle.arrangement; - -import com.intellij.openapi.util.Pair; -import com.intellij.psi.codeStyle.arrangement.ArrangementUtil; -import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule; -import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier; -import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition; -import com.intellij.psi.codeStyle.arrangement.model.HierarchicalArrangementConditionNode; -import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsRepresentationAware; -import gnu.trove.TIntIntHashMap; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeModel; -import java.util.List; -import java.util.Set; - -/** - * Holds glue logic between arrangement settings and their representation - - * '{@link ArrangementMatchCondition} -> {@link ArrangementRuleEditingModel}' - *

- * Thread-safe. - * - * @author Denis Zhdanov - * @since 8/15/12 2:50 PM - */ -public class ArrangementRuleEditingModelBuilder { - - /** - * Does the following: - *

-   * 
    - *
  1. - * {@link HierarchicalArrangementConditionNode Groups} given {@link ArrangementMatchCondition settings} using - * the given grouping rules; - *
  2. - *
  3. - * Build {@link DefaultMutableTreeNode tree nodes} for the {@link HierarchicalArrangementConditionNode groiping-aware nodes} - * and register them within the target tree structure (denoted by the given settings root element); - *
  4. - *
  5. - * Build necessary {@link ArrangementRuleEditingModel editing models} and store them at the given container (a key is a node row); - *
  6. - *
- *
- * - * @param rule target rule to process - * @param tree UI tree which shows arrangement matcher rules - * @param root UI tree settings root to use (may be not the same as the tree root) - * @param anchor node after which should be previous sibling for the root node of the inserted condition; - * null as an indication that new condition nodes should be inserted as the last 'root' child - * @param groupingRules conditions grouping rules - * {@link ArrangementStandardSettingsRepresentationAware#getDisplayValue(ArrangementModifier) group} setting - * nodes for UI representation - * @return created model and collection of row changes at the form {@code 'old row -> new row'} (all rows are zero-based); - * null if no model has been created - */ - @SuppressWarnings("MethodMayBeStatic") - @Nullable - public Pair build( - @NotNull StdArrangementMatchRule rule, - @NotNull JTree tree, - @NotNull ArrangementTreeNode root, - @Nullable ArrangementTreeNode anchor, - @NotNull List> groupingRules) - { - HierarchicalArrangementConditionNode grouped = ArrangementUtil.group(rule.getMatcher().getCondition(), groupingRules); - DefaultTreeModel treeModel = (DefaultTreeModel)tree.getModel(); - - if (grouped.getChild() == null) { - ArrangementTreeNode lastChild = null; - if (root.getChildCount() > 0) { - lastChild = root.getLastChild(); - } - if (lastChild != null && grouped.getCurrent().equals(lastChild.getBackingCondition())) { - return null; - } - } - - Pair pair = ArrangementConfigUtil.map(null, grouped, null); - ArrangementTreeNode topMostNode = ArrangementConfigUtil.getRoot(pair.first); - ArrangementConfigUtil.markRows(root, true); - ArrangementTreeNode bottomHierarchy = null; - if (anchor != null) { - bottomHierarchy = ArrangementConfigUtil.cutSubHierarchy(root, treeModel, anchor); - } - ArrangementConfigUtil.insert(root, root.getChildCount(), topMostNode, treeModel); - if (bottomHierarchy != null) { - ArrangementConfigUtil.insert(root, root.getChildCount(), bottomHierarchy, treeModel); - } - - TIntIntHashMap rowChanges = ArrangementConfigUtil.collectRowChangesAndUnmark(root, true); - topMostNode = ArrangementConfigUtil.getLastBefore(pair.first, root); - int row = ArrangementConfigUtil.getRow(pair.first, tree.isRootVisible()); - ArrangementRuleEditingModelImpl model = new ArrangementRuleEditingModelImpl( - treeModel, - rule, - topMostNode, - pair.first, - groupingRules, - row, - tree.isRootVisible() - ); - return Pair.create(model, rowChanges); - } -} diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelImpl.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelImpl.java deleted file mode 100644 index 1ca9a756a5d0..000000000000 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditingModelImpl.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.application.options.codeStyle.arrangement; - -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.Pair; -import com.intellij.psi.codeStyle.arrangement.ArrangementUtil; -import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule; -import com.intellij.psi.codeStyle.arrangement.match.StdArrangementEntryMatcher; -import com.intellij.psi.codeStyle.arrangement.model.*; -import com.intellij.util.containers.hash.HashSet; -import gnu.trove.TIntIntHashMap; -import org.jetbrains.annotations.NotNull; - -import javax.swing.tree.DefaultTreeModel; -import java.util.List; -import java.util.Set; - -/** - * Not thread-safe. - * - * @author Denis Zhdanov - * @since 8/15/12 2:40 PM - */ -public class ArrangementRuleEditingModelImpl implements ArrangementRuleEditingModel { - - @NotNull private static final MyConditionsBuilder CONDITIONS_BUILDER = new MyConditionsBuilder(); - - @NotNull private static final Logger LOG = Logger.getInstance("#" + ArrangementRuleEditingModelImpl.class.getName()); - - @NotNull private final Set myListeners = new HashSet(); - @NotNull private final Set myConditions = new HashSet(); - - @NotNull private final DefaultTreeModel myTreeModel; - @NotNull private final List> myGroupingRules; - private final boolean myRootVisible; - - @NotNull private ArrangementTreeNode myTopMost; - @NotNull private ArrangementTreeNode myBottomMost; - @NotNull private StdArrangementMatchRule myRule; - private int myRow; - - /** - * Creates new ArrangementRuleEditingModelImpl object. - * - * @param model tree model which holds target ui nodes. Basically, we need to perform ui nodes modification via it in order - * to generate corresponding events automatically - * @param rule backing arrangement rule - * @param topMost there is a possible case that a single settings node is shown in more than one visual line - * ({@link HierarchicalArrangementConditionNode}). This argument is the top-most UI node used for the - * settings node representation - * @param bottomMost bottom-most UI node used for the given settings node representation - * @param groupingRules grouping rules - * @param row row number for which current model is registered at the given model mappings - * @param rootVisible determines if the root should be count during rows calculations - */ - public ArrangementRuleEditingModelImpl(@NotNull DefaultTreeModel model, - @NotNull StdArrangementMatchRule rule, - @NotNull ArrangementTreeNode topMost, - @NotNull ArrangementTreeNode bottomMost, - @NotNull List> groupingRules, - int row, - boolean rootVisible) - { - myTreeModel = model; - myRule = rule; - myTopMost = topMost; - myBottomMost = bottomMost; - myGroupingRules = groupingRules; - myRow = row; - myRootVisible = rootVisible; - refreshConditions(); - } - - private void refreshConditions() { - myConditions.clear(); - CONDITIONS_BUILDER.conditions = myConditions; - try { - getCondition().invite(CONDITIONS_BUILDER); - } - finally { - CONDITIONS_BUILDER.conditions = null; - } - } - - @NotNull - @Override - public ArrangementMatchCondition getCondition() { - return myRule.getMatcher().getCondition(); - } - - @NotNull - @Override - public StdArrangementMatchRule getRule() { - return myRule; - } - - public int getRow() { - return myRow; - } - - public void setRow(int row) { - myRow = row; - } - - @NotNull - public ArrangementTreeNode getTopMost() { - return myTopMost; - } - - @NotNull - public ArrangementTreeNode getBottomMost() { - return myBottomMost; - } - - @Override - public boolean hasCondition(@NotNull Object key) { - return myConditions.contains(key); - } - - /** - * There is a possible case that tree nodes referenced by the current model become out of date due to a tree modification. - *

- * This method asks the model to refresh its tree nodes if necessary. - */ - public void refreshTreeNodes() { - for (ArrangementTreeNode node = myBottomMost; node != null; node = node.getParent()) { - if (node == myTopMost) { - // No refresh is necessary. - return; - } - ArrangementMatchCondition matchCondition = myTopMost.getBackingCondition(); - if (matchCondition != null && matchCondition.equals(node.getBackingCondition())) { - myTopMost = node; - return; - } - } - assert false; - } - - @Override - public void addAndCondition(@NotNull ArrangementAtomMatchCondition condition) { - if (ArrangementConstants.LOG_RULE_MODIFICATION) { - LOG.info(String.format( - "Arrangement rule modification - adding a condition '%s'. Model: '%s', row: %d", condition, myRule, myRow - )); - } - ArrangementMatchCondition newCondition = ArrangementUtil.and(getCondition().clone(), condition); - applyNewCondition(newCondition); - } - - @Override - public void removeAndCondition(@NotNull ArrangementMatchCondition condition) { - if (ArrangementConstants.LOG_RULE_MODIFICATION) { - LOG.info(String.format( - "Arrangement rule modification - removing a condition '%s'. Model: '%s', row: %d", condition, myRule, myRow - )); - } - - if (condition.equals(myBottomMost.getBackingCondition())) { - if (myTopMost != myBottomMost && myTopMost.getChildCount() > 1) { - destroy(); - return; - } - } - - ArrangementMatchCondition matchCondition = getCondition(); - assert matchCondition instanceof ArrangementCompositeMatchCondition; - ArrangementMatchCondition newCondition = matchCondition.clone(); - ArrangementCompositeMatchCondition composite = (ArrangementCompositeMatchCondition)newCondition; - composite.getOperands().remove(condition); - if (composite.getOperands().size() == 1) { - newCondition = composite.getOperands().iterator().next(); - } - applyNewCondition(newCondition); - } - - private void applyNewCondition(@NotNull ArrangementMatchCondition newCondition) { - myRule = new StdArrangementMatchRule(new StdArrangementEntryMatcher(newCondition)); - HierarchicalArrangementConditionNode grouped = ArrangementUtil.group(newCondition, myGroupingRules); - Pair replacement = ArrangementConfigUtil.map(null, grouped, null); - ArrangementTreeNode newBottom = replacement.first; - ArrangementTreeNode newTop = ArrangementConfigUtil.getRoot(newBottom); - final TIntIntHashMap rowChanges = ArrangementConfigUtil.replace(myTopMost, myBottomMost, newTop, myTreeModel, myRootVisible); - myBottomMost = newBottom; - myTopMost = newTop; - refreshTreeNodes(); - int newRow = ArrangementConfigUtil.getRow(myBottomMost, myRootVisible); - if (myRow != newRow) { - rowChanges.put(myRow, newRow); - myRow = newRow; - } - refreshConditions(); - for (Listener listener : myListeners) { - listener.onChanged(this, rowChanges); - } - - if (ArrangementConstants.LOG_RULE_MODIFICATION) { - LOG.info(String.format("Arrangement rule is modified: '%s', row: %d", myRule, myRow)); - } - } - - @Override - public void replaceCondition(@NotNull ArrangementAtomMatchCondition from, @NotNull ArrangementAtomMatchCondition to) { - if (ArrangementConstants.LOG_RULE_MODIFICATION) { - LOG.info(String.format( - "Arrangement rule modification - replacing condition '%s' by '%s'. Model: '%s', row: %d", from, to, myRule, myRow - )); - } - ArrangementMatchCondition newCondition; - ArrangementMatchCondition matchCondition = getCondition(); - if (matchCondition.equals(from)) { - newCondition = to; - } - else { - assert matchCondition instanceof ArrangementCompositeMatchCondition; - ArrangementCompositeMatchCondition composite = (ArrangementCompositeMatchCondition)matchCondition; - ArrangementCompositeMatchCondition newComposite = composite.clone(); - newComposite.getOperands().remove(from); - newComposite.getOperands().add(to); - newCondition = newComposite; - } - applyNewCondition(newCondition); - } - - @Override - public void destroy() { - if (ArrangementConstants.LOG_RULE_MODIFICATION) { - LOG.info(String.format("Arrangement rule modification - destroy. Model: '%s', row: %d", myRule, myRow)); - } - for (Listener listener : myListeners) { - listener.beforeModelDestroy(this); - } - TIntIntHashMap rowChanges = ArrangementConfigUtil.remove(myTopMost, myBottomMost, myTreeModel, myRootVisible); - for (Listener listener : myListeners) { - listener.afterModelDestroy(rowChanges); - } - } - - public void addListener(@NotNull Listener listener) { - myListeners.add(listener); - } - - @Override - public String toString() { - return "model for " + myRule; - } - - private static class MyConditionsBuilder implements ArrangementMatchConditionVisitor { - - @NotNull Set conditions; - - @Override - public void visit(@NotNull ArrangementAtomMatchCondition setting) { - conditions.add(setting.getValue()); - } - - @Override - public void visit(@NotNull ArrangementCompositeMatchCondition setting) { - for (ArrangementMatchCondition operand : setting.getOperands()) { - operand.invite(this); - } - } - } - - public interface Listener { - void onChanged(@NotNull ArrangementRuleEditingModelImpl model, @NotNull TIntIntHashMap rowChanges); - void beforeModelDestroy(@NotNull ArrangementRuleEditingModelImpl model); - void afterModelDestroy(@NotNull TIntIntHashMap rowChanges); - } -} diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditor.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditor.java index 9ed51b27169c..bd8431a9d415 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditor.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleEditor.java @@ -15,11 +15,15 @@ */ package com.intellij.application.options.codeStyle.arrangement; -import com.intellij.application.options.codeStyle.arrangement.node.match.ArrangementAtomMatchNodeComponent; -import com.intellij.application.options.codeStyle.arrangement.node.match.ArrangementMatchNodeComponent; -import com.intellij.psi.codeStyle.arrangement.match.ArrangementMatchRule; +import com.intellij.application.options.codeStyle.arrangement.node.match.ArrangementAtomMatchConditionComponent; +import com.intellij.application.options.codeStyle.arrangement.node.match.ArrangementMatchConditionComponent; +import com.intellij.psi.codeStyle.arrangement.ArrangementConditionInfo; +import com.intellij.psi.codeStyle.arrangement.ArrangementUtil; import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryMatcher; +import com.intellij.psi.codeStyle.arrangement.match.ArrangementMatchRule; +import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule; import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition; +import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition; import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingType; import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware; import com.intellij.ui.IdeBorderFactory; @@ -47,19 +51,24 @@ public class ArrangementRuleEditor extends JPanel { @NotNull private final List myRows = new ArrayList(); - @NotNull private final Map myComponents = - new HashMap(); + @NotNull private final Map myComponents = + new HashMap(); - @NotNull private final ArrangementStandardSettingsAware myFilter; - @NotNull private final ArrangementColorsProvider myColorsProvider; - @Nullable private ArrangementRuleEditingModel myModel; + @NotNull private final ArrangementStandardSettingsAware myFilter; + @NotNull private final ArrangementColorsProvider myColorsProvider; + @NotNull private final ArrangementChangeConditionCallback myChangeCallback; + + @Nullable private StdArrangementMatchRule myRule; + @Nullable private ArrangementConditionInfo myConditionInfo; public ArrangementRuleEditor(@NotNull ArrangementStandardSettingsAware filter, @NotNull ArrangementColorsProvider provider, - @NotNull ArrangementNodeDisplayManager displayManager) + @NotNull ArrangementNodeDisplayManager displayManager, + @NotNull ArrangementChangeConditionCallback callback) { myFilter = filter; myColorsProvider = provider; + myChangeCallback = callback; init(displayManager); addMouseListener(new MouseAdapter() { @Override @@ -89,13 +98,13 @@ public class ArrangementRuleEditor extends JPanel { MultiRowFlowPanel valuesPanel = new MultiRowFlowPanel(FlowLayout.LEFT, 8, 5); for (Object value : manager.sort(values)) { - ArrangementAtomMatchNodeComponent component = - new ArrangementAtomMatchNodeComponent(manager, myColorsProvider, new ArrangementAtomMatchCondition(key, value), null); + ArrangementAtomMatchConditionComponent component = + new ArrangementAtomMatchConditionComponent(manager, myColorsProvider, new ArrangementAtomMatchCondition(key, value), null); myComponents.put(value, component); valuesPanel.add(component.getUiComponent()); } - int top = ArrangementAtomMatchNodeComponent.VERTICAL_PADDING; + int top = ArrangementAtomMatchConditionComponent.VERTICAL_PADDING; add(new JLabel(manager.getDisplayLabel(key) + ":"), new GridBag().anchor(GridBagConstraints.NORTHWEST).insets(top, 0, 0, 0)); add(valuesPanel, new GridBag().anchor(GridBagConstraints.WEST).weightx(1).fillCellHorizontally().coverLine()); myRows.add(valuesPanel); @@ -105,36 +114,46 @@ public class ArrangementRuleEditor extends JPanel { * Asks current editor to refresh its state in accordance with the given arguments (e.g. when new rule is selected and * we want to show only available conditions). * - * @param model current rule settings model if defined; null as an indication that no settings should be active + * @param rule rule which match condition should be edited (if defined); + * null as an indication that no settings should be active */ - public void updateState(@Nullable ArrangementRuleEditingModel model) { - myModel = model; + public void updateState(@Nullable StdArrangementMatchRule rule) { + myRule = rule; + myConditionInfo = null; // Reset state. - for (ArrangementAtomMatchNodeComponent component : myComponents.values()) { + for (ArrangementAtomMatchConditionComponent component : myComponents.values()) { component.setEnabled(false); component.setSelected(false); } - if (model == null) { + if (rule == null) { return; } - Map> available = ArrangementConfigUtil.buildAvailableConditions( - myFilter, model.getRule().getMatcher().getCondition() - ); + ArrangementMatchCondition condition = rule.getMatcher().getCondition(); + myConditionInfo = ArrangementUtil.extractConditions(condition); + + Map> available = ArrangementConfigUtil.buildAvailableConditions(myFilter, condition); for (Collection ids : available.values()) { for (Object id : ids) { - ArrangementAtomMatchNodeComponent component = myComponents.get(id); + ArrangementAtomMatchConditionComponent component = myComponents.get(id); if (component != null) { component.setEnabled(true); - component.setSelected(model.hasCondition(id)); + component.setSelected(myConditionInfo.hasCondition(id)); } } } repaint(); } - + + private void updateState() { + assert myConditionInfo != null; + ArrangementMatchCondition newCondition = myConditionInfo.buildCondition(); + myChangeCallback.onChange(myRule, newCondition); + updateState(myRule); + } + public void applyAvailableWidth(int width) { for (MultiRowFlowPanel row : myRows) { row.setForcedWidth(width); @@ -150,19 +169,20 @@ public class ArrangementRuleEditor extends JPanel { } private void onMouseClicked(@NotNull MouseEvent e) { - if (myModel == null) { + if (myRule == null || myConditionInfo == null) { return; } - ArrangementAtomMatchNodeComponent clickedComponent = getNodeComponentAt(e.getLocationOnScreen()); + ArrangementAtomMatchConditionComponent clickedComponent = getNodeComponentAt(e.getLocationOnScreen()); if (clickedComponent == null || !clickedComponent.isEnabled()) { return; } ArrangementAtomMatchCondition chosenCondition = clickedComponent.getMatchCondition(); - boolean remove = myModel.hasCondition(chosenCondition.getValue()); + boolean remove = myConditionInfo.hasCondition(chosenCondition.getValue()); clickedComponent.setSelected(!remove); repaintComponent(clickedComponent); if (remove) { - myModel.removeAndCondition(chosenCondition); + myConditionInfo.removeCondition(chosenCondition); + updateState(); return; } @@ -172,30 +192,33 @@ public class ArrangementRuleEditor extends JPanel { continue; } for (Object key : mutex) { - if (myModel.hasCondition(key)) { - ArrangementAtomMatchNodeComponent componentToDeselect = myComponents.get(key); - myModel.replaceCondition(componentToDeselect.getMatchCondition(), chosenCondition); - for (ArrangementAtomMatchNodeComponent componentToCheck : myComponents.values()) { + if (myConditionInfo.hasCondition(key)) { + ArrangementAtomMatchConditionComponent componentToDeselect = myComponents.get(key); + myConditionInfo.removeCondition(componentToDeselect.getMatchCondition().getValue()); + myConditionInfo.addAtomCondition(chosenCondition); + ArrangementMatchCondition newCondition = myConditionInfo.buildCondition(); + for (ArrangementAtomMatchConditionComponent componentToCheck : myComponents.values()) { Object value = componentToCheck.getMatchCondition().getValue(); - if (myModel.hasCondition(value) && !ArrangementConfigUtil.isEnabled(value, myFilter, myModel.getCondition())) { - myModel.removeAndCondition(componentToCheck.getMatchCondition()); + if (myConditionInfo.hasCondition(value) && !ArrangementConfigUtil.isEnabled(value, myFilter, newCondition)) { + myConditionInfo.removeCondition(componentToCheck.getMatchCondition().getValue()); + newCondition = myConditionInfo.buildCondition(); } } // There is a possible case that some conditions become unavailable, e.g. changing type from 'field' to 'method' // makes 'volatile' condition inappropriate. - updateState(myModel); + updateState(); return; } } } - myModel.addAndCondition(chosenCondition); - updateState(myModel); + myConditionInfo.addAtomCondition(chosenCondition); + updateState(); } - + @Nullable - private ArrangementAtomMatchNodeComponent getNodeComponentAt(@NotNull Point screenPoint) { - for (ArrangementAtomMatchNodeComponent component : myComponents.values()) { + private ArrangementAtomMatchConditionComponent getNodeComponentAt(@NotNull Point screenPoint) { + for (ArrangementAtomMatchConditionComponent component : myComponents.values()) { Rectangle screenBounds = component.getScreenBounds(); if (screenBounds != null && screenBounds.contains(screenPoint)) { return component; @@ -204,7 +227,7 @@ public class ArrangementRuleEditor extends JPanel { return null; } - private void repaintComponent(@NotNull ArrangementMatchNodeComponent component) { + private void repaintComponent(@NotNull ArrangementMatchConditionComponent component) { Rectangle bounds = component.getScreenBounds(); if (bounds != null) { Point location = bounds.getLocation(); diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleTree.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleTree.java index 76441221c724..491fd7ccca24 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleTree.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleTree.java @@ -15,951 +15,915 @@ */ package com.intellij.application.options.codeStyle.arrangement; -import com.intellij.application.options.codeStyle.arrangement.node.ArrangementEditableNode; -import com.intellij.application.options.codeStyle.arrangement.node.ArrangementRepresentationAwareNode; -import com.intellij.application.options.codeStyle.arrangement.node.ArrangementSectionNode; -import com.intellij.application.options.codeStyle.arrangement.node.match.ArrangementMatchNodeComponent; -import com.intellij.application.options.codeStyle.arrangement.node.match.ArrangementMatchNodeComponentFactory; -import com.intellij.openapi.application.ApplicationBundle; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.Pair; -import com.intellij.psi.codeStyle.arrangement.StdArrangementSettings; -import com.intellij.psi.codeStyle.arrangement.match.StdArrangementEntryMatcher; -import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule; -import com.intellij.psi.codeStyle.arrangement.model.ArrangementCompositeMatchCondition; -import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition; -import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware; -import com.intellij.ui.treeStructure.Tree; -import com.intellij.util.Alarm; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.containers.HashSet; -import com.intellij.util.ui.UIUtil; -import com.intellij.util.ui.tree.WideSelectionTreeUI; -import gnu.trove.*; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import javax.swing.event.TreeSelectionEvent; -import javax.swing.event.TreeSelectionListener; -import javax.swing.tree.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.util.*; -import java.util.List; - /** * @author Denis Zhdanov * @since 8/10/12 2:10 PM */ public class ArrangementRuleTree { - @NotNull private static final JLabel EMPTY_RENDERER = new JLabel(""); - @NotNull private static final JLabel NEW_CONDITION_RENDERER = new JLabel("<empty rule>"); - @NotNull private static final Logger LOG = Logger.getInstance("#" + ArrangementRuleTree.class.getName()); - - private static final int EMPTY_RULE_REMOVE_DELAY_MILLIS = 300; - - @NotNull private final MySelectionModel mySelectionModel = new MySelectionModel(); - @NotNull private final MyModelChangeListener myModelChangeListener = new MyModelChangeListener(); - @NotNull private final MyModelNodesRefresher myModelNodesRefresher = new MyModelNodesRefresher(); - @NotNull private final ArrangementRuleEditingModelBuilder myModelBuilder = new ArrangementRuleEditingModelBuilder(); - @NotNull private final Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); - @NotNull private final RemoveInactiveNewModelRequest myRequest = new RemoveInactiveNewModelRequest(); - @NotNull private final ArrangementGroupingRulesManager myGroupingRulesManager = ArrangementGroupingRulesManager.INSTANCE; - - @NotNull private final TIntObjectHashMap myRenderers = - new TIntObjectHashMap(); - @NotNull private final TIntObjectHashMap myModels = - new TIntObjectHashMap(); - - @NotNull private final ArrangementRuleEditor myEditor; - @NotNull private final ArrangementTreeNode myMatchRulesRoot; - @NotNull private final DefaultTreeModel myTreeModel; - @NotNull private final Tree myTree; - @NotNull private final ArrangementMatchNodeComponentFactory myFactory; - @NotNull private final List> myUiGroupingRules; - - @Nullable private final MutableTreeNode myGroupingsRoot; - - private int myRowUnderMouse = -1; - - private int myCanvasWidth; - private boolean myExplicitSelectionChange; - private boolean mySkipSelectionChange; - - public ArrangementRuleTree(@Nullable StdArrangementSettings settings, - @NotNull List> uiGroupingRules, - @NotNull ArrangementNodeDisplayManager displayManager, - @NotNull ArrangementColorsProvider colorsProvider, - @NotNull ArrangementStandardSettingsAware settingsFilter) - { - myUiGroupingRules = uiGroupingRules; - myEditor = new ArrangementRuleEditor(settingsFilter, colorsProvider, displayManager); - myFactory = new ArrangementMatchNodeComponentFactory(displayManager, colorsProvider, null, uiGroupingRules); - myGroupingsRoot = myGroupingRulesManager.buildAvailableRules(settingsFilter, displayManager); - ArrangementTreeNode root = new ArrangementTreeNode(null); - if (myGroupingsRoot != null) { - root.add(myGroupingsRoot); - } - myMatchRulesRoot = new ArrangementSectionNode(ApplicationBundle.message("arrangement.settings.section.match")); - root.add(myMatchRulesRoot); - myTreeModel = new DefaultTreeModel(root); - - final Condition wideSelectionCondition = new Condition() { - @Override - public boolean value(Integer row) { - TreePath path = myTree.getPathForRow(row); - if (path == null) { - return false; - } - return isEmptyCondition(((ArrangementTreeNode)path.getLastPathComponent()).getBackingCondition()); - } - }; - myTree = new Tree(myTreeModel) { - - @Override - protected void setExpandedState(TreePath path, boolean state) { - // Don't allow node collapse - if (state) { - super.setExpandedState(path, state); - } - } - - @NotNull - @Override - protected Condition getWideSelectionBackgroundCondition() { - return wideSelectionCondition; - } - - @Override - protected void processMouseEvent(MouseEvent e) { - // JTree selects a node on mouse click at the same row (even outside the node bounds). We don't want to support - // such selection because selected nodes are highlighted at the rule tree, so, it produces a 'blink' effect. - if (e.getClickCount() > 0 && getNodeComponentAt(e.getX(), e.getY()) == null) { - TreePath path = myTree.getPathForLocation(e.getX(), e.getY()); - if (path == null || !isEmptyCondition(((ArrangementTreeNode)path.getLastPathComponent()).getBackingCondition())) { - mySkipSelectionChange = true; - } - } - try { - super.processMouseEvent(e); - List activeModels = getActiveModels(); - if (mySkipSelectionChange && activeModels.isEmpty()) { - hideEditor(); - } - } - finally { - mySkipSelectionChange = false; - } - } - }; - myTree.setSelectionModel(mySelectionModel); - myTree.setRootVisible(false); - myTree.setRowHeight(0); // Don't insist on the same row height - mySelectionModel.addTreeSelectionListener(new TreeSelectionListener() { - @Override - public void valueChanged(TreeSelectionEvent e) { - if (myExplicitSelectionChange) { - return; - } - TreePath[] paths = e.getPaths(); - if (paths == null) { - return; - } - for (int i = 0; i < paths.length; i++) { - onSelectionChange(paths[i], e.isAddedPath(i)); - } - } - }); - myTree.addMouseMotionListener(new MouseAdapter() { - @Override - public void mouseMoved(MouseEvent e) { - onMouseMoved(e); - } - }); - myTree.addMouseListener(new MouseAdapter() { - - @Override - public void mouseEntered(MouseEvent e) { - onMouseEntered(e); - } - - @Override - public void mouseExited(MouseEvent e) { - onMouseExited(); - } - - @Override - public void mouseClicked(MouseEvent e) { - onMouseClicked(e); - } - }); - - // Setup the tree to perform rule-aware navigation via up/down arrow keys. - myTree.getActionMap().put("selectNext", new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - selectNextRule(); - } - }); - myTree.getActionMap().put("selectPrevious", new AbstractAction() { - @Override - public void actionPerformed(ActionEvent e) { - selectPreviousRule(); - } - }); - - setSettings(settings); - - myTree.setShowsRootHandles(false); - MyCellRenderer renderer = new MyCellRenderer(); - myTree.setCellRenderer(renderer); - myTree.setEditable(true); - myTree.setCellEditor(new MyCellEditor(myTree, renderer)); - - // IJ wide selection tree ui is not applied for some LAFs (e.g. GTK) and default wide selection tree ui doesn't allow - // to customize selection background appliance algorithm (don't draw background for rule components; do draw for - // 'new rule' node). - // That's why we forcibly apply UI with custom selection strategy here. - // P.S. 'wide selection tree ui' appliance decision is taken at the com.intellij.ui.treeStructure.Tree.setUI(). - myTree.setUI(new WideSelectionTreeUI(true, wideSelectionCondition) { - @Override - protected boolean shouldPaintExpandControl(TreePath path, int row, boolean isExpanded, boolean hasBeenExpanded, boolean isLeaf) { - return false; - } - - @Override - protected void paintSelectedRows(Graphics g, JTree tr) { - if (myRowUnderMouse < 0) { - super.paintSelectedRows(g, tr); - return; - } - final Rectangle rect = tr.getVisibleRect(); - final int firstVisibleRow = tr.getClosestRowForLocation(rect.x, rect.y); - final int lastVisibleRow = tr.getClosestRowForLocation(rect.x, rect.y + rect.height); - - for (int row = firstVisibleRow; row <= lastVisibleRow; row++) { - Color color = null; - if (tr.getSelectionModel().isRowSelected(row) && wideSelectionCondition.value(row)) { - color = UIUtil.getTreeSelectionBackground(tr.hasFocus()); - } - else if (row == myRowUnderMouse) { - color = UIUtil.getDecoratedRowColor(); - } - - if (color == null) { - color = myTree.getBackground(); - } - - final Rectangle bounds = tr.getRowBounds(row); - g.setColor(color); - g.fillRect(0, bounds.y, tr.getWidth(), bounds.height); - } - } - }); - - myEditor.applyBackground(myTree.getBackground()); - } - - private void showEditor() { - List models = getActiveModels(); - if (models.size() != 1) { - return; - } - ArrangementRuleEditingModelImpl model = models.get(0); - int indent = myTree.getRowBounds(model.getRow()).x; - myEditor.updateState(model); - ArrangementTreeNode prevSibling = model.getBottomMost(); - ArrangementTreeNode parent = prevSibling.getParent(); - if (parent == null) { - return; - } - // TODO den check - ArrangementEditorTreeNode editorNode = new ArrangementEditorTreeNode(myEditor, myTreeModel, myCanvasWidth - indent); - myTreeModel.insertNodeInto(editorNode, parent, parent.getIndex(prevSibling) + 1); - } - - private void hideEditor() { - // TODO den implement - } - - public void updateCanvasWidth(final int width) { - myCanvasWidth = width; - myRenderers.forEachKey(new TIntProcedure() { - @Override - public boolean execute(int row) { - doUpdateCanvasWidth(row); - return true; - } - }); - } - - private void doUpdateCanvasWidth(int row) { - if (myCanvasWidth <= 0) { - return; - } - ArrangementMatchNodeComponent component = myRenderers.get(row); - if (component == null) { - return; - } - if (!component.onCanvasWidthChange(myCanvasWidth - myTree.getRowBounds(row).x)) { - return; - } - - TreePath path = myTree.getPathForRow(row); - if (path != null) { - myTreeModel.nodeChanged((TreeNode)path.getLastPathComponent()); - } - } - - private void selectPreviousRule() { - ArrangementTreeNode currentSelectionBottom = getCurrentSelectionBottom(); - - if (currentSelectionBottom == null) { - return; - } - - for (ArrangementTreeNode parent = currentSelectionBottom.getParent(); - parent != null; - currentSelectionBottom = parent, parent = parent.getParent()) - { - int i = parent.getIndex(currentSelectionBottom); - if (i <= 0) { - continue; - } - ArrangementTreeNode toSelect = parent.getChildAt(i - 1); - while (toSelect.getChildCount() > 0) { - toSelect = toSelect.getLastChild(); - } - mySelectionModel.setSelectionPath(new TreePath(toSelect.getPath())); - break; - } - } - - private void selectNextRule() { - ArrangementTreeNode currentSelectionBottom = getCurrentSelectionBottom(); - if (currentSelectionBottom == null) { - if (myMatchRulesRoot.getChildCount() > 0) { - mySelectionModel.setSelectionPath(new TreePath(myMatchRulesRoot.getFirstChild().getPath())); - } - return; - } - - for (ArrangementTreeNode parent = currentSelectionBottom.getParent(); - parent != null; - currentSelectionBottom = parent, parent = parent.getParent()) - { - int i = parent.getIndex(currentSelectionBottom); - if (i < parent.getChildCount() - 1) { - mySelectionModel.setSelectionPath(new TreePath(parent.getChildAt(i + 1).getPath())); - break; - } - } - } - - @Nullable - private ArrangementTreeNode getCurrentSelectionBottom() { - TreePath[] paths = mySelectionModel.getSelectionPaths(); - if (paths == null) { - return null; - } - - ArrangementTreeNode currentSelectionBottom = null; - for (TreePath treePath : paths) { - ArrangementTreeNode last = (ArrangementTreeNode)treePath.getLastPathComponent(); - if (last.getChildCount() <= 0) { - currentSelectionBottom = last; - break; - } - } - - if (currentSelectionBottom == null) { - return null; - } - return currentSelectionBottom; - } - - private void doClearSelection() { - mySelectionModel.clearSelection(); - myRenderers.forEachValue(new TObjectProcedure() { - @Override - public boolean execute(ArrangementMatchNodeComponent node) { - node.setSelected(false); - return true; - } - }); - myTree.repaint(); - } - - /** - * Updates renderer {@link ArrangementMatchNodeComponent#setSelected(boolean) 'selected'} state on tree node selection change. - * - * @param path changed selection path - * @param selected true if given path is selected now; false if given path was selected anymore - */ - private void onSelectionChange(@Nullable final TreePath path, boolean selected) { - if (path == null) { - return; - } - - for (TreePath p = path; p != null; p = p.getParentPath()) { - int row = myTree.getRowForPath(p); - ArrangementTreeNode node = (ArrangementTreeNode)p.getLastPathComponent(); - if (row < 0 && node != null) { - row = node.getRow(); - } - if (row < 0) { - return; - } - ArrangementMatchNodeComponent component = myRenderers.get(row); - if (component != null) { - component.setSelected(selected); - myTreeModel.nodeChanged(node); - repaintComponent(component, false); - } - } - } - - private static void expandAll(Tree tree, TreePath parent) { - // Traverse children - TreeNode node = (TreeNode)parent.getLastPathComponent(); - if (node.getChildCount() > 0) { - for (Enumeration e = node.children(); e.hasMoreElements(); ) { - TreeNode n = (TreeNode)e.nextElement(); - TreePath path = parent.pathByAddingChild(n); - expandAll(tree, path); - } - } - - // Expansion or collapse must be done bottom-up - tree.expandPath(parent); - } - - private void map(@NotNull List rules) { - for (StdArrangementMatchRule rule : rules) { - Pair pair - = myModelBuilder.build(rule, myTree, myMatchRulesRoot, null, myUiGroupingRules); - if (pair != null && pair.first != null) { - myModels.put(pair.first.getRow(), pair.first); - pair.first.addListener(myModelChangeListener); - } - } - } - - /** - * @return matcher model for the selected tree row(s) if any; null otherwise - */ - @NotNull - public List getActiveModels() { - TreePath[] paths = mySelectionModel.getSelectionPaths(); - if (paths == null) { - return Collections.emptyList(); - } - - // There is a possible case that particular settings node is represented on multiple rows and that non-leaf nodes are served - // for more than one rule. No model is registered for them then and we want just to skip them. - List result = new ArrayList(); - Set set = new HashSet(); - for (int i = paths.length - 1; i >= 0; i--) { - int row = myTree.getRowForPath(paths[i]); - ArrangementRuleEditingModelImpl model = myModels.get(row); - if (model != null && !set.contains(model)) { - result.add(model); - set.add(model); - } - } - return result; - } - - /** - * @return UI component encapsulated by the current object. Subsequent calls to this method return the same reference all the time - */ - @NotNull - public Tree getTreeComponent() { - return myTree; - } - - /** - * @return rules configured at the current tree at the moment - */ - @NotNull - public StdArrangementSettings getSettings() { - int[] rows = myModels.keys(); - Arrays.sort(rows); - List rules = new ArrayList(); - ArrangementMatchCondition prevGroup = null; - Set implicitGroupConditions = new HashSet(); - for (int row : rows) { - ArrangementRuleEditingModelImpl model = myModels.get(row); - ArrangementTreeNode topMost = model.getTopMost(); - ArrangementMatchCondition currentGroup = topMost.getBackingCondition(); - if (prevGroup != null && !prevGroup.equals(currentGroup)) { - rules.add(new StdArrangementMatchRule(new StdArrangementEntryMatcher(prevGroup))); - implicitGroupConditions.add(prevGroup); - prevGroup = null; - } - if (!myMatchRulesRoot.equals(topMost) && !topMost.equals(model.getBottomMost()) && !implicitGroupConditions.contains(currentGroup)) { - prevGroup = currentGroup; - } - rules.add(model.getRule()); - } - - if (prevGroup != null) { - rules.add(new StdArrangementMatchRule(new StdArrangementEntryMatcher(prevGroup))); - } - - if (myGroupingsRoot == null) { - return new StdArrangementSettings(rules); - } - else { - return new StdArrangementSettings(myGroupingRulesManager.buildRules(myGroupingsRoot), rules); - } - } - - public void setSettings(@Nullable StdArrangementSettings settings) { - myRenderers.clear(); - myModels.clear(); - while (myMatchRulesRoot.getChildCount() > 0) - myTreeModel.removeNodeFromParent(myMatchRulesRoot.getFirstChild()); - if (settings == null) { - return; - } - - if (myGroupingsRoot != null) { - myGroupingRulesManager.applyRules(settings.getGroupings(), myGroupingsRoot, myTreeModel); - } - - List rules = settings.getRules(); - map(rules); - expandAll(myTree, new TreePath(myTreeModel.getRoot())); - - if (ArrangementConstants.LOG_RULE_MODIFICATION) { - LOG.info("Arrangement tree is refreshed. Given rules:"); - for (StdArrangementMatchRule rule : rules) { - LOG.info(" " + rule.toString()); - } - LOG.info("Following models have been built:"); - myModels.forEachValue(new TObjectProcedure() { - @Override - public boolean execute(ArrangementRuleEditingModelImpl model) { - LOG.info(String.format(" row %d, model '%s'", model.getRow(), model.getRule())); - return true; - } - }); - } - } - - @NotNull - private ArrangementMatchNodeComponent getNodeComponentAt(int row, - @NotNull ArrangementMatchCondition condition, - @Nullable ArrangementRuleEditingModelImpl model) - { - ArrangementMatchNodeComponent result = myRenderers.get(row); - if (result == null || !result.getMatchCondition().equals(condition)) { - myRenderers.put(row, result = myFactory.getComponent(condition, model, true)); - doUpdateCanvasWidth(row); - } - return result; - } - - private void onMouseMoved(@NotNull MouseEvent e) { - int row = myTree.getRowForLocation(e.getX(), e.getY()); - if (row < 0 || row != myRowUnderMouse) { - onMouseExited(); - } - ArrangementMatchNodeComponent component = myRenderers.get(row); - if (component == null) { - return; - } - - onMouseEntered(e); - Rectangle changedScreenRectangle = component.handleMouseMove(e); - if (changedScreenRectangle != null) { - repaintScreenBounds(changedScreenRectangle, false); - } - } - - private void onMouseEntered(@NotNull MouseEvent e) { - int row = myTree.getRowForLocation(e.getX(), e.getY()); - if (row < 0) { - return; - } - - myRowUnderMouse = row; - ArrangementMatchNodeComponent component = myRenderers.get(row); - if (component == null) { - return; - } - repaintComponent(component, true); - } - - private void onMouseExited() { - if (myRowUnderMouse < 0) { - return; - } - - ArrangementMatchNodeComponent component = myRenderers.get(myRowUnderMouse); - myRowUnderMouse = -1; - if (component != null) { - repaintComponent(component, true); - } - } - - private void onMouseClicked(@NotNull MouseEvent e) { - ArrangementMatchNodeComponent component = getNodeComponentAt(e.getX(), e.getY()); - if (component != null) { - component.handleMouseClick(e); - return; - } - - TreePath path = myTree.getPathForLocation(e.getX(), e.getY()); - if (path != null && isEmptyCondition(((ArrangementTreeNode)path.getLastPathComponent()).getBackingCondition())) { - return; - } - - // Clear selection if it was a click at the empty space - doClearSelection(); - } - - @Nullable - private ArrangementMatchNodeComponent getNodeComponentAt(int x, int y) { - int row = myTree.getRowForLocation(x, y); - return myRenderers.get(row); - } - - private void repaintComponent(@NotNull ArrangementMatchNodeComponent component, boolean fromLeftEdge) { - Rectangle bounds = component.getScreenBounds(); - if (bounds != null) { - repaintScreenBounds(bounds, fromLeftEdge); - } - } - - private void repaintScreenBounds(@NotNull Rectangle bounds, boolean fromLeftEdge) { - Point location = bounds.getLocation(); - SwingUtilities.convertPointFromScreen(location, myTree); - int x = location.x; - int width = bounds.width; - if (fromLeftEdge) { - x = 0; - width += location.x; - } - myTree.repaint(x, location.y, width, bounds.height); - } - - private void onModelChange(@NotNull ArrangementRuleEditingModelImpl model, @NotNull final TIntIntHashMap rowChanges) { - processRowChanges(rowChanges); - - // Perform necessary actions for the changed model. - ArrangementTreeNode topMost = model.getTopMost(); - ArrangementTreeNode bottomMost = model.getBottomMost(); - doClearSelection(); - myExplicitSelectionChange = true; - try { - for (ArrangementTreeNode node = bottomMost; node != null; node = node.getParent()) { - TreePath path = new TreePath(node.getPath()); - int row = myTree.getRowForPath(path); - myRenderers.remove(row); - myTreeModel.nodeChanged(node); - if (node == bottomMost) { - mySelectionModel.addSelectionPath(path); - } - ArrangementMatchCondition matchCondition = node.getBackingCondition(); - if (matchCondition != null) { - getNodeComponentAt(row, matchCondition, model).setSelected(true); - } - if (node == topMost) { - break; - } - } - } - finally { - myExplicitSelectionChange = false; - } - } - - private void processRowChanges(TIntIntHashMap rowChanges) { - // Refresh models. - myModels.forEachValue(myModelNodesRefresher); - - // Shift row-based caches. - final TIntObjectHashMap changedModelMappings = - new TIntObjectHashMap(); - rowChanges.forEachEntry(new TIntIntProcedure() { - @Override - public boolean execute(int oldRow, int newRow) { - ArrangementRuleEditingModelImpl m = myModels.remove(oldRow); - if (m != null) { - changedModelMappings.put(newRow, m); - m.setRow(newRow); - } - return true; - } - }); - putAll(changedModelMappings, myModels); - - expandAll(myTree, new TreePath(myTreeModel.getRoot())); - - // Drop JTree visual caches. - rowChanges.forEachEntry(new TIntIntProcedure() { - @Override - public boolean execute(int oldRow, int newRow) { - refreshTreeNode(oldRow); - refreshTreeNode(newRow); - return true; - } - - private void refreshTreeNode(int row) { - TreePath path = myTree.getPathForRow(row); - if (path == null) { - return; - } - TreeNode node = (TreeNode)path.getLastPathComponent(); - if (node == null) { - return; - } - myTreeModel.nodeStructureChanged(node); - } - }); - } - - private static void putAll(@NotNull TIntObjectHashMap from, @NotNull final TIntObjectHashMap to) { - from.forEachEntry(new TIntObjectProcedure() { - @Override - public boolean execute(int key, T value) { - to.put(key, value); - return true; - } - }); - } - - /** - * Asks current model to create a new rule below the currently selected (or at the last position if no one is selected at the moment). - * - * @return model for the newly created row - */ - @NotNull - public ArrangementRuleEditingModel newModel() { - List activeModels = getActiveModels(); - final ArrangementTreeNode anchor = activeModels.size() != 1 ? null : activeModels.get(0).getBottomMost(); - doClearSelection(); - Pair pair = myModelBuilder.build( - ArrangementRuleEditingModel.EMPTY_RULE, myTree, myMatchRulesRoot, anchor, myUiGroupingRules - ); - assert pair != null; - assert pair.first != null; - assert pair.second != null; - processRowChanges(pair.second); - myModels.put(pair.first.getRow(), pair.first); - pair.first.addListener(myModelChangeListener); - mySelectionModel.setSelectionPath(myTree.getPathForRow(pair.first.getRow())); - return pair.first; - } - - private static boolean isEmptyCondition(@Nullable ArrangementMatchCondition condition) { - return condition instanceof ArrangementCompositeMatchCondition - && ((ArrangementCompositeMatchCondition)condition).getOperands().isEmpty(); - } - - @SuppressWarnings("MethodMayBeStatic") - public void disposeUI() { - Container parent = EMPTY_RENDERER.getParent(); - if (parent != null) parent.remove(EMPTY_RENDERER); - } - - private class MyCellRenderer extends DefaultTreeCellRenderer { - @Override - public Component getTreeCellRendererComponent(JTree tree, - Object value, - boolean selected, - boolean expanded, - boolean leaf, - int row, - boolean hasFocus) { - if (value instanceof ArrangementRepresentationAwareNode) { - return ((ArrangementRepresentationAwareNode)value).getRenderer(); - } - - ArrangementMatchCondition node = ((ArrangementTreeNode)value).getBackingCondition(); - if (node == null) { - return EMPTY_RENDERER; - } - if (isEmptyCondition(node)) { - return NEW_CONDITION_RENDERER; - } - - if (row < 0) { - ArrangementMatchNodeComponent component = myFactory.getComponent(node, null, true); - doUpdateCanvasWidth(row); - return component.getUiComponent(); - } - ArrangementMatchNodeComponent component = getNodeComponentAt(row, node, myModels.get(row)); - component.setSelected(selected); - return component.getUiComponent(); - } - } - - private static class MyCellEditor extends DefaultTreeCellEditor { - MyCellEditor(JTree tree, DefaultTreeCellRenderer renderer) { - super(tree, renderer); - } - - @Override - public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) { - if (value instanceof ArrangementEditableNode) { - return ((ArrangementEditableNode)value).getEditor(); - } - else { - return super.getTreeCellEditorComponent(tree, value, isSelected, expanded, leaf, row); - } - } - - @Override - public boolean isCellEditable(EventObject event) { - if (event instanceof MouseEvent) { - MouseEvent mouseEvent = (MouseEvent)event; - TreePath path = tree.getPathForLocation(mouseEvent.getX(), mouseEvent.getY()); - if (path == null) { - return false; - } - return path.getLastPathComponent() instanceof ArrangementEditableNode; - } - return false; - } - } - - private class MySelectionModel extends DefaultTreeSelectionModel { - - MySelectionModel() { - setSelectionMode(CONTIGUOUS_TREE_SELECTION); - } - - @Override - public void addSelectionPath(TreePath path) { - if (!mySkipSelectionChange) { - super.addSelectionPath(path); - } - } - - @Override - public void setSelectionPath(TreePath path) { - if (mySkipSelectionChange) { - return; - } - - myAlarm.cancelAllRequests(); - myAlarm.addRequest(myRequest, EMPTY_RULE_REMOVE_DELAY_MILLIS); - clearSelection(); - ArrangementTreeNode node = (ArrangementTreeNode)path.getLastPathComponent(); - - super.addSelectionPath(path); - - if (node.getChildCount() > 0) { - // Select the whole section. - for (ArrangementTreeNode child = node.getFirstChild(), last = node.getLastChild(); child != null; child = child.getNextNode()) { - addSelectionPath(new TreePath(child.getPath())); - if (child == last) { - break; - } - } - } - - showEditor(); - } - - public boolean isRowSelected(int row) { - int[] rows = getSelectionRows(); - if (rows == null) { - return false; - } - for (int i : rows) { - if (i == row) { - return true; - } - } - return false; - } - } - - private class MyModelChangeListener implements ArrangementRuleEditingModelImpl.Listener { - - private int mySelectedRowToRestore; - - @Override - public void onChanged(@NotNull ArrangementRuleEditingModelImpl model, @NotNull TIntIntHashMap rowChanges) { - onModelChange(model, rowChanges); - } - - @Override - public void beforeModelDestroy(@NotNull ArrangementRuleEditingModelImpl model) { - mySelectedRowToRestore = myTree.getRowForPath(new TreePath(model.getBottomMost().getPath())); - for (ArrangementTreeNode node = model.getBottomMost(); node != null; node = node.getParent()) { - int row = myTree.getRowForPath(new TreePath(node.getPath())); - myRenderers.remove(row); - myModels.remove(row); - if (node == model.getTopMost()) { - break; - } - } - } - - @Override - public void afterModelDestroy(@NotNull TIntIntHashMap rowChanges) { - processRowChanges(rowChanges); - if (!getActiveModels().isEmpty()) { - return; - } - TreePath path = myTree.getPathForRow(mySelectedRowToRestore); - if (path == null) { - ArrangementTreeNode lastLeaf = myMatchRulesRoot.getLastLeaf(); - if (lastLeaf == null) { - return; - } - path = new TreePath(lastLeaf.getPath()); - } - mySelectionModel.setSelectionPath(path); - } - } - - private static class MyModelNodesRefresher implements TObjectProcedure { - @Override - public boolean execute(ArrangementRuleEditingModelImpl model) { - model.refreshTreeNodes(); - return true; - } - } - - private class RemoveInactiveNewModelRequest implements Runnable { - - @SuppressWarnings("ConstantConditions") - @Override - public void run() { - myAlarm.cancelAllRequests(); - Object[] values = myModels.getValues(); - Set activeModels = ContainerUtil.newHashSet(getActiveModels()); - boolean emptyRuleRemoved = false; - for (Object value : values) { - ArrangementRuleEditingModelImpl model = (ArrangementRuleEditingModelImpl)value; - if (model != null && !activeModels.contains(model) && model.getRule() == ArrangementRuleEditingModel.EMPTY_RULE) { - model.destroy(); - emptyRuleRemoved = true; - } - } - - if (!emptyRuleRemoved || activeModels.isEmpty() || !getActiveModels().isEmpty() /* Selection was above the destroyed model */) { - return; - } - - doClearSelection(); - for (Object value : myModels.getValues()) { - ArrangementRuleEditingModelImpl model = (ArrangementRuleEditingModelImpl)value; - if (activeModels.contains(model)) { - mySelectionModel.addSelectionPath(new TreePath(model.getBottomMost().getPath())); - } - } - } - } + //@NotNull private static final JLabel EMPTY_RENDERER = new JLabel(""); + //@NotNull private static final JLabel NEW_CONDITION_RENDERER = new JLabel("<empty rule>"); + //@NotNull private static final Logger LOG = Logger.getInstance("#" + ArrangementRuleTree.class.getName()); + // + //private static final int EMPTY_RULE_REMOVE_DELAY_MILLIS = 300; + // + //@NotNull private final MySelectionModel mySelectionModel = new MySelectionModel(); + //@NotNull private final MyModelChangeListener myModelChangeListener = new MyModelChangeListener(); + //@NotNull private final MyModelNodesRefresher myModelNodesRefresher = new MyModelNodesRefresher(); + //@NotNull private final ArrangementRuleEditingModelBuilder myModelBuilder = new ArrangementRuleEditingModelBuilder(); + //@NotNull private final Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD); + //@NotNull private final RemoveInactiveNewModelRequest myRequest = new RemoveInactiveNewModelRequest(); + //@NotNull private final ArrangementGroupingRulesManager myGroupingRulesManager = ArrangementGroupingRulesManager.INSTANCE; + // + //@NotNull private final TIntObjectHashMap myRenderers = + // new TIntObjectHashMap(); + //@NotNull private final TIntObjectHashMap myModels = + // new TIntObjectHashMap(); + // + //@NotNull private final ArrangementRuleEditor myEditor; + //@NotNull private final ArrangementTreeNode myMatchRulesRoot; + //@NotNull private final DefaultTreeModel myTreeModel; + //@NotNull private final Tree myTree; + //@NotNull private final ArrangementMatchNodeComponentFactory myFactory; + //@NotNull private final List> myUiGroupingRules; + // + //@Nullable private final MutableTreeNode myGroupingsRoot; + // + //private int myRowUnderMouse = -1; + // + //private int myCanvasWidth; + //private boolean myExplicitSelectionChange; + //private boolean mySkipSelectionChange; + // + //public ArrangementRuleTree(@Nullable StdArrangementSettings settings, + // @NotNull List> uiGroupingRules, + // @NotNull ArrangementNodeDisplayManager displayManager, + // @NotNull ArrangementColorsProvider colorsProvider, + // @NotNull ArrangementStandardSettingsAware settingsFilter) + //{ + // myUiGroupingRules = uiGroupingRules; + // myEditor = new ArrangementRuleEditor(settingsFilter, colorsProvider, displayManager); + // myFactory = new ArrangementMatchNodeComponentFactory(displayManager, colorsProvider, null, uiGroupingRules); + // myGroupingsRoot = myGroupingRulesManager.buildAvailableRules(settingsFilter, displayManager); + // ArrangementTreeNode root = new ArrangementTreeNode(null); + // if (myGroupingsRoot != null) { + // root.add(myGroupingsRoot); + // } + // myMatchRulesRoot = new ArrangementSectionNode(ApplicationBundle.message("arrangement.settings.section.match")); + // root.add(myMatchRulesRoot); + // myTreeModel = new DefaultTreeModel(root); + // + // final Condition wideSelectionCondition = new Condition() { + // @Override + // public boolean value(Integer row) { + // TreePath path = myTree.getPathForRow(row); + // if (path == null) { + // return false; + // } + // return isEmptyCondition(((ArrangementTreeNode)path.getLastPathComponent()).getBackingCondition()); + // } + // }; + // myTree = new Tree(myTreeModel) { + // + // @Override + // protected void setExpandedState(TreePath path, boolean state) { + // // Don't allow node collapse + // if (state) { + // super.setExpandedState(path, state); + // } + // } + // + // @NotNull + // @Override + // protected Condition getWideSelectionBackgroundCondition() { + // return wideSelectionCondition; + // } + // + // @Override + // protected void processMouseEvent(MouseEvent e) { + // // JTree selects a node on mouse click at the same row (even outside the node bounds). We don't want to support + // // such selection because selected nodes are highlighted at the rule tree, so, it produces a 'blink' effect. + // if (e.getClickCount() > 0 && getNodeComponentAt(e.getX(), e.getY()) == null) { + // TreePath path = myTree.getPathForLocation(e.getX(), e.getY()); + // if (path == null || !isEmptyCondition(((ArrangementTreeNode)path.getLastPathComponent()).getBackingCondition())) { + // mySkipSelectionChange = true; + // } + // } + // try { + // super.processMouseEvent(e); + // List activeModels = getActiveModels(); + // if (mySkipSelectionChange && activeModels.isEmpty()) { + // hideEditor(); + // } + // } + // finally { + // mySkipSelectionChange = false; + // } + // } + // }; + // myTree.setSelectionModel(mySelectionModel); + // myTree.setRootVisible(false); + // myTree.setRowHeight(0); // Don't insist on the same row height + // mySelectionModel.addTreeSelectionListener(new TreeSelectionListener() { + // @Override + // public void valueChanged(TreeSelectionEvent e) { + // if (myExplicitSelectionChange) { + // return; + // } + // TreePath[] paths = e.getPaths(); + // if (paths == null) { + // return; + // } + // for (int i = 0; i < paths.length; i++) { + // onSelectionChange(paths[i], e.isAddedPath(i)); + // } + // } + // }); + // myTree.addMouseMotionListener(new MouseAdapter() { + // @Override + // public void mouseMoved(MouseEvent e) { + // onMouseMoved(e); + // } + // }); + // myTree.addMouseListener(new MouseAdapter() { + // + // @Override + // public void mouseEntered(MouseEvent e) { + // onMouseEntered(e); + // } + // + // @Override + // public void mouseExited(MouseEvent e) { + // onMouseExited(); + // } + // + // @Override + // public void mouseClicked(MouseEvent e) { + // onMouseClicked(e); + // } + // }); + // + // // Setup the tree to perform rule-aware navigation via up/down arrow keys. + // myTree.getActionMap().put("selectNext", new AbstractAction() { + // @Override + // public void actionPerformed(ActionEvent e) { + // selectNextRule(); + // } + // }); + // myTree.getActionMap().put("selectPrevious", new AbstractAction() { + // @Override + // public void actionPerformed(ActionEvent e) { + // selectPreviousRule(); + // } + // }); + // + // setSettings(settings); + // + // myTree.setShowsRootHandles(false); + // MyCellRenderer renderer = new MyCellRenderer(); + // myTree.setCellRenderer(renderer); + // myTree.setEditable(true); + // myTree.setCellEditor(new MyCellEditor(myTree, renderer)); + // + // // IJ wide selection tree ui is not applied for some LAFs (e.g. GTK) and default wide selection tree ui doesn't allow + // // to customize selection background appliance algorithm (don't draw background for rule components; do draw for + // // 'new rule' node). + // // That's why we forcibly apply UI with custom selection strategy here. + // // P.S. 'wide selection tree ui' appliance decision is taken at the com.intellij.ui.treeStructure.Tree.setUI(). + // myTree.setUI(new WideSelectionTreeUI(true, wideSelectionCondition) { + // @Override + // protected boolean shouldPaintExpandControl(TreePath path, int row, boolean isExpanded, boolean hasBeenExpanded, boolean isLeaf) { + // return false; + // } + // + // @Override + // protected void paintSelectedRows(Graphics g, JTree tr) { + // if (myRowUnderMouse < 0) { + // super.paintSelectedRows(g, tr); + // return; + // } + // final Rectangle rect = tr.getVisibleRect(); + // final int firstVisibleRow = tr.getClosestRowForLocation(rect.x, rect.y); + // final int lastVisibleRow = tr.getClosestRowForLocation(rect.x, rect.y + rect.height); + // + // for (int row = firstVisibleRow; row <= lastVisibleRow; row++) { + // Color color = null; + // if (tr.getSelectionModel().isRowSelected(row) && wideSelectionCondition.value(row)) { + // color = UIUtil.getTreeSelectionBackground(tr.hasFocus()); + // } + // else if (row == myRowUnderMouse) { + // color = UIUtil.getDecoratedRowColor(); + // } + // + // if (color == null) { + // color = myTree.getBackground(); + // } + // + // final Rectangle bounds = tr.getRowBounds(row); + // g.setColor(color); + // g.fillRect(0, bounds.y, tr.getWidth(), bounds.height); + // } + // } + // }); + // + // myEditor.applyBackground(myTree.getBackground()); + //} + // + //private void showEditor() { + // List models = getActiveModels(); + // if (models.size() != 1) { + // return; + // } + // ArrangementRuleEditingModelImpl model = models.get(0); + // int indent = myTree.getRowBounds(model.getRow()).x; + // myEditor.updateState(model); + // ArrangementTreeNode prevSibling = model.getBottomMost(); + // ArrangementTreeNode parent = prevSibling.getParent(); + // if (parent == null) { + // return; + // } + // // TODO den check + // ArrangementEditorTreeNode editorNode = new ArrangementEditorTreeNode(myEditor, myTreeModel, myCanvasWidth - indent); + // myTreeModel.insertNodeInto(editorNode, parent, parent.getIndex(prevSibling) + 1); + //} + // + //private void hideEditor() { + // // TODO den implement + //} + // + //public void updateCanvasWidth(final int width) { + // myCanvasWidth = width; + // myRenderers.forEachKey(new TIntProcedure() { + // @Override + // public boolean execute(int row) { + // doUpdateCanvasWidth(row); + // return true; + // } + // }); + //} + // + //private void doUpdateCanvasWidth(int row) { + // if (myCanvasWidth <= 0) { + // return; + // } + // ArrangementMatchConditionComponent component = myRenderers.get(row); + // if (component == null) { + // return; + // } + // if (!component.onCanvasWidthChange(myCanvasWidth - myTree.getRowBounds(row).x)) { + // return; + // } + // + // TreePath path = myTree.getPathForRow(row); + // if (path != null) { + // myTreeModel.nodeChanged((TreeNode)path.getLastPathComponent()); + // } + //} + // + //private void selectPreviousRule() { + // ArrangementTreeNode currentSelectionBottom = getCurrentSelectionBottom(); + // + // if (currentSelectionBottom == null) { + // return; + // } + // + // for (ArrangementTreeNode parent = currentSelectionBottom.getParent(); + // parent != null; + // currentSelectionBottom = parent, parent = parent.getParent()) + // { + // int i = parent.getIndex(currentSelectionBottom); + // if (i <= 0) { + // continue; + // } + // ArrangementTreeNode toSelect = parent.getChildAt(i - 1); + // while (toSelect.getChildCount() > 0) { + // toSelect = toSelect.getLastChild(); + // } + // mySelectionModel.setSelectionPath(new TreePath(toSelect.getPath())); + // break; + // } + //} + // + //private void selectNextRule() { + // ArrangementTreeNode currentSelectionBottom = getCurrentSelectionBottom(); + // if (currentSelectionBottom == null) { + // if (myMatchRulesRoot.getChildCount() > 0) { + // mySelectionModel.setSelectionPath(new TreePath(myMatchRulesRoot.getFirstChild().getPath())); + // } + // return; + // } + // + // for (ArrangementTreeNode parent = currentSelectionBottom.getParent(); + // parent != null; + // currentSelectionBottom = parent, parent = parent.getParent()) + // { + // int i = parent.getIndex(currentSelectionBottom); + // if (i < parent.getChildCount() - 1) { + // mySelectionModel.setSelectionPath(new TreePath(parent.getChildAt(i + 1).getPath())); + // break; + // } + // } + //} + // + //@Nullable + //private ArrangementTreeNode getCurrentSelectionBottom() { + // TreePath[] paths = mySelectionModel.getSelectionPaths(); + // if (paths == null) { + // return null; + // } + // + // ArrangementTreeNode currentSelectionBottom = null; + // for (TreePath treePath : paths) { + // ArrangementTreeNode last = (ArrangementTreeNode)treePath.getLastPathComponent(); + // if (last.getChildCount() <= 0) { + // currentSelectionBottom = last; + // break; + // } + // } + // + // if (currentSelectionBottom == null) { + // return null; + // } + // return currentSelectionBottom; + //} + // + //private void doClearSelection() { + // mySelectionModel.clearSelection(); + // myRenderers.forEachValue(new TObjectProcedure() { + // @Override + // public boolean execute(ArrangementMatchConditionComponent node) { + // node.setSelected(false); + // return true; + // } + // }); + // myTree.repaint(); + //} + // + ///** + // * Updates renderer {@link ArrangementMatchConditionComponent#setSelected(boolean) 'selected'} state on tree node selection change. + // * + // * @param path changed selection path + // * @param selected true if given path is selected now; false if given path was selected anymore + // */ + //private void onSelectionChange(@Nullable final TreePath path, boolean selected) { + // if (path == null) { + // return; + // } + // + // for (TreePath p = path; p != null; p = p.getParentPath()) { + // int row = myTree.getRowForPath(p); + // ArrangementTreeNode node = (ArrangementTreeNode)p.getLastPathComponent(); + // if (row < 0 && node != null) { + // row = node.getRow(); + // } + // if (row < 0) { + // return; + // } + // ArrangementMatchConditionComponent component = myRenderers.get(row); + // if (component != null) { + // component.setSelected(selected); + // myTreeModel.nodeChanged(node); + // repaintComponent(component, false); + // } + // } + //} + // + //private static void expandAll(Tree tree, TreePath parent) { + // // Traverse children + // TreeNode node = (TreeNode)parent.getLastPathComponent(); + // if (node.getChildCount() > 0) { + // for (Enumeration e = node.children(); e.hasMoreElements(); ) { + // TreeNode n = (TreeNode)e.nextElement(); + // TreePath path = parent.pathByAddingChild(n); + // expandAll(tree, path); + // } + // } + // + // // Expansion or collapse must be done bottom-up + // tree.expandPath(parent); + //} + // + //private void map(@NotNull List rules) { + // for (StdArrangementMatchRule rule : rules) { + // Pair pair + // = myModelBuilder.build(rule, myTree, myMatchRulesRoot, null, myUiGroupingRules); + // if (pair != null && pair.first != null) { + // myModels.put(pair.first.getRow(), pair.first); + // pair.first.addListener(myModelChangeListener); + // } + // } + //} + // + ///** + // * @return matcher model for the selected tree row(s) if any; null otherwise + // */ + //@NotNull + //public List getActiveModels() { + // TreePath[] paths = mySelectionModel.getSelectionPaths(); + // if (paths == null) { + // return Collections.emptyList(); + // } + // + // // There is a possible case that particular settings node is represented on multiple rows and that non-leaf nodes are served + // // for more than one rule. No model is registered for them then and we want just to skip them. + // List result = new ArrayList(); + // Set set = new HashSet(); + // for (int i = paths.length - 1; i >= 0; i--) { + // int row = myTree.getRowForPath(paths[i]); + // ArrangementRuleEditingModelImpl model = myModels.get(row); + // if (model != null && !set.contains(model)) { + // result.add(model); + // set.add(model); + // } + // } + // return result; + //} + // + ///** + // * @return UI component encapsulated by the current object. Subsequent calls to this method return the same reference all the time + // */ + //@NotNull + //public Tree getTreeComponent() { + // return myTree; + //} + // + ///** + // * @return rules configured at the current tree at the moment + // */ + //@NotNull + //public StdArrangementSettings getSettings() { + // int[] rows = myModels.keys(); + // Arrays.sort(rows); + // List rules = new ArrayList(); + // ArrangementMatchCondition prevGroup = null; + // Set implicitGroupConditions = new HashSet(); + // for (int row : rows) { + // ArrangementRuleEditingModelImpl model = myModels.get(row); + // ArrangementTreeNode topMost = model.getTopMost(); + // ArrangementMatchCondition currentGroup = topMost.getBackingCondition(); + // if (prevGroup != null && !prevGroup.equals(currentGroup)) { + // rules.add(new StdArrangementMatchRule(new StdArrangementEntryMatcher(prevGroup))); + // implicitGroupConditions.add(prevGroup); + // prevGroup = null; + // } + // if (!myMatchRulesRoot.equals(topMost) && !topMost.equals(model.getBottomMost()) && !implicitGroupConditions.contains(currentGroup)) { + // prevGroup = currentGroup; + // } + // rules.add(model.getRule()); + // } + // + // if (prevGroup != null) { + // rules.add(new StdArrangementMatchRule(new StdArrangementEntryMatcher(prevGroup))); + // } + // + // if (myGroupingsRoot == null) { + // return new StdArrangementSettings(rules); + // } + // else { + // return new StdArrangementSettings(myGroupingRulesManager.buildRules(myGroupingsRoot), rules); + // } + //} + // + //public void setSettings(@Nullable StdArrangementSettings settings) { + // myRenderers.clear(); + // myModels.clear(); + // while (myMatchRulesRoot.getChildCount() > 0) + // myTreeModel.removeNodeFromParent(myMatchRulesRoot.getFirstChild()); + // if (settings == null) { + // return; + // } + // + // if (myGroupingsRoot != null) { + // myGroupingRulesManager.applyRules(settings.getGroupings(), myGroupingsRoot, myTreeModel); + // } + // + // List rules = settings.getRules(); + // map(rules); + // expandAll(myTree, new TreePath(myTreeModel.getRoot())); + // + // if (ArrangementConstants.LOG_RULE_MODIFICATION) { + // LOG.info("Arrangement tree is refreshed. Given rules:"); + // for (StdArrangementMatchRule rule : rules) { + // LOG.info(" " + rule.toString()); + // } + // LOG.info("Following models have been built:"); + // myModels.forEachValue(new TObjectProcedure() { + // @Override + // public boolean execute(ArrangementRuleEditingModelImpl model) { + // LOG.info(String.format(" row %d, model '%s'", model.getRow(), model.getRule())); + // return true; + // } + // }); + // } + //} + // + //@NotNull + //private ArrangementMatchConditionComponent getNodeComponentAt(int row, + // @NotNull ArrangementMatchCondition condition, + // @Nullable ArrangementRuleEditingModelImpl model) + //{ + // ArrangementMatchConditionComponent result = myRenderers.get(row); + // if (result == null || !result.getMatchCondition().equals(condition)) { + // myRenderers.put(row, result = myFactory.getComponent(condition, model, true)); + // doUpdateCanvasWidth(row); + // } + // return result; + //} + // + //private void onMouseMoved(@NotNull MouseEvent e) { + // int row = myTree.getRowForLocation(e.getX(), e.getY()); + // if (row < 0 || row != myRowUnderMouse) { + // onMouseExited(); + // } + // ArrangementMatchConditionComponent component = myRenderers.get(row); + // if (component == null) { + // return; + // } + // + // onMouseEntered(e); + // Rectangle changedScreenRectangle = component.handleMouseMove(e); + // if (changedScreenRectangle != null) { + // repaintScreenBounds(changedScreenRectangle, false); + // } + //} + // + //private void onMouseEntered(@NotNull MouseEvent e) { + // int row = myTree.getRowForLocation(e.getX(), e.getY()); + // if (row < 0) { + // return; + // } + // + // myRowUnderMouse = row; + // ArrangementMatchConditionComponent component = myRenderers.get(row); + // if (component == null) { + // return; + // } + // repaintComponent(component, true); + //} + // + //private void onMouseExited() { + // if (myRowUnderMouse < 0) { + // return; + // } + // + // ArrangementMatchConditionComponent component = myRenderers.get(myRowUnderMouse); + // myRowUnderMouse = -1; + // if (component != null) { + // repaintComponent(component, true); + // } + //} + // + //private void onMouseClicked(@NotNull MouseEvent e) { + // ArrangementMatchConditionComponent component = getNodeComponentAt(e.getX(), e.getY()); + // if (component != null) { + // component.handleMouseClick(e); + // return; + // } + // + // TreePath path = myTree.getPathForLocation(e.getX(), e.getY()); + // if (path != null && isEmptyCondition(((ArrangementTreeNode)path.getLastPathComponent()).getBackingCondition())) { + // return; + // } + // + // // Clear selection if it was a click at the empty space + // doClearSelection(); + //} + // + //@Nullable + //private ArrangementMatchConditionComponent getNodeComponentAt(int x, int y) { + // int row = myTree.getRowForLocation(x, y); + // return myRenderers.get(row); + //} + // + //private void repaintComponent(@NotNull ArrangementMatchConditionComponent component, boolean fromLeftEdge) { + // Rectangle bounds = component.getScreenBounds(); + // if (bounds != null) { + // repaintScreenBounds(bounds, fromLeftEdge); + // } + //} + // + //private void repaintScreenBounds(@NotNull Rectangle bounds, boolean fromLeftEdge) { + // Point location = bounds.getLocation(); + // SwingUtilities.convertPointFromScreen(location, myTree); + // int x = location.x; + // int width = bounds.width; + // if (fromLeftEdge) { + // x = 0; + // width += location.x; + // } + // myTree.repaint(x, location.y, width, bounds.height); + //} + // + //private void onModelChange(@NotNull ArrangementRuleEditingModelImpl model, @NotNull final TIntIntHashMap rowChanges) { + // processRowChanges(rowChanges); + // + // // Perform necessary actions for the changed model. + // ArrangementTreeNode topMost = model.getTopMost(); + // ArrangementTreeNode bottomMost = model.getBottomMost(); + // doClearSelection(); + // myExplicitSelectionChange = true; + // try { + // for (ArrangementTreeNode node = bottomMost; node != null; node = node.getParent()) { + // TreePath path = new TreePath(node.getPath()); + // int row = myTree.getRowForPath(path); + // myRenderers.remove(row); + // myTreeModel.nodeChanged(node); + // if (node == bottomMost) { + // mySelectionModel.addSelectionPath(path); + // } + // ArrangementMatchCondition matchCondition = node.getBackingCondition(); + // if (matchCondition != null) { + // getNodeComponentAt(row, matchCondition, model).setSelected(true); + // } + // if (node == topMost) { + // break; + // } + // } + // } + // finally { + // myExplicitSelectionChange = false; + // } + //} + // + //private void processRowChanges(TIntIntHashMap rowChanges) { + // // Refresh models. + // myModels.forEachValue(myModelNodesRefresher); + // + // // Shift row-based caches. + // final TIntObjectHashMap changedModelMappings = + // new TIntObjectHashMap(); + // rowChanges.forEachEntry(new TIntIntProcedure() { + // @Override + // public boolean execute(int oldRow, int newRow) { + // ArrangementRuleEditingModelImpl m = myModels.remove(oldRow); + // if (m != null) { + // changedModelMappings.put(newRow, m); + // m.setRow(newRow); + // } + // return true; + // } + // }); + // putAll(changedModelMappings, myModels); + // + // expandAll(myTree, new TreePath(myTreeModel.getRoot())); + // + // // Drop JTree visual caches. + // rowChanges.forEachEntry(new TIntIntProcedure() { + // @Override + // public boolean execute(int oldRow, int newRow) { + // refreshTreeNode(oldRow); + // refreshTreeNode(newRow); + // return true; + // } + // + // private void refreshTreeNode(int row) { + // TreePath path = myTree.getPathForRow(row); + // if (path == null) { + // return; + // } + // TreeNode node = (TreeNode)path.getLastPathComponent(); + // if (node == null) { + // return; + // } + // myTreeModel.nodeStructureChanged(node); + // } + // }); + //} + // + //private static void putAll(@NotNull TIntObjectHashMap from, @NotNull final TIntObjectHashMap to) { + // from.forEachEntry(new TIntObjectProcedure() { + // @Override + // public boolean execute(int key, T value) { + // to.put(key, value); + // return true; + // } + // }); + //} + // + ///** + // * Asks current model to create a new rule below the currently selected (or at the last position if no one is selected at the moment). + // * + // * @return model for the newly created row + // */ + //@NotNull + //public ArrangementRuleEditingModel newModel() { + // List activeModels = getActiveModels(); + // final ArrangementTreeNode anchor = activeModels.size() != 1 ? null : activeModels.get(0).getBottomMost(); + // doClearSelection(); + // Pair pair = myModelBuilder.build( + // ArrangementRuleEditingModel.EMPTY_RULE, myTree, myMatchRulesRoot, anchor, myUiGroupingRules + // ); + // assert pair != null; + // assert pair.first != null; + // assert pair.second != null; + // processRowChanges(pair.second); + // myModels.put(pair.first.getRow(), pair.first); + // pair.first.addListener(myModelChangeListener); + // mySelectionModel.setSelectionPath(myTree.getPathForRow(pair.first.getRow())); + // return pair.first; + //} + // + //private static boolean isEmptyCondition(@Nullable ArrangementMatchCondition condition) { + // return condition instanceof ArrangementCompositeMatchCondition + // && ((ArrangementCompositeMatchCondition)condition).getOperands().isEmpty(); + //} + // + //@SuppressWarnings("MethodMayBeStatic") + //public void disposeUI() { + // Container parent = EMPTY_RENDERER.getParent(); + // if (parent != null) parent.remove(EMPTY_RENDERER); + //} + // + //private class MyCellRenderer extends DefaultTreeCellRenderer { + // @Override + // public Component getTreeCellRendererComponent(JTree tree, + // Object value, + // boolean selected, + // boolean expanded, + // boolean leaf, + // int row, + // boolean hasFocus) { + // if (value instanceof ArrangementRepresentationAwareNode) { + // return ((ArrangementRepresentationAwareNode)value).getRenderer(); + // } + // + // ArrangementMatchCondition node = ((ArrangementTreeNode)value).getBackingCondition(); + // if (node == null) { + // return EMPTY_RENDERER; + // } + // if (isEmptyCondition(node)) { + // return NEW_CONDITION_RENDERER; + // } + // + // if (row < 0) { + // ArrangementMatchConditionComponent component = myFactory.getComponent(node, null, true); + // doUpdateCanvasWidth(row); + // return component.getUiComponent(); + // } + // ArrangementMatchConditionComponent component = getNodeComponentAt(row, node, myModels.get(row)); + // component.setSelected(selected); + // return component.getUiComponent(); + // } + //} + // + //private static class MyCellEditor extends DefaultTreeCellEditor { + // MyCellEditor(JTree tree, DefaultTreeCellRenderer renderer) { + // super(tree, renderer); + // } + // + // @Override + // public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) { + // if (value instanceof ArrangementEditableNode) { + // return ((ArrangementEditableNode)value).getEditor(); + // } + // else { + // return super.getTreeCellEditorComponent(tree, value, isSelected, expanded, leaf, row); + // } + // } + // + // @Override + // public boolean isCellEditable(EventObject event) { + // if (event instanceof MouseEvent) { + // MouseEvent mouseEvent = (MouseEvent)event; + // TreePath path = tree.getPathForLocation(mouseEvent.getX(), mouseEvent.getY()); + // if (path == null) { + // return false; + // } + // return path.getLastPathComponent() instanceof ArrangementEditableNode; + // } + // return false; + // } + //} + // + //private class MySelectionModel extends DefaultTreeSelectionModel { + // + // MySelectionModel() { + // setSelectionMode(CONTIGUOUS_TREE_SELECTION); + // } + // + // @Override + // public void addSelectionPath(TreePath path) { + // if (!mySkipSelectionChange) { + // super.addSelectionPath(path); + // } + // } + // + // @Override + // public void setSelectionPath(TreePath path) { + // if (mySkipSelectionChange) { + // return; + // } + // + // myAlarm.cancelAllRequests(); + // myAlarm.addRequest(myRequest, EMPTY_RULE_REMOVE_DELAY_MILLIS); + // clearSelection(); + // ArrangementTreeNode node = (ArrangementTreeNode)path.getLastPathComponent(); + // + // super.addSelectionPath(path); + // + // if (node.getChildCount() > 0) { + // // Select the whole section. + // for (ArrangementTreeNode child = node.getFirstChild(), last = node.getLastChild(); child != null; child = child.getNextNode()) { + // addSelectionPath(new TreePath(child.getPath())); + // if (child == last) { + // break; + // } + // } + // } + // + // showEditor(); + // } + // + // public boolean isRowSelected(int row) { + // int[] rows = getSelectionRows(); + // if (rows == null) { + // return false; + // } + // for (int i : rows) { + // if (i == row) { + // return true; + // } + // } + // return false; + // } + //} + // + //private class MyModelChangeListener implements ArrangementRuleEditingModelImpl.Listener { + // + // private int mySelectedRowToRestore; + // + // @Override + // public void onChanged(@NotNull ArrangementRuleEditingModelImpl model, @NotNull TIntIntHashMap rowChanges) { + // onModelChange(model, rowChanges); + // } + // + // @Override + // public void beforeModelDestroy(@NotNull ArrangementRuleEditingModelImpl model) { + // mySelectedRowToRestore = myTree.getRowForPath(new TreePath(model.getBottomMost().getPath())); + // for (ArrangementTreeNode node = model.getBottomMost(); node != null; node = node.getParent()) { + // int row = myTree.getRowForPath(new TreePath(node.getPath())); + // myRenderers.remove(row); + // myModels.remove(row); + // if (node == model.getTopMost()) { + // break; + // } + // } + // } + // + // @Override + // public void afterModelDestroy(@NotNull TIntIntHashMap rowChanges) { + // processRowChanges(rowChanges); + // if (!getActiveModels().isEmpty()) { + // return; + // } + // TreePath path = myTree.getPathForRow(mySelectedRowToRestore); + // if (path == null) { + // ArrangementTreeNode lastLeaf = myMatchRulesRoot.getLastLeaf(); + // if (lastLeaf == null) { + // return; + // } + // path = new TreePath(lastLeaf.getPath()); + // } + // mySelectionModel.setSelectionPath(path); + // } + //} + // + //private static class MyModelNodesRefresher implements TObjectProcedure { + // @Override + // public boolean execute(ArrangementRuleEditingModelImpl model) { + // model.refreshTreeNodes(); + // return true; + // } + //} + // + //private class RemoveInactiveNewModelRequest implements Runnable { + // + // @SuppressWarnings("ConstantConditions") + // @Override + // public void run() { + // myAlarm.cancelAllRequests(); + // Object[] values = myModels.getValues(); + // Set activeModels = ContainerUtil.newHashSet(getActiveModels()); + // boolean emptyRuleRemoved = false; + // for (Object value : values) { + // ArrangementRuleEditingModelImpl model = (ArrangementRuleEditingModelImpl)value; + // if (model != null && !activeModels.contains(model) && model.getRule() == ArrangementRuleEditingModel.EMPTY_RULE) { + // model.destroy(); + // emptyRuleRemoved = true; + // } + // } + // + // if (!emptyRuleRemoved || activeModels.isEmpty() || !getActiveModels().isEmpty() /* Selection was above the destroyed model */) { + // return; + // } + // + // doClearSelection(); + // for (Object value : myModels.getValues()) { + // ArrangementRuleEditingModelImpl model = (ArrangementRuleEditingModelImpl)value; + // if (activeModels.contains(model)) { + // mySelectionModel.addSelectionPath(new TreePath(model.getBottomMost().getPath())); + // } + // } + // } + //} } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementSettingsPanel.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementSettingsPanel.java index be6a0c2e842d..971c299a2de7 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementSettingsPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementSettingsPanel.java @@ -26,7 +26,6 @@ import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.application.ApplicationBundle; import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.highlighter.EditorHighlighter; -import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Ref; import com.intellij.psi.codeStyle.CodeStyleSettings; import com.intellij.psi.codeStyle.arrangement.Rearranger; @@ -68,7 +67,7 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel { @NotNull private final Language myLanguage; @NotNull private final ArrangementStandardSettingsAware mySettingsAware; - @NotNull private final ArrangementRuleTree myRuleTree; + @NotNull private final ArrangementRuleTree myRuleTree = null; public ArrangementSettingsPanel(@NotNull CodeStyleSettings settings, @NotNull Language language) { super(settings); @@ -106,8 +105,9 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel { toolbarControl.setBorder(IdeBorderFactory.createBorder(SideBorder.LEFT | SideBorder.TOP | SideBorder.RIGHT)); myContent.add(toolbarControl, new GridBag().weightx(1).fillCellHorizontally().coverLine()); - myRuleTree = new ArrangementRuleTree(getSettings(settings), groupingRules, displayManager, colorsProvider, mySettingsAware); - final Tree treeComponent = myRuleTree.getTreeComponent(); + //myRuleTree = new ArrangementRuleTree(getSettings(settings), groupingRules, displayManager, colorsProvider, mySettingsAware); + //final Tree treeComponent = myRuleTree.getTreeComponent(); + final Tree treeComponent = null; actionToolbar.setTargetComponent(treeComponent); JBScrollPane scrollPane = new JBScrollPane(treeComponent); myContent.add(scrollPane, new GridBag().weightx(1).weighty(1).fillCell().coverLine()); @@ -138,7 +138,7 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel { } if (myWidth != visibleRect.width) { myWidth = visibleRect.width; - myRuleTree.updateCanvasWidth(myWidth); + //myRuleTree.updateCanvasWidth(myWidth); } } }); @@ -178,20 +178,20 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel { } else { myYToRestore = -1; - List models = myRuleTree.getActiveModels(); - if (models.size() == 1) { - Rectangle bounds = treeComponent.getPathBounds(new TreePath(models.get(0).getBottomMost().getPath())); - if (bounds != null) { - myYToRestore = bounds.y; - Rectangle viewRect = viewport.getViewRect(); - if (bounds.y < viewRect.y) { - scroll(bounds.y); - } - else if (bounds.y + bounds.height >= viewRect.y + viewRect.height) { - scroll(bounds.y + bounds.height - viewRect.height); - } - } - } + //List models = myRuleTree.getActiveModels(); + //if (models.size() == 1) { + // Rectangle bounds = treeComponent.getPathBounds(new TreePath(models.get(0).getBottomMost().getPath())); + // if (bounds != null) { + // myYToRestore = bounds.y; + // Rectangle viewRect = viewport.getViewRect(); + // if (bounds.y < viewRect.y) { + // scroll(bounds.y); + // } + // else if (bounds.y + bounds.height >= viewRect.y + viewRect.height) { + // scroll(bounds.y + bounds.height - viewRect.height); + // } + // } + //} } } @@ -217,7 +217,7 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel { @Override public void run() { treeComponent.requestFocus(); - ArrangementRuleEditingModel model = myRuleTree.newModel(); + //ArrangementRuleEditingModel model = myRuleTree.newModel(); //ruleEditor.updateState(model, scrollPane.getVisibleRect().width); resetEditor.set(Boolean.FALSE); try { @@ -232,10 +232,10 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel { @Override public void run() { treeComponent.requestFocus(); - List models = myRuleTree.getActiveModels(); - for (ArrangementRuleEditingModelImpl model : models) { - model.destroy(); - } + //List models = myRuleTree.getActiveModels(); + //for (ArrangementRuleEditingModelImpl model : models) { + // model.destroy(); + //} } }; final NotNullFunction updateMoveFunction = new NotNullFunction() { @@ -320,17 +320,18 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel { @Override public boolean isModified(@NotNull CodeStyleSettings settings) { - return !Comparing.equal(getSettings(settings), myRuleTree.getSettings()); + //return !Comparing.equal(getSettings(settings), myRuleTree.getSettings()); + return false; } @Override public void apply(@NotNull CodeStyleSettings settings) { - settings.getCommonSettings(myLanguage).setArrangementSettings(myRuleTree.getSettings()); + //settings.getCommonSettings(myLanguage).setArrangementSettings(myRuleTree.getSettings()); } @Override protected void resetImpl(@NotNull CodeStyleSettings settings) { - myRuleTree.setSettings(getSettings(settings)); + //myRuleTree.setSettings(getSettings(settings)); } @Override @@ -346,6 +347,6 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel { @Override public void dispose() { super.dispose(); - myRuleTree.disposeUI(); + //myRuleTree.disposeUI(); } } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementTreeNode.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementTreeNode.java deleted file mode 100644 index 79fa35b7962d..000000000000 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementTreeNode.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2000-2012 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.intellij.application.options.codeStyle.arrangement; - -import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.tree.DefaultMutableTreeNode; - -/** - * JTree node for arrangement rule tree. - *

- * The general idea is to provide two additional properties - {@link #getBackingCondition() backing condition} and {@link #getRow() row} - * and encapsulate class casts. - * - * @author Denis Zhdanov - * @since 8/20/12 10:53 PM - */ -public class ArrangementTreeNode extends DefaultMutableTreeNode { - - private static final int NO_ROW = -1; - - @Nullable private final ArrangementMatchCondition myCondition; - private int myRow; - - public ArrangementTreeNode(@Nullable ArrangementMatchCondition condition) { - this(condition, NO_ROW); - } - - public ArrangementTreeNode(@Nullable ArrangementMatchCondition condition, int row) { - myCondition = condition; - myRow = row; - } - - @Nullable - public ArrangementMatchCondition getBackingCondition() { - return myCondition; - } - - public boolean isRowSet() { - return myRow >= 0; - } - - public int getRow() { - return myRow; - } - - public void markRow(int row) { - myRow = row; - } - - public void resetRow() { - myRow = NO_ROW; - } - - @NotNull - public ArrangementTreeNode copy() { - // Settings are copied by-ref intentionally here. - return new ArrangementTreeNode(myCondition, myRow); - } - - @Nullable - @Override - public ArrangementTreeNode getParent() { - return (ArrangementTreeNode)super.getParent(); - } - - @Override - public ArrangementTreeNode getChildAt(int index) { - return (ArrangementTreeNode)super.getChildAt(index); - } - - @Override - public ArrangementTreeNode getFirstChild() { - return (ArrangementTreeNode)super.getFirstChild(); - } - - @Override - public ArrangementTreeNode getLastChild() { - return (ArrangementTreeNode)super.getLastChild(); - } - - @Override - public ArrangementTreeNode getNextSibling() { - return (ArrangementTreeNode)super.getNextSibling(); - } - - @Override - public ArrangementTreeNode getNextNode() { - return (ArrangementTreeNode)super.getNextNode(); - } - - @Override - public ArrangementTreeNode getPreviousNode() { - return (ArrangementTreeNode)super.getPreviousNode(); - } - - @Override - public ArrangementTreeNode getLastLeaf() { - return (ArrangementTreeNode)super.getLastLeaf(); - } - - @Override - public String toString() { - return myCondition == null ? "" : myCondition.toString() + (myRow >= 0 ? ": row=" + myRow : ""); - } -} diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/newui/ArrangementMatchingRulesList.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/newui/ArrangementMatchingRulesList.java new file mode 100644 index 000000000000..d4ac69b259a9 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/newui/ArrangementMatchingRulesList.java @@ -0,0 +1,103 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.application.options.codeStyle.arrangement.newui; + +import com.intellij.application.options.codeStyle.arrangement.ArrangementColorsProvider; +import com.intellij.application.options.codeStyle.arrangement.ArrangementConstants; +import com.intellij.application.options.codeStyle.arrangement.ArrangementNodeDisplayManager; +import com.intellij.application.options.codeStyle.arrangement.node.match.ArrangementMatchConditionComponent; +import com.intellij.application.options.codeStyle.arrangement.node.match.ArrangementMatchNodeComponentFactory; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule; +import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware; +import com.intellij.ui.components.JBList; +import gnu.trove.TIntObjectHashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.util.Collections; +import java.util.List; + +/** + * @author Denis Zhdanov + * @since 10/31/12 1:23 PM + */ +public class ArrangementMatchingRulesList extends JBList { + + private static final Logger LOG = Logger.getInstance("#" + ArrangementMatchingRulesList.class.getName()); + + @NotNull private final TIntObjectHashMap myComponents + = new TIntObjectHashMap(); + + @NotNull private final DefaultListModel myModel = new DefaultListModel(); + + @NotNull private final ArrangementMatchNodeComponentFactory myFactory; + + public ArrangementMatchingRulesList(@NotNull ArrangementNodeDisplayManager displayManager, + @NotNull ArrangementColorsProvider colorsProvider, + @NotNull ArrangementStandardSettingsAware settingsFilter) + { + myFactory = new ArrangementMatchNodeComponentFactory(displayManager, colorsProvider, myModel); + setModel(myModel); + setCellRenderer(new MyListCellRenderer()); + } + + public void setRules(@Nullable List rules) { + myComponents.clear(); + myModel.clear(); + + if (rules == null) { + return; + } + + for (StdArrangementMatchRule rule : rules) { + myModel.addElement(rule); + } + + if (ArrangementConstants.LOG_RULE_MODIFICATION) { + LOG.info("Arrangement matching rules list is refreshed. Given rules:"); + for (StdArrangementMatchRule rule : rules) { + LOG.info(" " + rule.toString()); + } + } + } + + @NotNull + public List getRules() { + // TODO den implement + return Collections.emptyList(); + } + + // TODO den remove + @Override + public void paint(Graphics g) { + super.paint(g); + } + + private class MyListCellRenderer implements ListCellRenderer { + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + ArrangementMatchConditionComponent component = myComponents.get(index); + if (component == null) { + StdArrangementMatchRule rule = (StdArrangementMatchRule)value; + myComponents.put(index, component = myFactory.getComponent(rule.getMatcher().getCondition(), rule, true)); + } + return component.getUiComponent(); + } + } +} diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/newui/ArrangementMatchingRulesPanel.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/newui/ArrangementMatchingRulesPanel.java new file mode 100644 index 000000000000..863abaa1afa2 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/newui/ArrangementMatchingRulesPanel.java @@ -0,0 +1,64 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.application.options.codeStyle.arrangement.newui; + +import com.intellij.application.options.codeStyle.arrangement.ArrangementColorsProvider; +import com.intellij.application.options.codeStyle.arrangement.ArrangementNodeDisplayManager; +import com.intellij.openapi.application.ApplicationBundle; +import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule; +import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware; +import com.intellij.ui.IdeBorderFactory; +import com.intellij.ui.components.JBScrollPane; +import com.intellij.util.ui.GridBag; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.util.List; + +/** + * @author Denis Zhdanov + * @since 10/30/12 5:28 PM + */ +public class ArrangementMatchingRulesPanel extends JPanel { + + @NotNull private final ArrangementMatchingRulesList myList; + + public ArrangementMatchingRulesPanel(@NotNull ArrangementNodeDisplayManager displayManager, + @NotNull ArrangementColorsProvider colorsProvider, + @NotNull ArrangementStandardSettingsAware settingsFilter) + { + myList = new ArrangementMatchingRulesList(displayManager, colorsProvider, settingsFilter); + init(); + } + + private void init() { + setBorder(IdeBorderFactory.createTitledBorder(ApplicationBundle.message("arrangement.settings.section.match"))); + setLayout(new GridBagLayout()); + JBScrollPane scrollPane = new JBScrollPane(myList); + add(scrollPane, new GridBag().fillCell().weightx(1).weighty(1)); + } + + @NotNull + public List getRules() { + return myList.getRules(); + } + + public void setRules(@Nullable List rules) { + myList.setRules(rules); + } +} diff --git a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/ArrangementOperator.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/newui/ArrangementRepresentationAware.java similarity index 71% rename from platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/ArrangementOperator.java rename to platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/newui/ArrangementRepresentationAware.java index d5c32c41f1db..0b3de91f519a 100644 --- a/platform/lang-api/src/com/intellij/psi/codeStyle/arrangement/ArrangementOperator.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/newui/ArrangementRepresentationAware.java @@ -13,12 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.intellij.psi.codeStyle.arrangement; +package com.intellij.application.options.codeStyle.arrangement.newui; + +import org.jetbrains.annotations.NotNull; + +import javax.swing.*; /** * @author Denis Zhdanov - * @since 8/27/12 10:30 AM + * @since 10/30/12 6:57 PM */ -public enum ArrangementOperator { - AND, OR +public interface ArrangementRepresentationAware { + + @NotNull + JComponent getRenderer(); } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/newui/ArrangementSettingsPanel2.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/newui/ArrangementSettingsPanel2.java new file mode 100644 index 000000000000..d6634e7db7ab --- /dev/null +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/newui/ArrangementSettingsPanel2.java @@ -0,0 +1,132 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.application.options.codeStyle.arrangement.newui; + +import com.intellij.application.options.CodeStyleAbstractPanel; +import com.intellij.application.options.codeStyle.arrangement.ArrangementColorsProvider; +import com.intellij.application.options.codeStyle.arrangement.ArrangementColorsProviderImpl; +import com.intellij.application.options.codeStyle.arrangement.ArrangementNodeDisplayManager; +import com.intellij.lang.Language; +import com.intellij.openapi.application.ApplicationBundle; +import com.intellij.openapi.editor.colors.EditorColorsScheme; +import com.intellij.openapi.editor.highlighter.EditorHighlighter; +import com.intellij.openapi.util.Comparing; +import com.intellij.psi.codeStyle.CodeStyleSettings; +import com.intellij.psi.codeStyle.arrangement.Rearranger; +import com.intellij.psi.codeStyle.arrangement.StdArrangementSettings; +import com.intellij.psi.codeStyle.arrangement.settings.ArrangementColorsAware; +import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware; +import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsRepresentationAware; +import com.intellij.psi.codeStyle.arrangement.settings.DefaultArrangementSettingsRepresentationManager; +import com.intellij.util.ui.GridBag; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; + +/** + * @author Denis Zhdanov + * @since 10/30/12 5:17 PM + */ +public abstract class ArrangementSettingsPanel2 extends CodeStyleAbstractPanel { + + @NotNull private final JPanel myContent = new JPanel(new GridBagLayout()); + + @NotNull private final Language myLanguage; + @NotNull private final ArrangementStandardSettingsAware mySettingsAware; + @NotNull private final ArrangementMatchingRulesPanel myMatchingRulesPanel; + + public ArrangementSettingsPanel2(@NotNull CodeStyleSettings settings, @NotNull Language language) { + super(settings); + myLanguage = language; + Rearranger rearranger = Rearranger.EXTENSION.forLanguage(language); + + assert rearranger instanceof ArrangementStandardSettingsAware; + mySettingsAware = (ArrangementStandardSettingsAware)rearranger; + + final ArrangementColorsProvider colorsProvider; + if (rearranger instanceof ArrangementColorsAware) { + colorsProvider = new ArrangementColorsProviderImpl((ArrangementColorsAware)rearranger); + } + else { + colorsProvider = new ArrangementColorsProviderImpl(null); + } + + ArrangementStandardSettingsRepresentationAware representationManager = DefaultArrangementSettingsRepresentationManager.INSTANCE; + if (mySettingsAware instanceof ArrangementStandardSettingsRepresentationAware) { + representationManager = (ArrangementStandardSettingsRepresentationAware)mySettingsAware; + } + + final ArrangementNodeDisplayManager displayManager = new ArrangementNodeDisplayManager( + mySettingsAware, colorsProvider, representationManager + ); + + myMatchingRulesPanel = new ArrangementMatchingRulesPanel(displayManager, colorsProvider, mySettingsAware); + myContent.add(myMatchingRulesPanel, new GridBag().fillCell().weightx(1).weighty(1)); + } + + @Nullable + @Override + public JComponent getPanel() { + return myContent; + } + + @Nullable + @Override + protected EditorHighlighter createHighlighter(EditorColorsScheme scheme) { + // TODO den implement + return null; + } + + @SuppressWarnings("unchecked") + @Nullable + private StdArrangementSettings getSettings(@NotNull CodeStyleSettings settings) { + StdArrangementSettings result = (StdArrangementSettings)settings.getCommonSettings(myLanguage).getArrangementSettings(); + if (result == null) { + result = mySettingsAware.getDefaultSettings(); + } + return result; + } + + @Override + public void apply(CodeStyleSettings settings) { + // TODO den implement + } + + @Override + public boolean isModified(CodeStyleSettings settings) { + StdArrangementSettings s = new StdArrangementSettings(myMatchingRulesPanel.getRules()); + // TODO den add grouping rules support + return !Comparing.equal(getSettings(settings), s); + } + + @Override + protected void resetImpl(CodeStyleSettings settings) { + StdArrangementSettings s = getSettings(settings); + if (s == null) { + myMatchingRulesPanel.setRules(null); + } + else { + myMatchingRulesPanel.setRules(s.getRules()); + } + } + + @Override + protected String getTabTitle() { + return ApplicationBundle.message("arrangement.title.settings.tab"); + } +} diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/ArrangementCheckBoxNode.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/ArrangementCheckBoxNode.java index a12ad7f36fc3..30eaa0869efd 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/ArrangementCheckBoxNode.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/ArrangementCheckBoxNode.java @@ -16,7 +16,6 @@ package com.intellij.application.options.codeStyle.arrangement.node; import com.intellij.application.options.codeStyle.arrangement.ArrangementNodeDisplayManager; -import com.intellij.application.options.codeStyle.arrangement.ArrangementTreeNode; import com.intellij.ui.components.JBCheckBox; import com.intellij.util.Consumer; import com.intellij.util.ui.GridBag; @@ -33,8 +32,8 @@ import java.awt.event.ItemListener; * @author Denis Zhdanov * @since 9/27/12 3:10 PM */ -public class ArrangementCheckBoxNode extends ArrangementTreeNode - implements ArrangementRepresentationAwareNode, ArrangementEditableNode +public class ArrangementCheckBoxNode/* extends ArrangementTreeNode + implements ArrangementRepresentationAwareNode, ArrangementEditableNode*/ { @NotNull private final JPanel myRenderer = new JPanel(new GridBagLayout()); @NotNull private final JBCheckBox myCheckBox = new JBCheckBox(); @@ -44,7 +43,7 @@ public class ArrangementCheckBoxNode extends ArrangementTreeNode @Nullable private Consumer> myListener; public ArrangementCheckBoxNode(@NotNull ArrangementNodeDisplayManager displayManager, @NotNull T data) { - super(null); + //super(null); myCheckBox.setText(displayManager.getDisplayValue(data)); myData = data; myCheckBox.setBackground(UIUtil.getTreeBackground()); @@ -60,13 +59,13 @@ public class ArrangementCheckBoxNode extends ArrangementTreeNode } @NotNull - @Override + //@Override public JComponent getRenderer() { return myRenderer; } @NotNull - @Override + //@Override public JComponent getEditor() { return myRenderer; } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/ArrangementComboBoxNode.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/ArrangementComboBoxNode.java index 4b561e10c4a5..4f64c1ecb78c 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/ArrangementComboBoxNode.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/ArrangementComboBoxNode.java @@ -16,7 +16,6 @@ package com.intellij.application.options.codeStyle.arrangement.node; import com.intellij.application.options.codeStyle.arrangement.ArrangementNodeDisplayManager; -import com.intellij.application.options.codeStyle.arrangement.ArrangementTreeNode; import com.intellij.util.ui.GridBag; import org.jetbrains.annotations.NotNull; @@ -29,7 +28,7 @@ import java.util.List; * @author Denis Zhdanov * @since 9/27/12 5:01 PM */ -public class ArrangementComboBoxNode extends ArrangementTreeNode implements ArrangementRepresentationAwareNode, ArrangementEditableNode { +public class ArrangementComboBoxNode /*extends ArrangementTreeNode implements ArrangementRepresentationAwareNode, ArrangementEditableNode*/ { @NotNull private final JPanel myControl = new JPanel(new GridBagLayout()); @NotNull private final JLabel myLabel = new JLabel(); @@ -37,7 +36,7 @@ public class ArrangementComboBoxNode extends ArrangementTreeNode implements A @NotNull private final List myData = new ArrayList(); public ArrangementComboBoxNode(@NotNull ArrangementNodeDisplayManager displayManager, @NotNull String text, @NotNull Iterable data) { - super(null); + //super(null); for (T t : data) { myData.add(t); myComboBox.addItem(displayManager.getDisplayValue(t)); @@ -48,13 +47,13 @@ public class ArrangementComboBoxNode extends ArrangementTreeNode implements A } @NotNull - @Override + //@Override public JComponent getEditor() { return myControl; } @NotNull - @Override + //@Override public JComponent getRenderer() { return myControl; } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/ArrangementSectionNode.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/ArrangementSectionNode.java index 5f39b5f9eaf7..d3c04b953c63 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/ArrangementSectionNode.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/ArrangementSectionNode.java @@ -15,7 +15,6 @@ */ package com.intellij.application.options.codeStyle.arrangement.node; -import com.intellij.application.options.codeStyle.arrangement.ArrangementTreeNode; import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -24,13 +23,13 @@ import javax.swing.*; * @author Denis Zhdanov * @since 9/27/12 2:24 PM */ -public class ArrangementSectionNode extends ArrangementTreeNode implements ArrangementRepresentationAwareNode { +public class ArrangementSectionNode /*extends ArrangementTreeNode implements ArrangementRepresentationAwareNode*/ { @NotNull private final JPanel myRenderer = new JPanel(); @NotNull private final JLabel myTextLabel = new JLabel(); public ArrangementSectionNode(@NotNull String text) { - super(null); + //super(null); myTextLabel.setText(text); myRenderer.add(myTextLabel); } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementAndMatchNodeComponent.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementAndMatchConditionComponent.java similarity index 66% rename from platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementAndMatchNodeComponent.java rename to platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementAndMatchConditionComponent.java index 11d427ab2cf1..878d29feb3cf 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementAndMatchNodeComponent.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementAndMatchConditionComponent.java @@ -16,14 +16,14 @@ package com.intellij.application.options.codeStyle.arrangement.node.match; import com.intellij.application.options.codeStyle.arrangement.ArrangementConfigUtil; +import com.intellij.application.options.codeStyle.arrangement.ArrangementConstants; import com.intellij.application.options.codeStyle.arrangement.ArrangementNodeDisplayManager; -import com.intellij.application.options.codeStyle.arrangement.ArrangementRuleEditingModel; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule; import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition; import com.intellij.psi.codeStyle.arrangement.model.ArrangementCompositeMatchCondition; import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition; import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchConditionVisitor; -import com.intellij.ui.awt.RelativePoint; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -36,26 +36,24 @@ import java.util.List; import java.util.Map; /** - * {@link ArrangementMatchNodeComponent Component} for showing {@link ArrangementCompositeMatchCondition composite nodes}. + * {@link ArrangementMatchConditionComponent Component} for showing {@link ArrangementCompositeMatchCondition composite nodes}. *

* Not thread-safe. * * @author Denis Zhdanov * @since 8/8/12 10:51 AM */ -public class ArrangementAndMatchNodeComponent extends JPanel implements ArrangementMatchNodeComponent { +public class ArrangementAndMatchConditionComponent extends JPanel implements ArrangementMatchConditionComponent { - private static final int BUBBLE_CONNECTOR_LENGTH = 5; - - @NotNull private final List myComponents = new ArrayList(); + @NotNull private final List myComponents = new ArrayList(); @NotNull private final ArrangementCompositeMatchCondition mySetting; @Nullable private Rectangle myScreenBounds; - public ArrangementAndMatchNodeComponent(@NotNull ArrangementCompositeMatchCondition setting, - @NotNull ArrangementMatchNodeComponentFactory factory, - @NotNull ArrangementNodeDisplayManager manager, - @Nullable ArrangementRuleEditingModel model) + public ArrangementAndMatchConditionComponent(@NotNull StdArrangementMatchRule rule, + @NotNull ArrangementCompositeMatchCondition setting, + @NotNull ArrangementMatchNodeComponentFactory factory, + @NotNull ArrangementNodeDisplayManager manager) { mySetting = setting; setOpaque(false); @@ -81,13 +79,13 @@ public class ArrangementAndMatchNodeComponent extends JPanel implements Arrangem for (Object key : ordered) { ArrangementMatchCondition operand = operands.get(key); assert operand != null; - ArrangementMatchNodeComponent component = factory.getComponent(operand, model, false); + ArrangementMatchConditionComponent component = factory.getComponent(operand, rule, true); myComponents.add(component); JComponent uiComponent = component.getUiComponent(); Dimension size = uiComponent.getPreferredSize(); add(uiComponent); uiComponent.setBounds(x, 0, size.width, size.height); - x += size.width + BUBBLE_CONNECTOR_LENGTH; + x += size.width + ArrangementConstants.HORIZONTAL_GAP; } } @@ -121,30 +119,11 @@ public class ArrangementAndMatchNodeComponent extends JPanel implements Arrangem @Override public void setSelected(boolean selected) { - for (ArrangementMatchNodeComponent component : myComponents) { + for (ArrangementMatchConditionComponent component : myComponents) { component.setSelected(selected); } } - @Override - public ArrangementMatchNodeComponent getNodeComponentAt(@NotNull RelativePoint point) { - if (myScreenBounds == null) { - return null; - } - Point screenPoint = point.getScreenPoint(); - if (!myScreenBounds.contains(screenPoint)) { - return null; - } - - for (ArrangementMatchNodeComponent component : myComponents) { - Rectangle screenBounds = component.getScreenBounds(); - if (screenBounds != null && screenBounds.contains(screenPoint)) { - return component; - } - } - return null; - } - @Override public Dimension getMinimumSize() { return getPreferredSize(); @@ -166,7 +145,7 @@ public class ArrangementAndMatchNodeComponent extends JPanel implements Arrangem myHeight = Math.max(size.height, myHeight); } if (components.length > 1) { - myWidth += (components.length - 1) * BUBBLE_CONNECTOR_LENGTH; + myWidth += (components.length - 1) * ArrangementConstants.HORIZONTAL_GAP; } return new Dimension(myWidth, myHeight); } @@ -179,37 +158,12 @@ public class ArrangementAndMatchNodeComponent extends JPanel implements Arrangem myScreenBounds = new Rectangle(point.x, point.y, bounds.width, bounds.height); } super.paint(g); - - Component[] components = getComponents(); - if (components.length < 2) { - return; - } - - // Draw node connectors. - int x = 0; - g.setColor(UIManager.getColor("Tree.hash")); - for (int i = 0; i < components.length; i++) { - Component component = components[i]; - Rectangle bounds = component.getBounds(); - if (myScreenBounds != null && i < myComponents.size()) { - myComponents.get(i).setScreenBounds(new Rectangle( - myScreenBounds.x + bounds.x, myScreenBounds.y + bounds.y, bounds.width, bounds.height - )); - } - int y = bounds.y + bounds.height / 2; - x += bounds.width; - if (i < components.length - 1) { - // TODO den check - //g.drawLine(x, y, x + BUBBLE_CONNECTOR_LENGTH, y); - } - x += BUBBLE_CONNECTOR_LENGTH; - } } @Override public Rectangle handleMouseMove(@NotNull MouseEvent event) { Point location = event.getLocationOnScreen(); - for (ArrangementMatchNodeComponent component : myComponents) { + for (ArrangementMatchConditionComponent component : myComponents) { Rectangle bounds = component.getScreenBounds(); if (bounds != null && bounds.contains(location)) { return component.handleMouseMove(event); @@ -221,7 +175,7 @@ public class ArrangementAndMatchNodeComponent extends JPanel implements Arrangem @Override public void handleMouseClick(@NotNull MouseEvent event) { Point location = event.getLocationOnScreen(); - for (ArrangementMatchNodeComponent component : myComponents) { + for (ArrangementMatchConditionComponent component : myComponents) { Rectangle bounds = component.getScreenBounds(); if (bounds != null && bounds.contains(location)) { component.handleMouseClick(event); diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementAtomMatchNodeComponent.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementAtomMatchConditionComponent.java similarity index 82% rename from platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementAtomMatchNodeComponent.java rename to platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementAtomMatchConditionComponent.java index 9071f56e0941..85dfbcc7c701 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementAtomMatchNodeComponent.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementAtomMatchConditionComponent.java @@ -23,7 +23,7 @@ import com.intellij.ui.IdeBorderFactory; import com.intellij.ui.RoundedLineBorder; import com.intellij.ui.SimpleColoredComponent; import com.intellij.ui.SimpleTextAttributes; -import com.intellij.ui.awt.RelativePoint; +import com.intellij.util.Consumer; import com.intellij.util.ui.GridBag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -33,16 +33,16 @@ import java.awt.*; import java.awt.event.MouseEvent; /** - * {@link ArrangementMatchNodeComponent} for {@link ArrangementAtomMatchCondition} representation. + * {@link ArrangementMatchConditionComponent} for {@link ArrangementAtomMatchCondition} representation. *

* Not thread-safe. * * @author Denis Zhdanov * @since 8/8/12 10:06 AM */ -public class ArrangementAtomMatchNodeComponent implements ArrangementMatchNodeComponent { +public class ArrangementAtomMatchConditionComponent implements ArrangementMatchConditionComponent { - public static final int VERTICAL_PADDING = 4; + public static final int VERTICAL_PADDING = 4; @NotNull private final JPanel myRenderer = new JPanel(new GridBagLayout()) { @Override @@ -76,14 +76,14 @@ public class ArrangementAtomMatchNodeComponent implements ArrangementMatchNodeCo return myTextControlSize == null ? super.getPreferredSize() : myTextControlSize; } }; - - @NotNull private final String myText; + + @NotNull private final String myText; @NotNull private final ArrangementColorsProvider myColorsProvider; @NotNull private final RoundedLineBorder myBorder; @NotNull private final ArrangementAtomMatchCondition myCondition; - @Nullable private final ActionButton myCloseButton; - @Nullable private final Runnable myCloseCallback; + @Nullable private final ActionButton myCloseButton; + @Nullable private final Consumer myCloseCallback; @NotNull private Color myBackgroundColor; @@ -91,13 +91,12 @@ public class ArrangementAtomMatchNodeComponent implements ArrangementMatchNodeCo @Nullable private Rectangle myScreenBounds; private boolean myEnabled = true; - private boolean myInverted; private boolean myCloseButtonHovered; - public ArrangementAtomMatchNodeComponent(@NotNull ArrangementNodeDisplayManager manager, - @NotNull ArrangementColorsProvider colorsProvider, - @NotNull ArrangementAtomMatchCondition condition, - @Nullable Runnable closeCallback) + public ArrangementAtomMatchConditionComponent(@NotNull ArrangementNodeDisplayManager manager, + @NotNull ArrangementColorsProvider colorsProvider, + @NotNull ArrangementAtomMatchCondition condition, + @Nullable Consumer closeCallback) { myColorsProvider = colorsProvider; myCondition = condition; @@ -117,7 +116,7 @@ public class ArrangementAtomMatchNodeComponent implements ArrangementMatchNodeCo } }; - GridBagConstraints constraints = new GridBag().anchor(GridBagConstraints.CENTER).insets(0, 0, 0, 0); + GridBagConstraints constraints = new GridBag().anchor(GridBagConstraints.WEST).weightx(1).insets(0, 0, 0, 0); JPanel insetsPanel = new JPanel(new GridBagLayout()); insetsPanel.add(myTextControl, constraints); @@ -140,8 +139,8 @@ public class ArrangementAtomMatchNodeComponent implements ArrangementMatchNodeCo super.paint(g); } }; - roundBorderPanel.add(insetsPanel, new GridBag().fillCellHorizontally()); - roundBorderPanel.add(myCloseButton, new GridBag().anchor(GridBagConstraints.CENTER).insets(VERTICAL_PADDING, 0, 0, 0)); + roundBorderPanel.add(insetsPanel, new GridBag().fillCellHorizontally().anchor(GridBagConstraints.WEST)); + roundBorderPanel.add(myCloseButton, new GridBag().anchor(GridBagConstraints.CENTER).weightx(1).insets(VERTICAL_PADDING, 0, 0, 0)); myBorder = IdeBorderFactory.createRoundedBorder(arcSize); roundBorderPanel.setBorder(myBorder); roundBorderPanel.setOpaque(false); @@ -180,11 +179,6 @@ public class ArrangementAtomMatchNodeComponent implements ArrangementMatchNodeCo return false; } - @Override - public ArrangementMatchNodeComponent getNodeComponentAt(@NotNull RelativePoint point) { - return (myScreenBounds != null && myScreenBounds.contains(point.getScreenPoint())) ? this : null; - } - /** * Instructs current component that it should {@link #getUiComponent() draw} itself according to the given 'selected' state. * @@ -212,17 +206,6 @@ public class ArrangementAtomMatchNodeComponent implements ArrangementMatchNodeCo myEnabled = enabled; } - /** - * Instructs current component that it should {@link #getUiComponent() draw} itself according to the given 'inverted' state. - *

- * For example, target rule might look like 'public' and inverting it produces 'not public'. - * - * @param inverted flag that indicates if current component should be drawn as 'inverted' - */ - public void setInverted(boolean inverted) { - myInverted = inverted; - } - @Nullable @Override public Rectangle handleMouseMove(@NotNull MouseEvent event) { @@ -237,8 +220,8 @@ public class ArrangementAtomMatchNodeComponent implements ArrangementMatchNodeCo @Override public void handleMouseClick(@NotNull MouseEvent event) { Rectangle buttonBounds = getCloseButtonScreenLocation(); - if (buttonBounds != null && buttonBounds.contains(event.getLocationOnScreen()) && myCloseCallback != null) { - myCloseCallback.run(); + if (buttonBounds != null && myCloseCallback != null && buttonBounds.contains(event.getLocationOnScreen())) { + myCloseCallback.consume(getMatchCondition()); } } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementEditIconMatchNodeComponent.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementEditIconMatchConditionComponent.java similarity index 89% rename from platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementEditIconMatchNodeComponent.java rename to platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementEditIconMatchConditionComponent.java index e7ca0abc5b89..80d7aecb7c21 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementEditIconMatchNodeComponent.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementEditIconMatchConditionComponent.java @@ -22,7 +22,6 @@ import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.actionSystem.impl.ActionButton; import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition; -import com.intellij.ui.awt.RelativePoint; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -36,14 +35,14 @@ import java.awt.event.MouseEvent; * @author Denis Zhdanov * @since 10/29/12 11:53 AM */ -public class ArrangementEditIconMatchNodeComponent extends JPanel implements ArrangementMatchNodeComponent { +public class ArrangementEditIconMatchConditionComponent extends JPanel implements ArrangementMatchConditionComponent { - @NotNull private final ArrangementMatchNodeComponent myDelegate; - @NotNull private final Icon myActiveEditIcon; - @NotNull private final Icon myInactiveEditIcon; - @NotNull private final ActionButton myEditButton; + @NotNull private final ArrangementMatchConditionComponent myDelegate; + @NotNull private final Icon myActiveEditIcon; + @NotNull private final Icon myInactiveEditIcon; + @NotNull private final ActionButton myEditButton; - public ArrangementEditIconMatchNodeComponent(@NotNull ArrangementMatchNodeComponent delegate) { + public ArrangementEditIconMatchConditionComponent(@NotNull ArrangementMatchConditionComponent delegate) { myDelegate = delegate; setLayout(null); @@ -93,12 +92,6 @@ public class ArrangementEditIconMatchNodeComponent extends JPanel implements Arr return this; } - @Override - @Nullable - public ArrangementMatchNodeComponent getNodeComponentAt(@NotNull RelativePoint point) { - return myDelegate.getNodeComponentAt(point); - } - @Override @Nullable public Rectangle getScreenBounds() { diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementGroupingMatchNodeComponent.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementGroupingMatchConditionComponent.java similarity index 86% rename from platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementGroupingMatchNodeComponent.java rename to platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementGroupingMatchConditionComponent.java index ecef8af22361..5e8149bc4562 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementGroupingMatchNodeComponent.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementGroupingMatchConditionComponent.java @@ -20,7 +20,6 @@ import com.intellij.application.options.codeStyle.arrangement.ArrangementNodeDis import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition; import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition; -import com.intellij.ui.awt.RelativePoint; import com.intellij.util.ui.GridBag; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; @@ -34,7 +33,7 @@ import java.awt.event.MouseEvent; * @author Denis Zhdanov * @since 9/12/12 5:39 PM */ -public class ArrangementGroupingMatchNodeComponent extends JPanel implements ArrangementMatchNodeComponent { +public class ArrangementGroupingMatchConditionComponent extends JPanel implements ArrangementMatchConditionComponent { private static final int TOP_INSET = 3; @@ -43,8 +42,8 @@ public class ArrangementGroupingMatchNodeComponent extends JPanel implements Arr @Nullable private Rectangle myScreenBounds; @NotNull private Dimension myPreferredSize; - public ArrangementGroupingMatchNodeComponent(@NotNull ArrangementNodeDisplayManager manager, - @NotNull ArrangementAtomMatchCondition condition) + public ArrangementGroupingMatchConditionComponent(@NotNull ArrangementNodeDisplayManager manager, + @NotNull ArrangementAtomMatchCondition condition) { myCondition = condition; String text = StringUtil.capitalize(StringUtil.pluralize(manager.getDisplayValue(myCondition.getValue()))); @@ -66,12 +65,6 @@ public class ArrangementGroupingMatchNodeComponent extends JPanel implements Arr return this; } - @Nullable - @Override - public ArrangementMatchNodeComponent getNodeComponentAt(@NotNull RelativePoint point) { - return (myScreenBounds != null && myScreenBounds.contains(point.getScreenPoint())) ? this : null; - } - @Nullable @Override public Rectangle getScreenBounds() { diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementMatchNodeComponent.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementMatchConditionComponent.java similarity index 86% rename from platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementMatchNodeComponent.java rename to platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementMatchConditionComponent.java index 54efda0f64ad..3ec53a667031 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementMatchNodeComponent.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementMatchConditionComponent.java @@ -16,7 +16,6 @@ package com.intellij.application.options.codeStyle.arrangement.node.match; import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition; -import com.intellij.ui.awt.RelativePoint; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -25,25 +24,25 @@ import java.awt.*; import java.awt.event.MouseEvent; /** - * // TODO den add doc + * Component which manages {@link ArrangementMatchCondition match condition} UI representation. * * @author Denis Zhdanov * @since 8/10/12 11:22 AM */ -public interface ArrangementMatchNodeComponent { +public interface ArrangementMatchConditionComponent { @NotNull ArrangementMatchCondition getMatchCondition(); - - // TODO den add doc + + /** + * @return UI component for the {@link #getMatchCondition() target match condition} + */ @NotNull JComponent getUiComponent(); - // TODO den add doc - @Nullable - ArrangementMatchNodeComponent getNodeComponentAt(@NotNull RelativePoint point); - - // TODO den add doc + /** + * @return screen bounds for the {@link #getUiComponent() target UI component} (if known) + */ @Nullable Rectangle getScreenBounds(); @@ -52,7 +51,7 @@ public interface ArrangementMatchNodeComponent { /** * Notifies current component that canvas (container where current component is painted) width has been changed. *

- * The intended usage is to allow component to draw something up to/at the right screen size. + * The intended usage is to allow component to draw something up to/at the right edge. * * @param width new canvas width * @return true if current component's representation has been changed; diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementMatchNodeComponentFactory.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementMatchNodeComponentFactory.java index 3c4bc8016f9a..a5737ac71622 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementMatchNodeComponentFactory.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/match/ArrangementMatchNodeComponentFactory.java @@ -17,17 +17,17 @@ package com.intellij.application.options.codeStyle.arrangement.node.match; import com.intellij.application.options.codeStyle.arrangement.ArrangementColorsProvider; import com.intellij.application.options.codeStyle.arrangement.ArrangementNodeDisplayManager; -import com.intellij.application.options.codeStyle.arrangement.ArrangementRuleEditingModel; import com.intellij.openapi.util.Ref; +import com.intellij.psi.codeStyle.arrangement.match.StdArrangementEntryMatcher; +import com.intellij.psi.codeStyle.arrangement.match.StdArrangementMatchRule; import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchCondition; import com.intellij.psi.codeStyle.arrangement.model.ArrangementCompositeMatchCondition; import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition; import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchConditionVisitor; -import com.intellij.util.containers.ContainerUtilRt; +import com.intellij.util.Consumer; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import java.util.List; +import javax.swing.*; import java.util.Set; /** @@ -36,78 +36,90 @@ import java.util.Set; */ public class ArrangementMatchNodeComponentFactory { - @NotNull private final Set myGroupingConditions = ContainerUtilRt.newHashSet(); @NotNull private final ArrangementNodeDisplayManager myDisplayManager; @NotNull private final ArrangementColorsProvider myColorsProvider; - @Nullable private final Runnable myRemoveConditionCallback; + @NotNull private final DefaultListModel myListModel; public ArrangementMatchNodeComponentFactory(@NotNull ArrangementNodeDisplayManager manager, @NotNull ArrangementColorsProvider provider, - @Nullable Runnable removeConditionCallback, - @NotNull List> groupingRules) + @NotNull final DefaultListModel model) { myDisplayManager = manager; myColorsProvider = provider; - myRemoveConditionCallback = removeConditionCallback; - for (Set rules : groupingRules) { - myGroupingConditions.addAll(rules); + myListModel = model; + } + + private static void removeCondition(@NotNull DefaultListModel model, + @NotNull StdArrangementMatchRule rule, + @NotNull ArrangementAtomMatchCondition condition) + { + int i = model.indexOf(rule); + if (i < 0) { + return; + } + + ArrangementMatchCondition existingCondition = rule.getMatcher().getCondition(); + if (existingCondition.equals(condition)) { + model.remove(i); + return; + } + + assert existingCondition instanceof ArrangementCompositeMatchCondition; + Set operands = ((ArrangementCompositeMatchCondition)existingCondition).getOperands(); + operands.remove(condition); + + if (operands.isEmpty()) { + model.remove(i); + } + else if (operands.size() == 1) { + model.set(i, new StdArrangementMatchRule(new StdArrangementEntryMatcher(operands.iterator().next()), rule.getOrderType())); } } + /** + * Allows to build UI component for the given model. + * + * @param rendererTarget target model element for which UI component should be built + * @param rule rule which contains given 'renderer target' condition and serves as + * a data entry for the target list model + * @param allowModification flag which indicates whether given model can be changed at future + * @return renderer for the given model + */ @NotNull - public ArrangementMatchNodeComponent getComponent(@NotNull final ArrangementMatchCondition node, - @Nullable final ArrangementRuleEditingModel model, - boolean showEditIcon) + public ArrangementMatchConditionComponent getComponent(@NotNull final ArrangementMatchCondition rendererTarget, + @NotNull final StdArrangementMatchRule rule, + final boolean allowModification) { - final Ref ref = new Ref(); - node.invite(new ArrangementMatchConditionVisitor() { + final Ref ref = new Ref(); + rendererTarget.invite(new ArrangementMatchConditionVisitor() { @Override public void visit(@NotNull ArrangementAtomMatchCondition condition) { - ArrangementMatchNodeComponent component; - if (myGroupingConditions.contains(condition)) { - component = new ArrangementGroupingMatchNodeComponent(myDisplayManager, condition); - } - else { - component = new ArrangementAtomMatchNodeComponent( - myDisplayManager, myColorsProvider, condition, prepareRemoveCallback(condition, model) - ); - } + RemoveAtomConditionCallback callback = allowModification ? new RemoveAtomConditionCallback(rule) : null; + ArrangementMatchConditionComponent component = new ArrangementAtomMatchConditionComponent( + myDisplayManager, myColorsProvider, condition, callback + ); ref.set(component); } @Override public void visit(@NotNull ArrangementCompositeMatchCondition condition) { - switch (condition.getOperator()) { - case AND: - ref.set(new ArrangementAndMatchNodeComponent(condition, ArrangementMatchNodeComponentFactory.this, myDisplayManager, model)); - break; - case OR: // TODO den implement - } + ref.set(new ArrangementAndMatchConditionComponent(rule, condition, ArrangementMatchNodeComponentFactory.this, myDisplayManager)); } }); - if (showEditIcon) { - return new ArrangementEditIconMatchNodeComponent(ref.get()); - } - else { - return ref.get(); - } + return ref.get(); } - @Nullable - private Runnable prepareRemoveCallback(@NotNull final ArrangementMatchCondition condition, - @Nullable final ArrangementRuleEditingModel model) - { - if (model == null) { - return null; + private class RemoveAtomConditionCallback implements Consumer { + + @NotNull private final StdArrangementMatchRule myRule; + + RemoveAtomConditionCallback(@NotNull StdArrangementMatchRule rule) { + myRule = rule; + } + + @Override + public void consume(ArrangementAtomMatchCondition condition) { + removeCondition(myListModel, myRule, condition); } - return new Runnable() { - @Override - public void run() { - model.removeAndCondition(condition); - if (myRemoveConditionCallback != null) { - myRemoveConditionCallback.run(); - } - } - }; } } diff --git a/platform/testFramework/testSrc/com/intellij/psi/codeStyle/arrangement/AbstractRearrangerTest.groovy b/platform/testFramework/testSrc/com/intellij/psi/codeStyle/arrangement/AbstractRearrangerTest.groovy index b7b3d2c124d1..33e4f360281a 100644 --- a/platform/testFramework/testSrc/com/intellij/psi/codeStyle/arrangement/AbstractRearrangerTest.groovy +++ b/platform/testFramework/testSrc/com/intellij/psi/codeStyle/arrangement/AbstractRearrangerTest.groovy @@ -75,7 +75,7 @@ abstract class AbstractRearrangerTest extends LightPlatformCodeInsightFixtureTes condition = atom(conditions[0]) } else { - condition = ArrangementUtil.and(conditions.collect { atom(it) } as ArrangementMatchCondition[]) + condition = ArrangementUtil.combine(conditions.collect { atom(it) } as ArrangementMatchCondition[]) } new StdArrangementMatchRule(new StdArrangementEntryMatcher(condition))