mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-19061 Integrate the Rearranger-plugin into core-IDE
Add ability to add new arrangement rules
This commit is contained in:
+4
-1
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
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.ArrangementEntryType;
|
||||
import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier;
|
||||
@@ -22,6 +23,7 @@ import com.intellij.psi.codeStyle.arrangement.model.ArrangementAtomMatchConditio
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingType;
|
||||
import com.intellij.ui.treeStructure.Tree;
|
||||
import gnu.trove.TIntIntHashMap;
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.Before;
|
||||
@@ -55,7 +57,8 @@ public abstract class AbstractArrangementRuleEditingModelTest {
|
||||
}
|
||||
|
||||
protected void configure(@NotNull ArrangementMatchCondition matchCondition) {
|
||||
myBuilder.build(matchCondition, myTree, myRoot, myGrouper, myRowMappings);
|
||||
Pair<ArrangementRuleEditingModelImpl,TIntIntHashMap> pair = myBuilder.build(matchCondition, myTree, myRoot, null, myGrouper);
|
||||
myRowMappings.put(pair.first.getRow(), pair.first);
|
||||
}
|
||||
|
||||
protected static ArrangementAtomMatchCondition atom(@NotNull Object condition) {
|
||||
|
||||
+48
@@ -29,6 +29,7 @@ import static org.junit.Assert.assertEquals
|
||||
* @author Denis Zhdanov
|
||||
* @since 8/17/12 1:12 PM
|
||||
*/
|
||||
@SuppressWarnings(["GroovyAssignabilityCheck", "GroovyVariableNotAssigned"])
|
||||
class ArrangementConfigUtilTest {
|
||||
|
||||
@Test
|
||||
@@ -412,6 +413,53 @@ to = '4'()}
|
||||
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)
|
||||
}
|
||||
|
||||
+5
-8
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
package com.intellij.application.options.codeStyle.arrangement;
|
||||
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition;
|
||||
import org.junit.Test;
|
||||
|
||||
import static com.intellij.psi.codeStyle.arrangement.ArrangementUtil.and;
|
||||
@@ -31,21 +30,19 @@ public class ArrangementRuleEditingModelBuilderTest extends AbstractArrangementR
|
||||
|
||||
@Test
|
||||
public void mapToTheSameLayer() {
|
||||
ArrangementMatchCondition matchCondition = and(atom(PUBLIC), atom(STATIC));
|
||||
myBuilder.build(matchCondition, myTree, myRoot, myGrouper, myRowMappings);
|
||||
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(matchCondition, myRoot.getFirstChild().getBackingCondition());
|
||||
assertEquals(and(atom(PUBLIC), atom(STATIC)), myRoot.getFirstChild().getBackingCondition());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void splitIntoTwoLayers() {
|
||||
ArrangementMatchCondition matchCondition = and(atom(FIELD), atom(PUBLIC), atom(STATIC));
|
||||
myBuilder.build(matchCondition, myTree, myRoot, myGrouper, myRowMappings);
|
||||
configure(and(atom(FIELD), atom(PUBLIC), atom(STATIC)));
|
||||
|
||||
checkRows(2);
|
||||
|
||||
@@ -60,8 +57,8 @@ public class ArrangementRuleEditingModelBuilderTest extends AbstractArrangementR
|
||||
|
||||
@Test
|
||||
public void addToExistingLayer() {
|
||||
myBuilder.build(and(atom(PUBLIC), atom(STATIC), atom(FIELD)), myTree, myRoot, myGrouper, myRowMappings);
|
||||
myBuilder.build(and(atom(PRIVATE), atom(FIELD)), myTree, myRoot, myGrouper, myRowMappings);
|
||||
configure(and(atom(PUBLIC), atom(STATIC), atom(FIELD)));
|
||||
configure(and(atom(PRIVATE), atom(FIELD)));
|
||||
|
||||
checkRows(2, 3);
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ public class ArrangementUtil {
|
||||
for (ArrangementMatchCondition node : nodes) {
|
||||
node.invite(visitor);
|
||||
}
|
||||
return result;
|
||||
return result.getOperands().size() == 1 ? result.getOperands().iterator().next() : result;
|
||||
}
|
||||
|
||||
// TODO den remove
|
||||
|
||||
+71
-59
@@ -135,6 +135,7 @@ public class ArrangementConfigUtil {
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@NotNull
|
||||
public static ArrangementTreeNode getLastBefore(@NotNull ArrangementTreeNode start, @NotNull ArrangementTreeNode stop)
|
||||
throws IllegalArgumentException
|
||||
@@ -223,7 +224,7 @@ public class ArrangementConfigUtil {
|
||||
* @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'}
|
||||
* @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,
|
||||
@@ -244,18 +245,6 @@ public class ArrangementConfigUtil {
|
||||
@NotNull DefaultTreeModel treeModel,
|
||||
boolean rootVisible)
|
||||
{
|
||||
markRows(from, rootVisible);
|
||||
if (from == to) {
|
||||
ArrangementTreeNode parent = from.getParent();
|
||||
assert parent != null;
|
||||
int index = parent.getIndex(from);
|
||||
treeModel.removeNodeFromParent(from);
|
||||
if (replacement != null) {
|
||||
insert(parent, index, replacement, treeModel);
|
||||
}
|
||||
return collectRowChangesAndUnmark(parent, 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
|
||||
@@ -311,12 +300,49 @@ public class ArrangementConfigUtil {
|
||||
//
|
||||
// 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;
|
||||
|
||||
//region Cut bottom sub-hierarchy
|
||||
@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; <code>null</code> otherwise
|
||||
*/
|
||||
@Nullable
|
||||
public static ArrangementTreeNode cutSubHierarchy(@NotNull ArrangementTreeNode topMost,
|
||||
@NotNull DefaultTreeModel treeModel,
|
||||
@NotNull ArrangementTreeNode bottomMost)
|
||||
{
|
||||
ArrangementTreeNode cutHierarchy = null;
|
||||
for (ArrangementTreeNode current = to; current != root; current = current.getParent()) {
|
||||
for (ArrangementTreeNode current = bottomMost; current != null && current != topMost; current = current.getParent()) {
|
||||
ArrangementTreeNode parent = current.getParent();
|
||||
assert parent != null;
|
||||
int i = parent.getIndex(current);
|
||||
@@ -338,43 +364,7 @@ public class ArrangementConfigUtil {
|
||||
}
|
||||
cutHierarchy = parentCopy;
|
||||
}
|
||||
//endregion
|
||||
|
||||
int childCountBefore = root.getChildCount();
|
||||
|
||||
//region Remove target sub-hierarchy
|
||||
for (ArrangementTreeNode current = to; current != root;) {
|
||||
ArrangementTreeNode parent = current.getParent();
|
||||
treeModel.removeNodeFromParent(current);
|
||||
current = parent;
|
||||
if (parent == null || parent.getChildCount() > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region Insert nodes.
|
||||
int insertionIndex = root.getChildCount() < childCountBefore ? childCountBefore - 1 : childCountBefore;
|
||||
if (replacement != null) {
|
||||
insert(root, insertionIndex, replacement, treeModel);
|
||||
}
|
||||
if (cutHierarchy != null) {
|
||||
List<ArrangementTreeNode> toInsert = new ArrayList<ArrangementTreeNode>();
|
||||
if (hasEqualSetting(root, cutHierarchy)) {
|
||||
for (int i = 0; i < cutHierarchy.getChildCount(); i++) {
|
||||
toInsert.add(cutHierarchy.getChildAt(i));
|
||||
}
|
||||
}
|
||||
else {
|
||||
toInsert.add(cutHierarchy);
|
||||
}
|
||||
for (ArrangementTreeNode node : toInsert) {
|
||||
insert(root, root.getChildCount(), node, treeModel);
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
return collectRowChangesAndUnmark(root, rootVisible);
|
||||
return cutHierarchy;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -383,7 +373,7 @@ public class ArrangementConfigUtil {
|
||||
* @param node reference to the target hierarchy
|
||||
* @param rootVisible determines if the root should be count during rows calculations
|
||||
*/
|
||||
private static void markRows(@NotNull ArrangementTreeNode node, boolean rootVisible) {
|
||||
public static void markRows(@NotNull ArrangementTreeNode node, boolean rootVisible) {
|
||||
ArrangementTreeNode root = getRoot(node);
|
||||
int row = rootVisible ? 0 : -1;
|
||||
Stack<ArrangementTreeNode> nodes = new Stack<ArrangementTreeNode>();
|
||||
@@ -413,10 +403,10 @@ public class ArrangementConfigUtil {
|
||||
* 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'}
|
||||
* @return collection of row changes at the form {@code 'old row -> new row'} (all rows are zero-based)
|
||||
*/
|
||||
@NotNull
|
||||
private static TIntIntHashMap collectRowChangesAndUnmark(@NotNull ArrangementTreeNode node, boolean rootVisible) {
|
||||
public static TIntIntHashMap collectRowChangesAndUnmark(@NotNull ArrangementTreeNode node, boolean rootVisible) {
|
||||
@NotNull TIntIntHashMap changes = new TIntIntHashMap();
|
||||
ArrangementTreeNode root = getRoot(node);
|
||||
int row = rootVisible ? 0 : -1;
|
||||
@@ -467,7 +457,7 @@ public class ArrangementConfigUtil {
|
||||
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.
|
||||
* <p/>
|
||||
@@ -493,14 +483,36 @@ public class ArrangementConfigUtil {
|
||||
* | | |_5 | | | |_6 |
|
||||
* </pre>
|
||||
* <p/>
|
||||
*
|
||||
*
|
||||
* @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 <code>true</code> if given child node has been merged to the existing node; <code>false</code> otherwise
|
||||
*/
|
||||
public static boolean insert(@NotNull final ArrangementTreeNode parent,
|
||||
public static void insert(@NotNull final ArrangementTreeNode parent,
|
||||
final int index,
|
||||
@NotNull final ArrangementTreeNode child,
|
||||
@NotNull DefaultTreeModel treeModel)
|
||||
{
|
||||
ArrangementTreeNode root = getRoot(parent);
|
||||
List<ArrangementTreeNode> toInsert = new ArrayList<ArrangementTreeNode>();
|
||||
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)
|
||||
@@ -562,7 +574,7 @@ public class ArrangementConfigUtil {
|
||||
* @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'}
|
||||
* @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,
|
||||
|
||||
+8
-2
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.application.options.codeStyle.arrangement;
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataKey;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
|
||||
/**
|
||||
@@ -24,11 +25,16 @@ import org.jetbrains.annotations.NonNls;
|
||||
public class ArrangementConstants {
|
||||
|
||||
@NonNls public static final String ACTION_GROUP_RULE_EDITOR_CONTEXT_MENU = "Arrangement.RuleEditor.Context.Menu";
|
||||
@NonNls public static final String RULE_EDITOR_PLACE = "Arrangement.RuleEditor.Place";
|
||||
@NonNls public static final String RULE_TREE_PLACE = "Arrangement.RuleTree.Place";
|
||||
@NonNls public static final String ACTION_GROUP_RULE_EDITOR_TOOL_WINDOW = "Arrangement.RuleEditor.ToolWIndow";
|
||||
|
||||
@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 boolean LOG_RULE_MODIFICATION = Boolean.parseBoolean(System.getProperty("log.arrangement.rule.modification"));
|
||||
|
||||
public static final DataKey<Runnable> NEW_RULE_FUNCTION_KEY = DataKey.create("Arrangement.Rule.Function.New");
|
||||
|
||||
private ArrangementConstants() {
|
||||
}
|
||||
}
|
||||
|
||||
+33
-45
@@ -21,13 +21,13 @@ import com.intellij.psi.codeStyle.arrangement.model.ArrangementMatchCondition;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.HierarchicalArrangementConditionNode;
|
||||
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementSettingsGrouper;
|
||||
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsRepresentationAware;
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
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 javax.swing.tree.TreeNode;
|
||||
|
||||
/**
|
||||
* Holds glue logic between arrangement settings and their representation -
|
||||
@@ -58,45 +58,41 @@ public class ArrangementRuleEditingModelBuilder {
|
||||
* </ol>
|
||||
* </pre>
|
||||
*
|
||||
* @param matchCondition target settings 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 grouper strategy that knows how to
|
||||
* {@link ArrangementStandardSettingsRepresentationAware#getDisplayValue(ArrangementModifier) group} setting
|
||||
* nodes for UI representation
|
||||
* @param rowMappings container to hold built {@link ArrangementRuleEditingModel editing models} (UI tree row numbers are used as keys)
|
||||
* @param matchCondition target settings 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;
|
||||
* <code>null</code> as an indication that new condition nodes should be inserted as the last 'root' child
|
||||
* @param grouper strategy that knows how to
|
||||
* {@link ArrangementStandardSettingsRepresentationAware#getDisplayValue(ArrangementModifier) group} setting
|
||||
* nodes for UI representation
|
||||
* @return collection of row changes at the form {@code 'old row -> new row'} (all rows are zero-based)
|
||||
*/
|
||||
@SuppressWarnings("MethodMayBeStatic")
|
||||
public void build(@NotNull ArrangementMatchCondition matchCondition,
|
||||
@NotNull JTree tree,
|
||||
@NotNull ArrangementTreeNode root,
|
||||
@NotNull ArrangementSettingsGrouper grouper,
|
||||
@NotNull TIntObjectHashMap<ArrangementRuleEditingModelImpl> rowMappings)
|
||||
public Pair<ArrangementRuleEditingModelImpl, TIntIntHashMap> build(
|
||||
@NotNull ArrangementMatchCondition matchCondition,
|
||||
@NotNull JTree tree,
|
||||
@NotNull ArrangementTreeNode root,
|
||||
@Nullable ArrangementTreeNode anchor,
|
||||
@NotNull ArrangementSettingsGrouper grouper)
|
||||
{
|
||||
int initialInsertRow = 0;
|
||||
|
||||
// Count rows before the settings root.
|
||||
for (TreeNode n = root.getParent(); n != null; n = n.getParent()) {
|
||||
for (int i = n.getChildCount() - 1; i >= 0; i--) {
|
||||
TreeNode child = n.getChildAt(i);
|
||||
if (child != root) {
|
||||
initialInsertRow += calculateRowsCount(child);
|
||||
}
|
||||
}
|
||||
initialInsertRow++;
|
||||
}
|
||||
|
||||
// Count root width.
|
||||
initialInsertRow += calculateRowsCount(root);
|
||||
if (!tree.isRootVisible()) {
|
||||
initialInsertRow--;
|
||||
}
|
||||
|
||||
HierarchicalArrangementConditionNode grouped = grouper.group(matchCondition);
|
||||
DefaultTreeModel treeModel = (DefaultTreeModel)tree.getModel();
|
||||
Pair<ArrangementTreeNode, Integer> pair = ArrangementConfigUtil.map(root, grouped, treeModel);
|
||||
ArrangementTreeNode topMostNode = ArrangementConfigUtil.getLastBefore(pair.first, root);
|
||||
int row = initialInsertRow + pair.second - 1;
|
||||
Pair<ArrangementTreeNode, Integer> pair = ArrangementConfigUtil.map(null, grouped, null);
|
||||
ArrangementTreeNode topMostNode = ArrangementConfigUtil.getRoot(pair.first);
|
||||
ArrangementConfigUtil.markRows(root, tree.isRootVisible());
|
||||
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, tree.isRootVisible());
|
||||
topMostNode = ArrangementConfigUtil.getLastBefore(pair.first, root);
|
||||
int row = ArrangementConfigUtil.getRow(pair.first, tree.isRootVisible());
|
||||
ArrangementRuleEditingModelImpl model = new ArrangementRuleEditingModelImpl(
|
||||
treeModel,
|
||||
matchCondition,
|
||||
@@ -106,14 +102,6 @@ public class ArrangementRuleEditingModelBuilder {
|
||||
row,
|
||||
tree.isRootVisible()
|
||||
);
|
||||
rowMappings.put(row, model);
|
||||
}
|
||||
|
||||
private static int calculateRowsCount(@NotNull TreeNode node) {
|
||||
int result = 1;
|
||||
for (int i = node.getChildCount() - 1; i >= 0; i--) {
|
||||
result += calculateRowsCount(node.getChildAt(i));
|
||||
}
|
||||
return result;
|
||||
return Pair.create(model, rowChanges);
|
||||
}
|
||||
}
|
||||
|
||||
+109
-27
@@ -16,14 +16,18 @@
|
||||
package com.intellij.application.options.codeStyle.arrangement;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
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.*;
|
||||
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.ArrangementSettingType;
|
||||
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementSettingsGrouper;
|
||||
import com.intellij.ui.awt.RelativePoint;
|
||||
import com.intellij.ui.treeStructure.Tree;
|
||||
import com.intellij.util.Alarm;
|
||||
import com.intellij.util.Consumer;
|
||||
import gnu.trove.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -47,13 +51,17 @@ import java.util.List;
|
||||
*/
|
||||
public class ArrangementRuleTree {
|
||||
|
||||
@NotNull private static final JLabel EMPTY_RENDERER = new JLabel("");
|
||||
private static final Logger LOG = Logger.getInstance("#" + ArrangementRuleTree.class.getName());
|
||||
@NotNull private static final JLabel EMPTY_RENDERER = new JLabel("");
|
||||
@NotNull private static final JLabel NEW_CONDITION_RENDERER = new JLabel("<html><b><empty condition></b>");
|
||||
@NotNull private static final Logger LOG = Logger.getInstance("#" + ArrangementRuleTree.class.getName());
|
||||
|
||||
@NotNull private final List<ArrangementRuleSelectionListener> myListeners = new ArrayList<ArrangementRuleSelectionListener>();
|
||||
@NotNull private final TreeSelectionModel 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 TIntObjectHashMap<ArrangementNodeComponent> myRenderers =
|
||||
new TIntObjectHashMap<ArrangementNodeComponent>();
|
||||
@@ -64,11 +72,13 @@ public class ArrangementRuleTree {
|
||||
@NotNull private final DefaultTreeModel myTreeModel;
|
||||
@NotNull private final Tree myTree;
|
||||
@NotNull private final ArrangementNodeComponentFactory myFactory;
|
||||
@NotNull private final ArrangementSettingsGrouper myGrouper;
|
||||
|
||||
private boolean myExplicitSelectionChange;
|
||||
private boolean mySkipSelectionChange;
|
||||
|
||||
public ArrangementRuleTree(@NotNull ArrangementSettingsGrouper grouper, @NotNull ArrangementNodeDisplayManager displayManager) {
|
||||
myGrouper = grouper;
|
||||
myFactory = new ArrangementNodeComponentFactory(displayManager, new Consumer<ArrangementAtomMatchCondition>() {
|
||||
@Override
|
||||
public void consume(@NotNull ArrangementAtomMatchCondition setting) {
|
||||
@@ -78,6 +88,7 @@ public class ArrangementRuleTree {
|
||||
myRoot = new ArrangementTreeNode(null);
|
||||
myTreeModel = new DefaultTreeModel(myRoot);
|
||||
myTree = new Tree(myTreeModel) {
|
||||
|
||||
@Override
|
||||
protected void setExpandedState(TreePath path, boolean state) {
|
||||
// Don't allow node collapse
|
||||
@@ -86,16 +97,34 @@ public class ArrangementRuleTree {
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected boolean isAlwaysPaintRowBackground() {
|
||||
return false;
|
||||
protected Condition<Integer> getWideSelectionBackgroundCondition() {
|
||||
return new Condition<Integer>() {
|
||||
@Override
|
||||
public boolean value(Integer row) {
|
||||
TreePath path = getPathForRow(row);
|
||||
if (path == null) {
|
||||
return false;
|
||||
}
|
||||
return isEmptyCondition(((ArrangementTreeNode)path.getLastPathComponent()).getBackingCondition());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@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.
|
||||
mySkipSelectionChange = e.getClickCount() > 0 && getNodeComponentAt(e.getLocationOnScreen()) == null;
|
||||
if (e.getClickCount() > 0 && getNodeComponentAt(e.getLocationOnScreen()) == null) {
|
||||
TreePath path = myTree.getPathForLocation(e.getX(), e.getY());
|
||||
if (path == null) {
|
||||
mySkipSelectionChange = true;
|
||||
}
|
||||
if (path == null || !isEmptyCondition(((ArrangementTreeNode)path.getLastPathComponent()).getBackingCondition())) {
|
||||
mySkipSelectionChange = true;
|
||||
}
|
||||
}
|
||||
try {
|
||||
super.processMouseEvent(e);
|
||||
if (mySkipSelectionChange) {
|
||||
@@ -106,7 +135,7 @@ public class ArrangementRuleTree {
|
||||
mySkipSelectionChange = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
myTree.setSelectionModel(mySelectionModel);
|
||||
myTree.setRootVisible(false);
|
||||
mySelectionModel.addTreeSelectionListener(new TreeSelectionListener() {
|
||||
@@ -166,7 +195,7 @@ public class ArrangementRuleTree {
|
||||
rules.add(new ArrangementCompositeMatchCondition(ArrangementCompositeMatchCondition.Operator.AND)
|
||||
.addOperand(new ArrangementAtomMatchCondition(ArrangementSettingType.TYPE, ArrangementEntryType.METHOD))
|
||||
.addOperand(new ArrangementAtomMatchCondition(ArrangementSettingType.MODIFIER, ArrangementModifier.PRIVATE)));
|
||||
map(myRoot, rules, grouper);
|
||||
map(rules);
|
||||
|
||||
expandAll(myTree, new TreePath(myRoot));
|
||||
myTree.setShowsRootHandles(false);
|
||||
@@ -264,7 +293,7 @@ public class ArrangementRuleTree {
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates renderer's {@link ArrangementNodeComponent#setSelected(boolean) 'selected'} state on tree ndoe selection change.
|
||||
* Updates renderer {@link ArrangementNodeComponent#setSelected(boolean) 'selected'} state on tree node selection change.
|
||||
*
|
||||
* @param path changed selection path
|
||||
* @param selected <code>true</code> if given path is selected now; <code>false</code> if given path was selected anymore
|
||||
@@ -305,21 +334,12 @@ public class ArrangementRuleTree {
|
||||
tree.expandPath(parent);
|
||||
}
|
||||
|
||||
private void map(@NotNull ArrangementTreeNode root,
|
||||
@NotNull List<ArrangementMatchCondition> matchConditions,
|
||||
@NotNull ArrangementSettingsGrouper grouper)
|
||||
{
|
||||
ArrangementRuleEditingModelBuilder builder = new ArrangementRuleEditingModelBuilder();
|
||||
private void map(@NotNull List<ArrangementMatchCondition> matchConditions) {
|
||||
for (ArrangementMatchCondition matchCondition : matchConditions) {
|
||||
builder.build(matchCondition, myTree, root, grouper, myModels);
|
||||
Pair<ArrangementRuleEditingModelImpl, TIntIntHashMap> pair = myModelBuilder.build(matchCondition, myTree, myRoot, null, myGrouper);
|
||||
myModels.put(pair.first.getRow(), pair.first);
|
||||
pair.first.addListener(myModelChangeListener);
|
||||
}
|
||||
myModels.forEachValue(new TObjectProcedure<ArrangementRuleEditingModelImpl>() {
|
||||
@Override
|
||||
public boolean execute(ArrangementRuleEditingModelImpl model) {
|
||||
model.addListener(myModelChangeListener);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void addEditingListener(@NotNull ArrangementRuleSelectionListener listener) {
|
||||
@@ -330,7 +350,7 @@ public class ArrangementRuleTree {
|
||||
* @return matcher model for the selected tree row(s) if any; null otherwise
|
||||
*/
|
||||
@Nullable
|
||||
public ArrangementRuleEditingModel getActiveModel() {
|
||||
public ArrangementRuleEditingModelImpl getActiveModel() {
|
||||
TreePath[] paths = mySelectionModel.getSelectionPaths();
|
||||
if (paths == null) {
|
||||
return null;
|
||||
@@ -340,14 +360,17 @@ public class ArrangementRuleTree {
|
||||
// for more than one rule. No model is registered for them then and we want just to skip them.
|
||||
for (int i = paths.length - 1; i >= 0; i--) {
|
||||
int row = myTree.getRowForPath(paths[i]);
|
||||
ArrangementRuleEditingModel model = myModels.get(row);
|
||||
ArrangementRuleEditingModelImpl model = myModels.get(row);
|
||||
if (model != null) {
|
||||
return model;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @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;
|
||||
@@ -387,7 +410,13 @@ public class ArrangementRuleTree {
|
||||
component.handleMouseClick(e);
|
||||
return;
|
||||
}
|
||||
// Clear selection
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -530,6 +559,31 @@ public class ArrangementRuleTree {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
ArrangementRuleEditingModelImpl activeModel = getActiveModel();
|
||||
final ArrangementTreeNode anchor = activeModel == null ? null : activeModel.getBottomMost();
|
||||
doClearSelection();
|
||||
Pair<ArrangementRuleEditingModelImpl,TIntIntHashMap> pair = myModelBuilder.build(
|
||||
new ArrangementCompositeMatchCondition(ArrangementCompositeMatchCondition.Operator.AND), myTree, myRoot, anchor, myGrouper
|
||||
);
|
||||
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();
|
||||
}
|
||||
|
||||
private class MyCellRenderer implements TreeCellRenderer {
|
||||
@Override
|
||||
@@ -545,7 +599,10 @@ public class ArrangementRuleTree {
|
||||
if (node == null) {
|
||||
return EMPTY_RENDERER;
|
||||
}
|
||||
|
||||
else if (isEmptyCondition(node)) {
|
||||
return NEW_CONDITION_RENDERER;
|
||||
}
|
||||
|
||||
if (row < 0) {
|
||||
return myFactory.getComponent(node).getUiComponent();
|
||||
}
|
||||
@@ -568,6 +625,8 @@ public class ArrangementRuleTree {
|
||||
return;
|
||||
}
|
||||
|
||||
myAlarm.cancelAllRequests();
|
||||
myAlarm.addRequest(myRequest, 300);
|
||||
clearSelection();
|
||||
ArrangementTreeNode component = (ArrangementTreeNode)path.getLastPathComponent();
|
||||
|
||||
@@ -626,4 +685,27 @@ public class ArrangementRuleTree {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private class RemoveInactiveNewModelRequest implements Runnable, TObjectProcedure<ArrangementRuleEditingModelImpl> {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
myAlarm.cancelAllRequests();
|
||||
ArrangementRuleEditingModelImpl model = getActiveModel();
|
||||
if (model != null && isEmptyCondition(model.getMatchCondition())) {
|
||||
return;
|
||||
}
|
||||
myModels.forEachValue(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean execute(@NotNull ArrangementRuleEditingModelImpl model) {
|
||||
if (isEmptyCondition(model.getMatchCondition())) {
|
||||
model.destroy();
|
||||
// TODO den restore selection (e.g. from bottom)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+49
-7
@@ -16,18 +16,27 @@
|
||||
package com.intellij.application.options.codeStyle.arrangement;
|
||||
|
||||
import com.intellij.application.options.CodeStyleAbstractPanel;
|
||||
import com.intellij.ide.DataManager;
|
||||
import com.intellij.ide.ui.customization.CustomizationUtil;
|
||||
import com.intellij.openapi.actionSystem.ActionGroup;
|
||||
import com.intellij.openapi.actionSystem.ActionManager;
|
||||
import com.intellij.openapi.actionSystem.ActionToolbar;
|
||||
import com.intellij.openapi.actionSystem.DataProvider;
|
||||
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.Ref;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.arrangement.settings.*;
|
||||
import com.intellij.ui.IdeBorderFactory;
|
||||
import com.intellij.ui.SideBorder;
|
||||
import com.intellij.ui.components.JBScrollPane;
|
||||
import com.intellij.ui.treeStructure.Tree;
|
||||
import com.intellij.util.ui.GridBag;
|
||||
import org.jdesktop.swingx.JXTaskPane;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
@@ -44,29 +53,38 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel {
|
||||
|
||||
@NotNull private final JPanel myContent = new JPanel(new GridBagLayout());
|
||||
|
||||
public ArrangementSettingsPanel(@NotNull CodeStyleSettings settings, @NotNull ArrangementStandardSettingsAware filter) {
|
||||
public ArrangementSettingsPanel(@NotNull CodeStyleSettings settings, @NotNull final ArrangementStandardSettingsAware filter) {
|
||||
super(settings);
|
||||
ArrangementStandardSettingsRepresentationAware representationManager = DefaultArrangementSettingsRepresentationManager.INSTANCE;
|
||||
if (filter instanceof ArrangementStandardSettingsRepresentationAware) {
|
||||
representationManager = (ArrangementStandardSettingsRepresentationAware)filter;
|
||||
}
|
||||
ArrangementNodeDisplayManager displayManager = new ArrangementNodeDisplayManager(filter, representationManager);
|
||||
final ArrangementNodeDisplayManager displayManager = new ArrangementNodeDisplayManager(filter, representationManager);
|
||||
ArrangementSettingsGrouper grouper = DefaultArrangementSettingsGrouper.INSTANCE;
|
||||
if (filter instanceof ArrangementSettingsGrouper) {
|
||||
grouper = (ArrangementSettingsGrouper)filter;
|
||||
}
|
||||
|
||||
final ActionManager actionManager = ActionManager.getInstance();
|
||||
final ActionGroup actionGroup = (ActionGroup)actionManager.getAction(ArrangementConstants.ACTION_GROUP_RULE_EDITOR_TOOL_WINDOW);
|
||||
ActionToolbar actionToolbar = actionManager.createActionToolbar(ArrangementConstants.RULE_EDITOR_TOOL_WINDOW_PLACE, actionGroup, true);
|
||||
JPanel toolbarControl = new JPanel(new GridBagLayout());
|
||||
toolbarControl.add(actionToolbar.getComponent(), new GridBag().anchor(GridBagConstraints.WEST).weightx(1).fillCellHorizontally());
|
||||
toolbarControl.setBorder(IdeBorderFactory.createBorder(SideBorder.LEFT | SideBorder.TOP | SideBorder.RIGHT));
|
||||
myContent.add(toolbarControl, new GridBag().weightx(1).fillCellHorizontally().coverLine());
|
||||
|
||||
final ArrangementRuleTree ruleTree = new ArrangementRuleTree(grouper, displayManager);
|
||||
Tree component = ruleTree.getTreeComponent();
|
||||
myContent.add(new JBScrollPane(component), new GridBag().weightx(1).weighty(1).fillCell().coverLine());
|
||||
Tree treeComponent = ruleTree.getTreeComponent();
|
||||
actionToolbar.setTargetComponent(treeComponent);
|
||||
myContent.add(new JBScrollPane(treeComponent), new GridBag().weightx(1).weighty(1).fillCell().coverLine());
|
||||
CustomizationUtil.installPopupHandler(
|
||||
component, ArrangementConstants.ACTION_GROUP_RULE_EDITOR_CONTEXT_MENU, ArrangementConstants.RULE_EDITOR_PLACE
|
||||
treeComponent, ArrangementConstants.ACTION_GROUP_RULE_EDITOR_CONTEXT_MENU, ArrangementConstants.RULE_EDITOR_PLACE
|
||||
);
|
||||
|
||||
final JXTaskPane editorPane = new JXTaskPane(ApplicationBundle.message("arrangement.title.editor"));
|
||||
final ArrangementRuleEditor ruleEditor = new ArrangementRuleEditor(filter, displayManager);
|
||||
ruleEditor.applyBackground(component.getBackground());
|
||||
editorPane.getContentPane().setBackground(component.getBackground());
|
||||
ruleEditor.applyBackground(treeComponent.getBackground());
|
||||
editorPane.getContentPane().setBackground(treeComponent.getBackground());
|
||||
editorPane.add(ruleEditor);
|
||||
editorPane.setCollapsed(true);
|
||||
final Ref<Boolean> resetEditor = new Ref<Boolean>(Boolean.TRUE);
|
||||
@@ -97,6 +115,30 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel {
|
||||
editorPane.setCollapsed(true);
|
||||
}
|
||||
});
|
||||
final Runnable newRuleFunction = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ArrangementRuleEditingModel model = ruleTree.newModel();
|
||||
ruleEditor.updateState(model);
|
||||
resetEditor.set(Boolean.FALSE);
|
||||
try {
|
||||
editorPane.setCollapsed(false);
|
||||
}
|
||||
finally {
|
||||
resetEditor.set(Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
};
|
||||
treeComponent.putClientProperty(DataManager.CLIENT_PROPERTY_DATA_PROVIDER, new DataProvider() {
|
||||
@Nullable
|
||||
@Override
|
||||
public Object getData(@NonNls String dataId) {
|
||||
if (ArrangementConstants.NEW_RULE_FUNCTION_KEY.is(dataId)) {
|
||||
return newRuleFunction;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.action;
|
||||
|
||||
import com.intellij.application.options.codeStyle.arrangement.ArrangementConstants;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.application.ApplicationBundle;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 8/24/12 1:54 PM
|
||||
*/
|
||||
public class AddArrangementRuleAction extends AnAction {
|
||||
|
||||
public AddArrangementRuleAction() {
|
||||
getTemplatePresentation().setIcon(AllIcons.General.Add);
|
||||
getTemplatePresentation().setText(ApplicationBundle.message("arrangement.action.condition.add.text"));
|
||||
getTemplatePresentation().setDescription(ApplicationBundle.message("arrangement.action.rule.add.description"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
Runnable function = ArrangementConstants.NEW_RULE_FUNCTION_KEY.getData(e.getDataContext());
|
||||
if (function != null) {
|
||||
function.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import com.intellij.Patches;
|
||||
import com.intellij.ide.util.treeView.*;
|
||||
import com.intellij.openapi.ui.GraphicsConfig;
|
||||
import com.intellij.openapi.ui.Queryable;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.ui.*;
|
||||
@@ -97,7 +98,7 @@ public class Tree extends JTree implements ComponentWithEmptyText, ComponentWith
|
||||
TreeUI actualUI = ui;
|
||||
if (!isCustomUI()) {
|
||||
if (!(ui instanceof WideSelectionTreeUI) && isWideSelection() && !UIUtil.isUnderGTKLookAndFeel()) {
|
||||
actualUI = new WideSelectionTreeUI(isWideSelection(), isAlwaysPaintRowBackground());
|
||||
actualUI = new WideSelectionTreeUI(isWideSelection(), getWideSelectionBackgroundCondition());
|
||||
}
|
||||
}
|
||||
super.setUI(actualUI);
|
||||
@@ -140,8 +141,14 @@ public class Tree extends JTree implements ComponentWithEmptyText, ComponentWith
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean isAlwaysPaintRowBackground() {
|
||||
return !SystemInfo.isMac;
|
||||
/**
|
||||
* @return a strategy which determines if a wide selection should be drawn for a target row (it's number is
|
||||
* {@link Condition#value(Object) given} as an argument to the strategy)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@NotNull
|
||||
protected Condition<Integer> getWideSelectionBackgroundCondition() {
|
||||
return SystemInfo.isMac ? Condition.FALSE : Condition.TRUE;
|
||||
}
|
||||
|
||||
public boolean isFileColorsEnabled() {
|
||||
|
||||
@@ -555,3 +555,5 @@ arrangement.title.editor=Edit rule
|
||||
arrangement.text.type=Type
|
||||
arrangement.text.modifier=Modifier
|
||||
arrangement.text.negate=Negate
|
||||
arrangement.action.condition.add.text=Add condition
|
||||
arrangement.action.condition.add.description=Add new arrangement rule condition
|
||||
@@ -811,6 +811,10 @@
|
||||
<group id="Arrangement.RuleEditor.Context.Menu">
|
||||
<!--TODO den implement-->
|
||||
</group>
|
||||
|
||||
<group id="Arrangement.RuleEditor.ToolWIndow">
|
||||
<action id="Arrangement.Rule.Add" class="com.intellij.application.options.codeStyle.arrangement.action.AddArrangementRuleAction"/>
|
||||
</group>
|
||||
|
||||
</actions>
|
||||
</component>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.util.ui.tree;
|
||||
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.SystemInfo;
|
||||
import com.intellij.util.containers.ComparatorUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
@@ -36,26 +37,34 @@ import java.awt.event.MouseListener;
|
||||
* @author Konstantin Bulenkov
|
||||
*/
|
||||
public class WideSelectionTreeUI extends BasicTreeUI {
|
||||
|
||||
@NonNls public static final String SOURCE_LIST_CLIENT_PROPERTY = "mac.ui.source.list";
|
||||
@NonNls public static final String STRIPED_CLIENT_PROPERTY = "mac.ui.striped";
|
||||
|
||||
private static final Border LIST_BACKGROUND_PAINTER = UIManager.getBorder("List.sourceListBackgroundPainter");
|
||||
private static final Border LIST_SELECTION_BACKGROUND_PAINTER = UIManager.getBorder("List.sourceListSelectionBackgroundPainter");
|
||||
private static final Border LIST_FOCUSED_SELECTION_BACKGROUND_PAINTER = UIManager.getBorder("List.sourceListFocusedSelectionBackgroundPainter");
|
||||
|
||||
|
||||
@NotNull private final Condition<Integer> myWideSelectionCondition;
|
||||
private boolean myWideSelection;
|
||||
private boolean myAlwaysPaintRowBackground;
|
||||
private boolean myOldRepaintAllRowValue;
|
||||
private boolean invertLineColor;
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public WideSelectionTreeUI() {
|
||||
this(true, true);
|
||||
this(true, Condition.TRUE);
|
||||
}
|
||||
|
||||
public WideSelectionTreeUI(final boolean wideSelection, boolean alwaysPaintRowBackground) {
|
||||
/**
|
||||
* Creates new <code>WideSelectionTreeUI</code> object.
|
||||
*
|
||||
* @param wideSelection flag that determines if wide selection should be used
|
||||
* @param wideSelectionCondition strategy that determine if wide selection should be used for a target row (it's zero-based index
|
||||
* is given to the condition as an argument)
|
||||
*/
|
||||
public WideSelectionTreeUI(final boolean wideSelection, @NotNull Condition<Integer> wideSelectionCondition) {
|
||||
myWideSelection = wideSelection;
|
||||
myAlwaysPaintRowBackground = alwaysPaintRowBackground;
|
||||
myWideSelectionCondition = wideSelectionCondition;
|
||||
}
|
||||
|
||||
private final MouseListener mySelectionListener = new MouseAdapter() {
|
||||
@@ -232,7 +241,7 @@ public class WideSelectionTreeUI extends BasicTreeUI {
|
||||
@Override
|
||||
protected void paintVerticalLine(Graphics g, JComponent c, int x, int top, int bottom) {
|
||||
if (tree.hasFocus() && UIUtil.isUnderAlloyIDEALookAndFeel()) {
|
||||
int y0 = top, y1 = top;
|
||||
int y0, y1 = top;
|
||||
while (y1 < bottom) {
|
||||
y0 = y1;
|
||||
final int row = tree.getRowForPath(tree.getClosestPathForLocation(x, y0 + 1));
|
||||
@@ -297,7 +306,7 @@ public class WideSelectionTreeUI extends BasicTreeUI {
|
||||
LIST_SELECTION_BACKGROUND_PAINTER.paintBorder(tree, rowGraphics, xOffset, bounds.y, containerWidth, bounds.height);
|
||||
}
|
||||
}
|
||||
else if (myAlwaysPaintRowBackground) {
|
||||
else if (myWideSelectionCondition.value(row)) {
|
||||
rowGraphics.setColor(background);
|
||||
rowGraphics.fillRect(xOffset, bounds.y, containerWidth, bounds.height);
|
||||
}
|
||||
@@ -309,7 +318,7 @@ public class WideSelectionTreeUI extends BasicTreeUI {
|
||||
bg = background;
|
||||
}
|
||||
|
||||
if (myAlwaysPaintRowBackground || selected) {
|
||||
if (myWideSelectionCondition.value(row) || selected) {
|
||||
rowGraphics.setColor(bg);
|
||||
rowGraphics.fillRect(xOffset, bounds.y, containerWidth, bounds.height - 1);
|
||||
}
|
||||
@@ -330,7 +339,7 @@ public class WideSelectionTreeUI extends BasicTreeUI {
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g, JComponent c) {
|
||||
if (myAlwaysPaintRowBackground && myWideSelection && !UIUtil.isUnderAquaBasedLookAndFeel()) {
|
||||
if (myWideSelection && !UIUtil.isUnderAquaBasedLookAndFeel()) {
|
||||
paintSelectedRows(g, ((JTree)c));
|
||||
}
|
||||
if (myWideSelection) {
|
||||
@@ -357,7 +366,7 @@ public class WideSelectionTreeUI extends BasicTreeUI {
|
||||
final int lastVisibleRow = tr.getClosestRowForLocation(rect.x, rect.y + rect.height);
|
||||
|
||||
for (int row = firstVisibleRow; row <= lastVisibleRow; row++) {
|
||||
if (tr.getSelectionModel().isRowSelected(row)) {
|
||||
if (tr.getSelectionModel().isRowSelected(row) && myWideSelectionCondition.value(row)) {
|
||||
final Rectangle bounds = tr.getRowBounds(row);
|
||||
Color color = UIUtil.getTreeSelectionBackground(tr.hasFocus());
|
||||
if (color != null) {
|
||||
|
||||
Reference in New Issue
Block a user