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-IDEA
1. Adding ability to modify arrangement rules; 2. Refactoring;
This commit is contained in:
@@ -16,11 +16,13 @@
|
||||
package com.intellij.psi.codeStyle.arrangement;
|
||||
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType;
|
||||
import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier;
|
||||
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementMatcherSettings;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.*;
|
||||
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementSettingsGrouper;
|
||||
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware;
|
||||
import com.intellij.psi.codeStyle.arrangement.sort.ArrangementEntrySortType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -32,7 +34,9 @@ import java.util.*;
|
||||
* @author Denis Zhdanov
|
||||
* @since 7/20/12 2:31 PM
|
||||
*/
|
||||
public class JavaRearranger implements Rearranger<JavaElementArrangementEntry>, ArrangementStandardSettingsAware {
|
||||
public class JavaRearranger implements Rearranger<JavaElementArrangementEntry>, ArrangementStandardSettingsAware,
|
||||
ArrangementSettingsGrouper
|
||||
{
|
||||
|
||||
// Type
|
||||
@NotNull private static final Set<ArrangementEntryType> SUPPORTED_TYPES = EnumSet.of(
|
||||
@@ -80,35 +84,87 @@ public class JavaRearranger implements Rearranger<JavaElementArrangementEntry>,
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNameFilterEnabled(@Nullable ArrangementMatcherSettings settings) {
|
||||
public boolean isNameFilterEnabled(@Nullable ArrangementSettingsNode current) {
|
||||
// TODO den implement
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(@NotNull ArrangementEntryType type, @Nullable ArrangementMatcherSettings settings) {
|
||||
public boolean isEnabled(@NotNull ArrangementEntryType type, @Nullable ArrangementSettingsNode current) {
|
||||
return SUPPORTED_TYPES.contains(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(@NotNull ArrangementModifier modifier, @Nullable ArrangementMatcherSettings settings) {
|
||||
if (settings == null) {
|
||||
public boolean isEnabled(@NotNull ArrangementModifier modifier, @Nullable ArrangementSettingsNode current) {
|
||||
if (current == null) {
|
||||
return SUPPORTED_MODIFIERS.contains(modifier);
|
||||
}
|
||||
Object key = NO_TYPE;
|
||||
for (ArrangementEntryType type : ArrangementEntryType.values()) {
|
||||
if (settings.hasCondition(type)) {
|
||||
key = type;
|
||||
break;
|
||||
|
||||
final Ref<Object> typeRef = new Ref<Object>();
|
||||
current.invite(new ArrangementSettingsNodeVisitor() {
|
||||
@Override
|
||||
public void visit(@NotNull ArrangementSettingsAtomNode node) {
|
||||
if (node.getType() == ArrangementSettingType.TYPE) {
|
||||
typeRef.set(node.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull ArrangementSettingsCompositeNode node) {
|
||||
for (ArrangementSettingsNode n : node.getOperands()) {
|
||||
if (typeRef.get() != null) {
|
||||
return;
|
||||
}
|
||||
n.invite(this);
|
||||
}
|
||||
}
|
||||
});
|
||||
Object key = typeRef.get() == null ? NO_TYPE : typeRef.get();
|
||||
Set<ArrangementModifier> modifiers = MODIFIERS_BY_TYPE.get(key);
|
||||
return modifiers != null && modifiers.contains(modifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(@NotNull ArrangementEntrySortType type, @Nullable ArrangementMatcherSettings settings) {
|
||||
public boolean isEnabled(@NotNull ArrangementEntrySortType type, @Nullable ArrangementSettingsNode current) {
|
||||
// TODO den implement
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public HierarchicalArrangementSettingsNode group(@NotNull ArrangementSettingsNode node) {
|
||||
final Ref<HierarchicalArrangementSettingsNode> result = new Ref<HierarchicalArrangementSettingsNode>();
|
||||
node.invite(new ArrangementSettingsNodeVisitor() {
|
||||
@Override
|
||||
public void visit(@NotNull ArrangementSettingsAtomNode node) {
|
||||
result.set(new HierarchicalArrangementSettingsNode(node));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull ArrangementSettingsCompositeNode node) {
|
||||
ArrangementSettingsNode typeNode = null;
|
||||
for (ArrangementSettingsNode n : node.getOperands()) {
|
||||
if (n instanceof ArrangementSettingsAtomNode && ((ArrangementSettingsAtomNode)n).getType() == ArrangementSettingType.TYPE) {
|
||||
typeNode = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (typeNode == null) {
|
||||
result.set(new HierarchicalArrangementSettingsNode(node));
|
||||
}
|
||||
else {
|
||||
HierarchicalArrangementSettingsNode parent = new HierarchicalArrangementSettingsNode(typeNode);
|
||||
ArrangementSettingsCompositeNode compositeWithoutType = new ArrangementSettingsCompositeNode(node.getOperator());
|
||||
for (ArrangementSettingsNode n : node.getOperands()) {
|
||||
if (n != typeNode) {
|
||||
compositeWithoutType.addOperand(n);
|
||||
}
|
||||
}
|
||||
parent.addChild(new HierarchicalArrangementSettingsNode(compositeWithoutType));
|
||||
result.set(parent);
|
||||
}
|
||||
}
|
||||
});
|
||||
return result.get();
|
||||
}
|
||||
}
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.ArrangementUtil;
|
||||
import com.intellij.psi.codeStyle.arrangement.JavaRearranger;
|
||||
import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType;
|
||||
import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingType;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsAtomNode;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode;
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
|
||||
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.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 08/15/2012
|
||||
*/
|
||||
public class ArrangementRuleEditingModelBuilderTest {
|
||||
|
||||
@NotNull private ArrangementRuleEditingModelBuilder myBuilder;
|
||||
@NotNull private DefaultMutableTreeNode myRoot;
|
||||
@NotNull private TIntObjectHashMap<ArrangementRuleEditingModel> myRowMappings;
|
||||
@NotNull private JavaRearranger myGrouper;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
myBuilder = new ArrangementRuleEditingModelBuilder();
|
||||
myRoot = new DefaultMutableTreeNode();
|
||||
myRowMappings = new TIntObjectHashMap<ArrangementRuleEditingModel>();
|
||||
myGrouper = new JavaRearranger();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapToTheSameLayer() {
|
||||
build(ArrangementUtil.and(atom(PUBLIC), atom(STATIC)));
|
||||
// TODO den uncomment
|
||||
//assertEquals(1, myRowMappings.size());
|
||||
}
|
||||
|
||||
private void build(@NotNull ArrangementSettingsNode node) {
|
||||
myBuilder.build(node, myRoot, myGrouper, myRowMappings);
|
||||
}
|
||||
|
||||
private static ArrangementSettingsAtomNode 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 ArrangementSettingsAtomNode(type, condition);
|
||||
}
|
||||
}
|
||||
@@ -16,10 +16,15 @@
|
||||
package com.intellij.psi.codeStyle.arrangement;
|
||||
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryMatcher;
|
||||
import com.intellij.psi.codeStyle.arrangement.match.CompositeArrangementEntryMatcher;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsAtomNode;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsCompositeNode;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNodeVisitor;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -108,17 +113,48 @@ public class ArrangementUtil {
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region Matchers composition
|
||||
//region Rule composition
|
||||
|
||||
@NotNull
|
||||
public static ArrangementSettingsNode and(@NotNull ArrangementSettingsNode... nodes) {
|
||||
final ArrangementSettingsCompositeNode result = new ArrangementSettingsCompositeNode(ArrangementSettingsCompositeNode.Operator.AND);
|
||||
final ArrangementSettingsNodeVisitor visitor = new ArrangementSettingsNodeVisitor() {
|
||||
@Override
|
||||
public void visit(@NotNull ArrangementSettingsAtomNode node) {
|
||||
result.addOperand(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(@NotNull ArrangementSettingsCompositeNode node) {
|
||||
if (node.getOperator() == ArrangementSettingsCompositeNode.Operator.AND) {
|
||||
for (ArrangementSettingsNode operand : node.getOperands()) {
|
||||
operand.invite(this);
|
||||
}
|
||||
}
|
||||
else {
|
||||
result.addOperand(node);
|
||||
}
|
||||
}
|
||||
};
|
||||
for (ArrangementSettingsNode node : nodes) {
|
||||
node.invite(visitor);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// TODO den remove
|
||||
@NotNull
|
||||
public static ArrangementEntryMatcher or(@NotNull ArrangementEntryMatcher... matchers) {
|
||||
return combine(CompositeArrangementEntryMatcher.Operator.OR, matchers);
|
||||
}
|
||||
|
||||
// TODO den remove
|
||||
@NotNull
|
||||
public static ArrangementEntryMatcher and(@NotNull ArrangementEntryMatcher... matchers) {
|
||||
return combine(CompositeArrangementEntryMatcher.Operator.AND, matchers);
|
||||
}
|
||||
|
||||
// TODO den remove
|
||||
@NotNull
|
||||
private static ArrangementEntryMatcher combine(@NotNull CompositeArrangementEntryMatcher.Operator operator,
|
||||
@NotNull ArrangementEntryMatcher... matchers)
|
||||
|
||||
+1
@@ -90,6 +90,7 @@ public class ArrangementSettingsAtomNode implements ArrangementSettingsNode {
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ArrangementSettingsAtomNode clone() {
|
||||
ArrangementSettingsAtomNode result = new ArrangementSettingsAtomNode(myType, myValue);
|
||||
|
||||
+6
-6
@@ -16,11 +16,10 @@
|
||||
package com.intellij.psi.codeStyle.arrangement.model;
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* // TODO den add doc
|
||||
@@ -30,7 +29,7 @@ import java.util.List;
|
||||
*/
|
||||
public class ArrangementSettingsCompositeNode implements ArrangementSettingsNode {
|
||||
|
||||
@NotNull private final List<ArrangementSettingsNode> myOperands = new ArrayList<ArrangementSettingsNode>();
|
||||
@NotNull private final Set<ArrangementSettingsNode> myOperands = new HashSet<ArrangementSettingsNode>();
|
||||
@NotNull private final Operator myOperator;
|
||||
|
||||
public ArrangementSettingsCompositeNode(@NotNull Operator operator) {
|
||||
@@ -38,7 +37,7 @@ public class ArrangementSettingsCompositeNode implements ArrangementSettingsNode
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<ArrangementSettingsNode> getOperands() {
|
||||
public Set<ArrangementSettingsNode> getOperands() {
|
||||
return myOperands;
|
||||
}
|
||||
|
||||
@@ -57,6 +56,7 @@ public class ArrangementSettingsCompositeNode implements ArrangementSettingsNode
|
||||
visitor.visit(this);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ArrangementSettingsCompositeNode clone() {
|
||||
ArrangementSettingsCompositeNode result = new ArrangementSettingsCompositeNode(myOperator);
|
||||
@@ -70,7 +70,7 @@ public class ArrangementSettingsCompositeNode implements ArrangementSettingsNode
|
||||
public String toString() {
|
||||
return StringUtil.join(myOperands, myOperator == Operator.AND ? " and " : " or ");
|
||||
}
|
||||
|
||||
|
||||
public enum Operator {
|
||||
AND, OR
|
||||
}
|
||||
|
||||
+1
@@ -27,5 +27,6 @@ public interface ArrangementSettingsNode extends Cloneable {
|
||||
|
||||
void invite(@NotNull ArrangementSettingsNodeVisitor visitor);
|
||||
|
||||
@NotNull
|
||||
ArrangementSettingsNode clone();
|
||||
}
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.settings;
|
||||
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.HierarchicalArrangementSettingsNode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Strategy which hints on how arrangement settings should be grouped at the UI.
|
||||
* <p/>
|
||||
* Implementations of this interface are expected to be thread-safe.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since 8/15/12 4:21 PM
|
||||
*/
|
||||
public interface ArrangementSettingsGrouper {
|
||||
|
||||
/**
|
||||
* Allows to answer if and how should be grouped given settings.
|
||||
* <p/>
|
||||
* Example: given settings node is a composite <code>'AND'</code> node with the following conditions:
|
||||
* <code>'type: field; modifier: public; modifier: static; modifier: final'</code>. We might want to show it like below:
|
||||
* <pre>
|
||||
* field
|
||||
* |
|
||||
* ---public---static---final
|
||||
* </pre>
|
||||
* That means that we'll return a {@link HierarchicalArrangementSettingsNode} with condition <code>'type: field'</code> and
|
||||
* a single child node with composite <code>'AND'</code> condition <code>'modifier: public; modifier: static; modifier: final'</code>.
|
||||
*
|
||||
* @param node settings node which conditions should be grouped
|
||||
* @return grouping-aware node
|
||||
*/
|
||||
@NotNull
|
||||
HierarchicalArrangementSettingsNode group(@NotNull ArrangementSettingsNode node);
|
||||
}
|
||||
+5
-4
@@ -17,6 +17,7 @@ package com.intellij.psi.codeStyle.arrangement.settings;
|
||||
|
||||
import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType;
|
||||
import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode;
|
||||
import com.intellij.psi.codeStyle.arrangement.sort.ArrangementEntrySortType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -33,14 +34,14 @@ import java.util.List;
|
||||
public interface ArrangementStandardSettingsAware {
|
||||
|
||||
// TODO den add doc
|
||||
boolean isNameFilterEnabled(@Nullable ArrangementMatcherSettings current);
|
||||
boolean isNameFilterEnabled(@Nullable ArrangementSettingsNode current);
|
||||
|
||||
// TODO den add doc
|
||||
boolean isEnabled(@NotNull ArrangementEntryType type, @Nullable ArrangementMatcherSettings current);
|
||||
boolean isEnabled(@NotNull ArrangementEntryType type, @Nullable ArrangementSettingsNode current);
|
||||
|
||||
// TODO den add doc
|
||||
boolean isEnabled(@NotNull ArrangementModifier modifier, @Nullable ArrangementMatcherSettings current);
|
||||
boolean isEnabled(@NotNull ArrangementModifier modifier, @Nullable ArrangementSettingsNode current);
|
||||
|
||||
// TODO den add doc
|
||||
boolean isEnabled(@NotNull ArrangementEntrySortType type, @Nullable ArrangementMatcherSettings current);
|
||||
boolean isEnabled(@NotNull ArrangementEntrySortType type, @Nullable ArrangementSettingsNode current);
|
||||
}
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* 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.settings;
|
||||
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.HierarchicalArrangementSettingsNode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 8/15/12 4:23 PM
|
||||
*/
|
||||
public class DefaultArrangementSettingsGrouper implements ArrangementSettingsGrouper {
|
||||
|
||||
@NotNull public static final DefaultArrangementSettingsGrouper INSTANCE = new DefaultArrangementSettingsGrouper();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public HierarchicalArrangementSettingsNode group(@NotNull ArrangementSettingsNode node) {
|
||||
// Don't group.
|
||||
return new HierarchicalArrangementSettingsNode(node);
|
||||
}
|
||||
}
|
||||
-25
@@ -1,25 +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.settings.group;
|
||||
|
||||
/**
|
||||
* // TODO den add doc
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since 8/8/12 12:29 PM
|
||||
*/
|
||||
public interface ArrangementSettingsNodeGrouper {
|
||||
}
|
||||
+1
-1
@@ -135,7 +135,7 @@ public class ArrangementAndNodeComponent extends JPanel implements ArrangementNo
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g) {
|
||||
Point point = ArrangementSettingsUtil.getLocationOnScreen(this);
|
||||
Point point = ArrangementConfigUtil.getLocationOnScreen(this);
|
||||
if (point != null) {
|
||||
Rectangle bounds = getBounds();
|
||||
myScreenBounds = new Rectangle(point.x, point.y, bounds.width, bounds.height);
|
||||
|
||||
+2
-3
@@ -16,7 +16,6 @@
|
||||
package com.intellij.application.options.codeStyle.arrangement;
|
||||
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsAtomNode;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode;
|
||||
import com.intellij.ui.IdeBorderFactory;
|
||||
import com.intellij.ui.awt.RelativePoint;
|
||||
import com.intellij.util.ui.GridBag;
|
||||
@@ -40,7 +39,7 @@ public class ArrangementAtomNodeComponent implements ArrangementNodeComponent {
|
||||
@NotNull private final JPanel myRenderer = new JPanel(new GridBagLayout()) {
|
||||
@Override
|
||||
public void paint(Graphics g) {
|
||||
Point point = ArrangementSettingsUtil.getLocationOnScreen(this);
|
||||
Point point = ArrangementConfigUtil.getLocationOnScreen(this);
|
||||
if (point != null) {
|
||||
Rectangle bounds = myRenderer.getBounds();
|
||||
myScreenBounds = new Rectangle(point.x, point.y, bounds.width, bounds.height);
|
||||
@@ -114,7 +113,7 @@ public class ArrangementAtomNodeComponent implements ArrangementNodeComponent {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ArrangementSettingsNode getSettingsNode() {
|
||||
public ArrangementSettingsAtomNode getSettingsNode() {
|
||||
return mySettingsNode;
|
||||
}
|
||||
|
||||
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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.ArrangementEntryType;
|
||||
import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingType;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode;
|
||||
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementMatcherSettings;
|
||||
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Contains various utility methods to be used during showing arrangement settings.
|
||||
* <p/>
|
||||
* Thread-safe.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since 8/8/12 9:14 AM
|
||||
*/
|
||||
public class ArrangementConfigUtil {
|
||||
|
||||
private ArrangementConfigUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to answer what new settings are available for a particular {@link ArrangementMatcherSettings arrangement matcher rules}.
|
||||
*
|
||||
* @param filter filter to use
|
||||
* @param settings object that encapsulates information about current arrangement matcher settings
|
||||
* @return map which contains information on what new new settings are available at the current situation
|
||||
*/
|
||||
@NotNull
|
||||
public static Map<ArrangementSettingType, Collection<?>> buildAvailableOptions(@NotNull ArrangementStandardSettingsAware filter,
|
||||
@Nullable ArrangementSettingsNode settings)
|
||||
{
|
||||
Map<ArrangementSettingType, Collection<?>> result = new EnumMap<ArrangementSettingType, Collection<?>>(ArrangementSettingType.class);
|
||||
processData(filter, settings, result, ArrangementSettingType.TYPE, ArrangementEntryType.values());
|
||||
processData(filter, settings, result, ArrangementSettingType.MODIFIER, ArrangementModifier.values());
|
||||
return result;
|
||||
}
|
||||
|
||||
private static <T> void processData(@NotNull ArrangementStandardSettingsAware filter,
|
||||
@Nullable ArrangementSettingsNode settings,
|
||||
@NotNull Map<ArrangementSettingType, Collection<?>> result,
|
||||
@NotNull ArrangementSettingType type,
|
||||
@NotNull T[] values)
|
||||
{
|
||||
List<T> data = null;
|
||||
for (T v : values) {
|
||||
if (!isEnabled(v, filter, settings)) {
|
||||
continue;
|
||||
}
|
||||
if (data == null) {
|
||||
data = new ArrayList<T>();
|
||||
}
|
||||
data.add(v);
|
||||
}
|
||||
if (data != null) {
|
||||
result.put(type, data);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isEnabled(@NotNull Object conditionId,
|
||||
@NotNull ArrangementStandardSettingsAware filter,
|
||||
@Nullable ArrangementSettingsNode settings)
|
||||
{
|
||||
if (conditionId instanceof ArrangementEntryType) {
|
||||
return filter.isEnabled((ArrangementEntryType)conditionId, settings);
|
||||
}
|
||||
else if (conditionId instanceof ArrangementModifier) {
|
||||
return filter.isEnabled((ArrangementModifier)conditionId, settings);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Point getLocationOnScreen(@NotNull JComponent component) {
|
||||
int dx = 0;
|
||||
int dy = 0;
|
||||
for (Container c = component; c != null; c = c.getParent()) {
|
||||
if (c.isShowing()) {
|
||||
Point locationOnScreen = c.getLocationOnScreen();
|
||||
locationOnScreen.translate(dx, dy);
|
||||
return locationOnScreen;
|
||||
}
|
||||
else {
|
||||
Point location = c.getLocation();
|
||||
dx += location.x;
|
||||
dy += location.y;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+63
-41
@@ -16,11 +16,8 @@
|
||||
package com.intellij.application.options.codeStyle.arrangement;
|
||||
|
||||
import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryMatcher;
|
||||
import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType;
|
||||
import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingType;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsAtomNode;
|
||||
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementMatcherSettings;
|
||||
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware;
|
||||
import com.intellij.util.ui.GridBag;
|
||||
import com.intellij.util.ui.MultiRowFlowPanel;
|
||||
@@ -29,6 +26,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
@@ -47,18 +46,25 @@ public class ArrangementMatcherRuleEditor extends JPanel {
|
||||
new HashMap<Object, ArrangementAtomNodeComponent>();
|
||||
|
||||
@NotNull private final ArrangementStandardSettingsAware myFilter;
|
||||
@Nullable private ArrangementRuleEditingModel myModel;
|
||||
|
||||
public ArrangementMatcherRuleEditor(@NotNull ArrangementStandardSettingsAware filter,
|
||||
@NotNull ArrangementNodeDisplayManager displayManager)
|
||||
{
|
||||
myFilter = filter;
|
||||
init(displayManager);
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
onMouseClicked(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void init(@NotNull ArrangementNodeDisplayManager displayManager) {
|
||||
setLayout(new GridBagLayout());
|
||||
|
||||
Map<ArrangementSettingType, Collection<?>> supportedSettings = ArrangementSettingsUtil.buildAvailableOptions(myFilter, null);
|
||||
Map<ArrangementSettingType, Collection<?>> supportedSettings = ArrangementConfigUtil.buildAvailableOptions(myFilter, null);
|
||||
addRowIfPossible(ArrangementSettingType.TYPE, supportedSettings, displayManager);
|
||||
addRowIfPossible(ArrangementSettingType.MODIFIER, supportedSettings, displayManager);
|
||||
}
|
||||
@@ -78,7 +84,7 @@ public class ArrangementMatcherRuleEditor extends JPanel {
|
||||
myComponents.put(value, component);
|
||||
valuesPanel.add(component.getUiComponent());
|
||||
}
|
||||
|
||||
|
||||
int top = ArrangementAtomNodeComponent.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());
|
||||
@@ -89,52 +95,68 @@ public class ArrangementMatcherRuleEditor 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 settings current rule settings if defined; null as an indication that no settings should be active
|
||||
* @param model current rule settings model if defined; null as an indication that no settings should be active
|
||||
*/
|
||||
public void updateState(@Nullable ArrangementMatcherSettings settings) {
|
||||
for (ArrangementEntryType type : ArrangementEntryType.values()) {
|
||||
ArrangementAtomNodeComponent component = myComponents.get(type);
|
||||
if (component == null) {
|
||||
continue;
|
||||
}
|
||||
boolean enabled;
|
||||
boolean selected;
|
||||
if (settings == null) {
|
||||
enabled = false;
|
||||
selected = false;
|
||||
}
|
||||
else {
|
||||
enabled = myFilter.isEnabled(type, settings);
|
||||
selected = settings.hasCondition(type);
|
||||
}
|
||||
component.setEnabled(enabled);
|
||||
component.setSelected(selected);
|
||||
public void updateState(@Nullable ArrangementRuleEditingModel model) {
|
||||
myModel = model;
|
||||
|
||||
// Reset state.
|
||||
for (ArrangementAtomNodeComponent component : myComponents.values()) {
|
||||
component.setEnabled(false);
|
||||
component.setSelected(false);
|
||||
}
|
||||
for (ArrangementModifier modifier : ArrangementModifier.values()) {
|
||||
ArrangementAtomNodeComponent component = myComponents.get(modifier);
|
||||
if (component == null) {
|
||||
continue;
|
||||
|
||||
if (model == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<ArrangementSettingType, Collection<?>> available = ArrangementConfigUtil.buildAvailableOptions(myFilter, model.getSettingsNode());
|
||||
for (Collection<?> ids : available.values()) {
|
||||
for (Object id : ids) {
|
||||
ArrangementAtomNodeComponent component = myComponents.get(id);
|
||||
if (component != null) {
|
||||
component.setEnabled(true);
|
||||
component.setSelected(model.hasCondition(id));
|
||||
}
|
||||
}
|
||||
boolean enabled;
|
||||
boolean selected;
|
||||
if (settings == null) {
|
||||
enabled = false;
|
||||
selected = false;
|
||||
}
|
||||
else {
|
||||
enabled = myFilter.isEnabled(modifier, settings);
|
||||
selected = settings.hasCondition(modifier);
|
||||
}
|
||||
component.setEnabled(enabled);
|
||||
component.setSelected(selected);
|
||||
}
|
||||
repaint();
|
||||
}
|
||||
|
||||
|
||||
public void applyBackground(@NotNull Color color) {
|
||||
setBackground(color);
|
||||
for (JComponent component : myColoredComponents) {
|
||||
component.setBackground(color);
|
||||
}
|
||||
}
|
||||
|
||||
private void onMouseClicked(@NotNull MouseEvent e) {
|
||||
if (myModel == null) {
|
||||
return;
|
||||
}
|
||||
ArrangementAtomNodeComponent component = getNodeComponentAt(e.getLocationOnScreen());
|
||||
if (component == null) {
|
||||
return;
|
||||
}
|
||||
ArrangementSettingsAtomNode settingsNode = component.getSettingsNode();
|
||||
boolean remove = myModel.hasCondition(settingsNode.getValue());
|
||||
component.setSelected(!remove);
|
||||
if (remove) {
|
||||
myModel.removeAndCondition(settingsNode);
|
||||
}
|
||||
else {
|
||||
myModel.addAndCondition(settingsNode);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private ArrangementAtomNodeComponent getNodeComponentAt(@NotNull Point screenPoint) {
|
||||
for (ArrangementAtomNodeComponent component : myComponents.values()) {
|
||||
Rectangle screenBounds = component.getScreenBounds();
|
||||
if (screenBounds != null && screenBounds.contains(screenPoint)) {
|
||||
return component;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ public class ArrangementNodeDisplayManager {
|
||||
@NotNull ArrangementStandardSettingsRepresentationAware representationManager)
|
||||
{
|
||||
myRepresentationManager = representationManager;
|
||||
Map<ArrangementSettingType, Collection<?>> map = ArrangementSettingsUtil.buildAvailableOptions(filter, null);
|
||||
Map<ArrangementSettingType, Collection<?>> map = ArrangementConfigUtil.buildAvailableOptions(filter, null);
|
||||
for (Map.Entry<ArrangementSettingType, Collection<?>> entry : map.entrySet()) {
|
||||
myMaxWidths.put(entry.getKey(), maxWidth(entry.getValue()));
|
||||
}
|
||||
|
||||
+2
-3
@@ -15,16 +15,15 @@
|
||||
*/
|
||||
package com.intellij.application.options.codeStyle.arrangement;
|
||||
|
||||
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementMatcherSettings;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 8/15/12 9:14 AM
|
||||
*/
|
||||
public interface ArrangementMatcherEditingListener {
|
||||
public interface ArrangementRuleEditingListener {
|
||||
|
||||
void startEditing(@NotNull ArrangementMatcherSettings settings);
|
||||
void startEditing(@NotNull ArrangementRuleEditingModel settings);
|
||||
|
||||
void stopEditing();
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.ArrangementEntryType;
|
||||
import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsAtomNode;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Combines and encapsulates information about arrangement matcher rules representation (tree nodes) and
|
||||
* underlying {@link ArrangementSettingsNode data model}.
|
||||
* <p/>
|
||||
* 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
|
||||
ArrangementSettingsNode getSettingsNode();
|
||||
|
||||
/**
|
||||
* 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 <code>true</code> if current model has a registered mapping for the given key;
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
boolean hasCondition(@NotNull Object key);
|
||||
|
||||
void addAndCondition(@NotNull ArrangementSettingsAtomNode node);
|
||||
|
||||
void removeAndCondition(@NotNull ArrangementSettingsNode node);
|
||||
}
|
||||
+71
@@ -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.application.options.codeStyle.arrangement;
|
||||
|
||||
import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.HierarchicalArrangementSettingsNode;
|
||||
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementSettingsGrouper;
|
||||
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsRepresentationAware;
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
|
||||
/**
|
||||
* Holds glue logic between arrangement settings and their representation -
|
||||
* '{@link ArrangementSettingsNode} -> {@link ArrangementRuleEditingModel}'
|
||||
* <p/>
|
||||
* Thread-safe.
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since 8/15/12 2:50 PM
|
||||
*/
|
||||
public class ArrangementRuleEditingModelBuilder {
|
||||
|
||||
/**
|
||||
* Does the following:
|
||||
* <pre>
|
||||
* <ol>
|
||||
* <li>
|
||||
* {@link HierarchicalArrangementSettingsNode Groups} given {@link ArrangementSettingsNode settings} using
|
||||
* the given {@link ArrangementStandardSettingsRepresentationAware#group(ArrangementSettingsNode) strategy};
|
||||
* </li>
|
||||
* <li>
|
||||
* Build {@link DefaultMutableTreeNode tree nodes} for the {@link HierarchicalArrangementSettingsNode groiping-aware nodes}
|
||||
* and register them within the target tree structure (denoted by the given settings root element);
|
||||
* </li>
|
||||
* <li>
|
||||
* Build necessary {@link ArrangementRuleEditingModel editing models} and store them at the given container (a key is a node row);
|
||||
* </li>
|
||||
* </ol>
|
||||
* </pre>
|
||||
*
|
||||
* @param setting target settings to process
|
||||
* @param root UI tree settings root to use
|
||||
* @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)
|
||||
*/
|
||||
public void build(@NotNull ArrangementSettingsNode setting,
|
||||
@NotNull DefaultMutableTreeNode root,
|
||||
@NotNull ArrangementSettingsGrouper grouper,
|
||||
@NotNull TIntObjectHashMap<ArrangementRuleEditingModel> rowMappings)
|
||||
{
|
||||
HierarchicalArrangementSettingsNode grouped = grouper.group(setting);
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.ArrangementSettingsAtomNode;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode;
|
||||
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 8/15/12 2:40 PM
|
||||
*/
|
||||
public class ArrangementRuleEditingModelImpl implements ArrangementRuleEditingModel {
|
||||
|
||||
@NotNull private ArrangementSettingsNode mySettingsNode;
|
||||
|
||||
public ArrangementRuleEditingModelImpl(@NotNull ArrangementSettingsNode node) {
|
||||
mySettingsNode = node;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public ArrangementSettingsNode getSettingsNode() {
|
||||
return mySettingsNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCondition(@NotNull Object key) {
|
||||
// TODO den implement
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAndCondition(@NotNull ArrangementSettingsAtomNode node) {
|
||||
// TODO den implement
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAndCondition(@NotNull ArrangementSettingsNode node) {
|
||||
// TODO den implement
|
||||
}
|
||||
}
|
||||
+52
-77
@@ -17,8 +17,11 @@ package com.intellij.application.options.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 com.intellij.psi.codeStyle.arrangement.settings.ArrangementMatcherSettings;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingType;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsAtomNode;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsCompositeNode;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode;
|
||||
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementSettingsGrouper;
|
||||
import com.intellij.ui.awt.RelativePoint;
|
||||
import com.intellij.ui.treeStructure.Tree;
|
||||
import gnu.trove.TIntObjectHashMap;
|
||||
@@ -43,18 +46,20 @@ import java.util.List;
|
||||
*/
|
||||
public class ArrangementRuleTree {
|
||||
|
||||
@NotNull private final List<ArrangementMatcherEditingListener> myListeners = new ArrayList<ArrangementMatcherEditingListener>();
|
||||
@NotNull private final TreeSelectionModel mySelectionModel = new MySelectionModel();
|
||||
@NotNull private final TIntObjectHashMap<ArrangementNodeComponent> myRenderers = new TIntObjectHashMap<ArrangementNodeComponent>();
|
||||
@NotNull private final TIntObjectHashMap<ArrangementMatcherSettings> mySettings =
|
||||
new TIntObjectHashMap<ArrangementMatcherSettings>();
|
||||
@NotNull private final List<ArrangementRuleEditingListener> myListeners = new ArrayList<ArrangementRuleEditingListener>();
|
||||
@NotNull private final TreeSelectionModel mySelectionModel = new MySelectionModel();
|
||||
|
||||
@NotNull private final Tree myTree;
|
||||
@NotNull private final ArrangementNodeComponentFactory myFactory;
|
||||
@NotNull private final TIntObjectHashMap<ArrangementNodeComponent> myRenderers =
|
||||
new TIntObjectHashMap<ArrangementNodeComponent>();
|
||||
@NotNull private final TIntObjectHashMap<ArrangementRuleEditingModel> myModels =
|
||||
new TIntObjectHashMap<ArrangementRuleEditingModel>();
|
||||
|
||||
@NotNull private final Tree myTree;
|
||||
@NotNull private final ArrangementNodeComponentFactory myFactory;
|
||||
|
||||
private boolean mySkipSelectionChange;
|
||||
|
||||
public ArrangementRuleTree(@NotNull ArrangementNodeDisplayManager displayManager) {
|
||||
public ArrangementRuleTree(@NotNull ArrangementSettingsGrouper grouper, @NotNull ArrangementNodeDisplayManager displayManager) {
|
||||
myFactory = new ArrangementNodeComponentFactory(displayManager);
|
||||
DefaultMutableTreeNode root = new DefaultMutableTreeNode();
|
||||
DefaultTreeModel treeModel = new DefaultTreeModel(root);
|
||||
@@ -102,32 +107,23 @@ public class ArrangementRuleTree {
|
||||
onMouseClicked(e);
|
||||
}
|
||||
});
|
||||
|
||||
ArrangementSettingsCompositeNode constants = new ArrangementSettingsCompositeNode(ArrangementSettingsCompositeNode.Operator.AND);
|
||||
constants.addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.MODIFIER, ArrangementModifier.PUBLIC));
|
||||
constants.addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.MODIFIER, ArrangementModifier.STATIC));
|
||||
constants.addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.MODIFIER, ArrangementModifier.FINAL));
|
||||
|
||||
ArrangementSettingsCompositeNode privateFields = new ArrangementSettingsCompositeNode(ArrangementSettingsCompositeNode.Operator.AND);
|
||||
privateFields.addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.MODIFIER, ArrangementModifier.PRIVATE));
|
||||
|
||||
HierarchicalArrangementSettingsNode fields = new HierarchicalArrangementSettingsNode(new ArrangementSettingsAtomNode(
|
||||
ArrangementSettingType.TYPE, ArrangementEntryType.FIELD
|
||||
));
|
||||
fields.addChild(new HierarchicalArrangementSettingsNode(constants));
|
||||
fields.addChild(new HierarchicalArrangementSettingsNode(privateFields));
|
||||
int row = map(root, fields, null, 0);
|
||||
|
||||
HierarchicalArrangementSettingsNode methods = new HierarchicalArrangementSettingsNode(new ArrangementSettingsAtomNode(
|
||||
ArrangementSettingType.TYPE, ArrangementEntryType.METHOD
|
||||
));
|
||||
methods.addChild(new HierarchicalArrangementSettingsNode(new ArrangementSettingsAtomNode(
|
||||
ArrangementSettingType.MODIFIER, ArrangementModifier.PUBLIC
|
||||
)));
|
||||
methods.addChild(new HierarchicalArrangementSettingsNode(new ArrangementSettingsAtomNode(
|
||||
ArrangementSettingType.MODIFIER, ArrangementModifier.PRIVATE
|
||||
)));
|
||||
map(root, methods, null, row);
|
||||
|
||||
List<ArrangementSettingsNode> rules = new ArrayList<ArrangementSettingsNode>();
|
||||
rules.add(new ArrangementSettingsCompositeNode(ArrangementSettingsCompositeNode.Operator.AND)
|
||||
.addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.TYPE, ArrangementEntryType.FIELD))
|
||||
.addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.MODIFIER, ArrangementModifier.PUBLIC))
|
||||
.addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.MODIFIER, ArrangementModifier.STATIC))
|
||||
.addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.MODIFIER, ArrangementModifier.FINAL)));
|
||||
rules.add(new ArrangementSettingsCompositeNode(ArrangementSettingsCompositeNode.Operator.AND)
|
||||
.addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.TYPE, ArrangementEntryType.FIELD))
|
||||
.addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.MODIFIER, ArrangementModifier.PRIVATE)));
|
||||
rules.add(new ArrangementSettingsCompositeNode(ArrangementSettingsCompositeNode.Operator.AND)
|
||||
.addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.TYPE, ArrangementEntryType.METHOD))
|
||||
.addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.MODIFIER, ArrangementModifier.PUBLIC)));
|
||||
rules.add(new ArrangementSettingsCompositeNode(ArrangementSettingsCompositeNode.Operator.AND)
|
||||
.addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.TYPE, ArrangementEntryType.METHOD))
|
||||
.addOperand(new ArrangementSettingsAtomNode(ArrangementSettingType.MODIFIER, ArrangementModifier.PRIVATE)));
|
||||
map(root, rules, grouper);
|
||||
|
||||
expandAll(myTree, new TreePath(root));
|
||||
myTree.setRootVisible(false);
|
||||
@@ -168,58 +164,37 @@ public class ArrangementRuleTree {
|
||||
tree.expandPath(parent);
|
||||
}
|
||||
|
||||
private int map(@NotNull DefaultMutableTreeNode parentTreeNode,
|
||||
@NotNull HierarchicalArrangementSettingsNode settingsNode,
|
||||
@Nullable ArrangementSettingsCompositeNode template,
|
||||
int row)
|
||||
private void map(@NotNull DefaultMutableTreeNode root,
|
||||
@NotNull List<ArrangementSettingsNode> settings,
|
||||
@NotNull ArrangementSettingsGrouper grouper)
|
||||
{
|
||||
DefaultMutableTreeNode childTreeNode = new DefaultMutableTreeNode(settingsNode.getCurrent());
|
||||
parentTreeNode.add(childTreeNode);
|
||||
List<HierarchicalArrangementSettingsNode> children = settingsNode.getChildren();
|
||||
if (children.isEmpty()) {
|
||||
ArrangementMatcherSettings settings = new ArrangementMatcherSettings();
|
||||
ArrangementSettingsNode condition;
|
||||
if (template == null) {
|
||||
condition = settingsNode.getCurrent();
|
||||
}
|
||||
else {
|
||||
condition = template.clone().addOperand(settingsNode.getCurrent());
|
||||
}
|
||||
settings.addCondition(condition);
|
||||
mySettings.put(row, settings);
|
||||
return row + 1;
|
||||
}
|
||||
else {
|
||||
row++;
|
||||
ArrangementSettingsCompositeNode newTemplate =
|
||||
template == null ? new ArrangementSettingsCompositeNode(ArrangementSettingsCompositeNode.Operator.AND)
|
||||
: template.clone();
|
||||
newTemplate.addOperand(settingsNode.getCurrent());
|
||||
for (HierarchicalArrangementSettingsNode node : children) {
|
||||
row = map(childTreeNode, node, newTemplate, row);
|
||||
}
|
||||
return row;
|
||||
ArrangementRuleEditingModelBuilder builder = new ArrangementRuleEditingModelBuilder();
|
||||
for (ArrangementSettingsNode setting : settings) {
|
||||
builder.build(setting, root, grouper, myModels);
|
||||
}
|
||||
}
|
||||
|
||||
public void addEditingListener(@NotNull ArrangementMatcherEditingListener listener) {
|
||||
public void addEditingListener(@NotNull ArrangementRuleEditingListener listener) {
|
||||
myListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return matcher settings for the selected tree row(s) if any; null otherwise
|
||||
* @return matcher model for the selected tree row(s) if any; null otherwise
|
||||
*/
|
||||
@Nullable
|
||||
public ArrangementMatcherSettings getActiveSettings() {
|
||||
public ArrangementRuleEditingModel getActiveModel() {
|
||||
TreePath[] paths = mySelectionModel.getSelectionPaths();
|
||||
if (paths == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 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.
|
||||
for (int i = paths.length - 1; i >= 0; i--) {
|
||||
int row = myTree.getRowForPath(paths[i]);
|
||||
ArrangementMatcherSettings settings = mySettings.get(row);
|
||||
if (settings != null) {
|
||||
return settings;
|
||||
ArrangementRuleEditingModel model = myModels.get(row);
|
||||
if (model != null) {
|
||||
return model;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -296,13 +271,13 @@ public class ArrangementRuleTree {
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyEditingListeners(@Nullable ArrangementMatcherSettings settings) {
|
||||
for (ArrangementMatcherEditingListener listener : myListeners) {
|
||||
if (settings == null) {
|
||||
private void notifyEditingListeners(@Nullable ArrangementRuleEditingModel model) {
|
||||
for (ArrangementRuleEditingListener listener : myListeners) {
|
||||
if (model == null) {
|
||||
listener.stopEditing();
|
||||
}
|
||||
else {
|
||||
listener.startEditing(settings);
|
||||
listener.startEditing(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -328,7 +303,7 @@ public class ArrangementRuleTree {
|
||||
public void setSelectionPath(TreePath path) {
|
||||
if (!mySkipSelectionChange) {
|
||||
super.setSelectionPath(path);
|
||||
notifyEditingListeners(getActiveSettings());
|
||||
notifyEditingListeners(getActiveModel());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-8
@@ -22,10 +22,7 @@ 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.ArrangementMatcherSettings;
|
||||
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.psi.codeStyle.arrangement.settings.*;
|
||||
import com.intellij.ui.components.JBScrollPane;
|
||||
import com.intellij.ui.treeStructure.Tree;
|
||||
import com.intellij.util.ui.GridBag;
|
||||
@@ -54,8 +51,12 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel {
|
||||
representationManager = (ArrangementStandardSettingsRepresentationAware)filter;
|
||||
}
|
||||
ArrangementNodeDisplayManager displayManager = new ArrangementNodeDisplayManager(filter, representationManager);
|
||||
ArrangementSettingsGrouper grouper = DefaultArrangementSettingsGrouper.INSTANCE;
|
||||
if (filter instanceof ArrangementSettingsGrouper) {
|
||||
grouper = (ArrangementSettingsGrouper)filter;
|
||||
}
|
||||
|
||||
final ArrangementRuleTree ruleTree = new ArrangementRuleTree(displayManager);
|
||||
final ArrangementRuleTree ruleTree = new ArrangementRuleTree(grouper, displayManager);
|
||||
Tree component = ruleTree.getTreeComponent();
|
||||
myContent.add(new JBScrollPane(component), new GridBag().weightx(1).weighty(1).fillCell().coverLine());
|
||||
CustomizationUtil.installPopupHandler(
|
||||
@@ -78,10 +79,10 @@ public abstract class ArrangementSettingsPanel extends CodeStyleAbstractPanel {
|
||||
}
|
||||
}
|
||||
});
|
||||
ruleTree.addEditingListener(new ArrangementMatcherEditingListener() {
|
||||
ruleTree.addEditingListener(new ArrangementRuleEditingListener() {
|
||||
@Override
|
||||
public void startEditing(@NotNull ArrangementMatcherSettings settings) {
|
||||
ruleEditor.updateState(settings);
|
||||
public void startEditing(@NotNull ArrangementRuleEditingModel model) {
|
||||
ruleEditor.updateState(model);
|
||||
resetEditor.set(Boolean.FALSE);
|
||||
try {
|
||||
editorPane.setCollapsed(false);
|
||||
|
||||
-177
@@ -1,177 +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.actionSystem.DataContext;
|
||||
import com.intellij.openapi.actionSystem.DataKey;
|
||||
import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType;
|
||||
import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingType;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.ArrangementSettingsNode;
|
||||
import com.intellij.psi.codeStyle.arrangement.model.HierarchicalArrangementSettingsNode;
|
||||
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementMatcherSettings;
|
||||
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* // TODO den add doc
|
||||
*
|
||||
* @author Denis Zhdanov
|
||||
* @since 8/8/12 9:14 AM
|
||||
*/
|
||||
public class ArrangementSettingsUtil {
|
||||
|
||||
public static final DataKey<ArrangementNodeComponent> NODE_COMPONENT = DataKey.create("Arrangement.Rule.Editor.Node.Component");
|
||||
public static final DataKey<ArrangementStandardSettingsAware> FILTER = DataKey.create("Arrangement.Rule.Editor.Settings.Filter");
|
||||
public static final DataKey<ArrangementNodeDisplayManager> DISPLAY_MANAGER = DataKey.create("Arrangement.Rule.Editor.Display.Manager");
|
||||
public static final DataKey<ArrangementMatcherSettings> SETTINGS = DataKey.create("Arrangement.Rule.Editor.Matcher.Settings");
|
||||
public static final DataKey<JComponent> TREE = DataKey.create("Arrangement.Rule.Editor.Tree");
|
||||
|
||||
private static final EntryTypeHelper ENTRY_TYPE_HELPER = new EntryTypeHelper();
|
||||
private static final ModifierHelper MODIFIER_HELPER = new ModifierHelper();
|
||||
|
||||
private ArrangementSettingsUtil() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ArrangementSettingsNode getSettingsNode(@NotNull DataContext context) {
|
||||
ArrangementNodeComponent nodeComponent = NODE_COMPONENT.getData(context);
|
||||
return nodeComponent == null ? null : nodeComponent.getSettingsNode();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static HierarchicalArrangementSettingsNode buildTreeStructure(@NotNull ArrangementSettingsNode modelNode) {
|
||||
// TODO den implement
|
||||
return new HierarchicalArrangementSettingsNode(modelNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Serves for the same purposes as {@link #buildAvailableOptions(ArrangementStandardSettingsAware, ArrangementMatcherSettings)} but
|
||||
* retrieves necessary information from the given context.
|
||||
*
|
||||
* @param context target information holder
|
||||
* @return map which contains information on what new new settings are available at the current situation
|
||||
*/
|
||||
@NotNull
|
||||
public static Map<ArrangementSettingType, Collection<?>> buildAvailableOptions(@NotNull DataContext context) {
|
||||
ArrangementStandardSettingsAware filter = FILTER.getData(context);
|
||||
if (filter == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
return buildAvailableOptions(filter, SETTINGS.getData(context));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to answer what new settings are available for a particular {@link ArrangementMatcherSettings arrangement matcher rules}.
|
||||
*
|
||||
* @param filter filter to use
|
||||
* @param settings object that encapsulates information about current arrangement matcher settings
|
||||
* @return map which contains information on what new new settings are available at the current situation
|
||||
*/
|
||||
@NotNull
|
||||
public static Map<ArrangementSettingType, Collection<?>> buildAvailableOptions(@NotNull ArrangementStandardSettingsAware filter,
|
||||
@Nullable ArrangementMatcherSettings settings)
|
||||
{
|
||||
Map<ArrangementSettingType, Collection<?>> result = new EnumMap<ArrangementSettingType, Collection<?>>(ArrangementSettingType.class);
|
||||
processData(filter, settings, result, ArrangementEntryType.values(), ENTRY_TYPE_HELPER);
|
||||
processData(filter, settings, result, ArrangementModifier.values(), MODIFIER_HELPER);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static <T> void processData(@NotNull ArrangementStandardSettingsAware filter,
|
||||
@Nullable ArrangementMatcherSettings settings,
|
||||
Map<ArrangementSettingType, Collection<?>> result,
|
||||
@NotNull T[] values,
|
||||
@NotNull Helper<T> helper)
|
||||
{
|
||||
List<T> data = null;
|
||||
for (T v : values) {
|
||||
if (!helper.isEnabled(v, filter, settings)) {
|
||||
continue;
|
||||
}
|
||||
if (data == null) {
|
||||
data = new ArrayList<T>();
|
||||
}
|
||||
data.add(v);
|
||||
}
|
||||
if (data != null) {
|
||||
result.put(helper.getType(), data);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Point getLocationOnScreen(@NotNull JComponent component) {
|
||||
int dx = 0;
|
||||
int dy = 0;
|
||||
for (Container c = component; c != null; c = c.getParent()) {
|
||||
if (c.isShowing()) {
|
||||
Point locationOnScreen = c.getLocationOnScreen();
|
||||
locationOnScreen.translate(dx, dy);
|
||||
return locationOnScreen;
|
||||
}
|
||||
else {
|
||||
Point location = c.getLocation();
|
||||
dx += location.x;
|
||||
dy += location.y;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private interface Helper<T> {
|
||||
@NotNull ArrangementSettingType getType();
|
||||
boolean isEnabled(@NotNull T data, @NotNull ArrangementStandardSettingsAware filter, @Nullable ArrangementMatcherSettings settings);
|
||||
}
|
||||
|
||||
private static class EntryTypeHelper implements Helper<ArrangementEntryType> {
|
||||
@NotNull
|
||||
@Override
|
||||
public ArrangementSettingType getType() {
|
||||
return ArrangementSettingType.TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(@NotNull ArrangementEntryType data,
|
||||
@NotNull ArrangementStandardSettingsAware filter,
|
||||
@Nullable ArrangementMatcherSettings settings)
|
||||
{
|
||||
return filter.isEnabled(data, settings);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ModifierHelper implements Helper<ArrangementModifier> {
|
||||
@NotNull
|
||||
@Override
|
||||
public ArrangementSettingType getType() {
|
||||
return ArrangementSettingType.MODIFIER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(@NotNull ArrangementModifier data,
|
||||
@NotNull ArrangementStandardSettingsAware filter,
|
||||
@Nullable ArrangementMatcherSettings settings)
|
||||
{
|
||||
return filter.isEnabled(data, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user