IDEA-19061 Integrate the Rearranger-plugin into core-IDE

Grouping rules UI
This commit is contained in:
Denis.Zhdanov
2012-09-27 22:14:47 +04:00
parent 2529cf996c
commit fd84b9a936
6 changed files with 312 additions and 30 deletions
@@ -16,17 +16,20 @@
package com.intellij.application.options.codeStyle.arrangement;
import com.intellij.application.options.codeStyle.arrangement.node.ArrangementCheckBoxNode;
import com.intellij.application.options.codeStyle.arrangement.node.ArrangementComboBoxNode;
import com.intellij.application.options.codeStyle.arrangement.node.ArrangementSectionNode;
import com.intellij.openapi.application.ApplicationBundle;
import com.intellij.psi.codeStyle.arrangement.group.ArrangementGroupingRule;
import com.intellij.psi.codeStyle.arrangement.group.ArrangementGroupingType;
import com.intellij.psi.codeStyle.arrangement.order.ArrangementEntryOrderType;
import com.intellij.psi.codeStyle.arrangement.settings.ArrangementStandardSettingsAware;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
@@ -48,24 +51,102 @@ public class ArrangementGroupingRulesManager {
@NotNull ArrangementNodeDisplayManager displayManager)
{
ArrangementSectionNode result = new ArrangementSectionNode(ApplicationBundle.message("arrangement.settings.section.groups"));
String orderLabel = ApplicationBundle.message("arrangement.settings.label.order") + ":";
for (ArrangementGroupingType groupingType : ArrangementGroupingType.values()) {
if (!settingsFilter.isEnabled(groupingType, null)) {
continue;
}
ArrangementCheckBoxNode groupingCheckNode = new ArrangementCheckBoxNode(displayManager.getDisplayValue(groupingType));
result.add(groupingCheckNode);
final ArrangementCheckBoxNode<ArrangementGroupingType> groupingNode
= new ArrangementCheckBoxNode<ArrangementGroupingType>(displayManager, groupingType);
result.add(groupingNode);
List<ArrangementEntryOrderType> supportedOrderTypes = new ArrayList<ArrangementEntryOrderType>();
for (ArrangementEntryOrderType orderType : ArrangementEntryOrderType.values()) {
if (settingsFilter.isEnabled(groupingType, orderType)) {
supportedOrderTypes.add(orderType);
}
}
if (supportedOrderTypes.isEmpty()) {
continue;
}
final ArrangementComboBoxNode<ArrangementEntryOrderType> orderTypeNode
= new ArrangementComboBoxNode<ArrangementEntryOrderType>(displayManager, orderLabel, supportedOrderTypes);
groupingNode.add(orderTypeNode);
groupingNode.setChangeCallback(new Consumer<ArrangementCheckBoxNode<ArrangementGroupingType>>() {
@Override
public void consume(ArrangementCheckBoxNode<ArrangementGroupingType> node) {
orderTypeNode.setEnabled(groupingNode.isSelected());
}
});
groupingNode.setSelected(true);
}
return result;
}
public void applyRules(@NotNull List<ArrangementGroupingRule> rules, @NotNull TreeNode groupsRoot) {
// TODO den implement
@SuppressWarnings({"MethodMayBeStatic", "unchecked"})
public void applyRules(@NotNull List<ArrangementGroupingRule> rules,
@NotNull MutableTreeNode groupsRoot,
@NotNull DefaultTreeModel model)
{
for (int i = 0; i < groupsRoot.getChildCount(); i++) {
ArrangementCheckBoxNode<?> child = (ArrangementCheckBoxNode<?>)groupsRoot.getChildAt(i);
child.setSelected(false);
}
for (int i = 0; i < rules.size(); i++) {
ArrangementGroupingRule rule = rules.get(i);
int index = getIndex(groupsRoot, rule.getRule());
if (index < 0) {
return;
}
ArrangementCheckBoxNode<?> child = (ArrangementCheckBoxNode<?>)groupsRoot.getChildAt(index);
child.setSelected(true);
if (i != index) {
model.removeNodeFromParent(child);
int insertionIndex = i < index ? i : i - 1;
model.insertNodeInto(child, groupsRoot, insertionIndex);
}
if (child.getChildCount() > 0) {
ArrangementComboBoxNode<ArrangementEntryOrderType> orderTypeNode =
(ArrangementComboBoxNode<ArrangementEntryOrderType>)child.getChildAt(0);
orderTypeNode.setSelectedValue(rule.getOrderType());
}
}
}
private static int getIndex(@NotNull TreeNode root, @NotNull Object data) {
for (int i = 0; i < root.getChildCount(); i++) {
TreeNode child = root.getChildAt(i);
if (child instanceof ArrangementCheckBoxNode<?> && data.equals(((ArrangementCheckBoxNode)child).getValue())) {
return i;
}
}
return -1;
}
@SuppressWarnings({"unchecked", "MethodMayBeStatic"})
@NotNull
public List<ArrangementGroupingRule> buildRules(@NotNull TreeNode groupsRoot) {
List<ArrangementGroupingRule> result = new ArrayList<ArrangementGroupingRule>();
// TODO den implement
for (int i = 0; i < groupsRoot.getChildCount(); i++) {
ArrangementCheckBoxNode<ArrangementGroupingType> groupNode =
(ArrangementCheckBoxNode<ArrangementGroupingType>)groupsRoot.getChildAt(i);
if (!groupNode.isSelected()) {
continue;
}
ArrangementGroupingType groupingType = groupNode.getValue();
if (groupNode.getChildCount() < 1) {
result.add(new ArrangementGroupingRule(groupingType));
continue;
}
ArrangementComboBoxNode<ArrangementEntryOrderType> orderNode =
(ArrangementComboBoxNode<ArrangementEntryOrderType>)groupNode.getChildAt(0);
ArrangementEntryOrderType orderType = orderNode.getSelectedValue();
result.add(new ArrangementGroupingRule(groupingType, orderType));
}
return result;
}
}
@@ -15,6 +15,7 @@
*/
package com.intellij.application.options.codeStyle.arrangement;
import com.intellij.application.options.codeStyle.arrangement.node.ArrangementEditableNode;
import com.intellij.application.options.codeStyle.arrangement.node.ArrangementRepresentationAwareNode;
import com.intellij.application.options.codeStyle.arrangement.node.ArrangementSectionNode;
import com.intellij.application.options.codeStyle.arrangement.node.match.ArrangementMatchNodeComponent;
@@ -205,16 +206,22 @@ public class ArrangementRuleTree {
setSettings(settings);
myTree.setShowsRootHandles(false);
myTree.setCellRenderer(new MyCellRenderer());
TreeUI ui = myTree.getUI();
if (!(ui instanceof WideSelectionTreeUI)) {
// IJ wide selection tree ui is not applied for some LAFs (e.g. GTK) and default wide selection tree ui doesn't allow
// to customize selection background appliance algorithm (don't draw background for rule components; do draw for
// 'new rule' node).
// That's why we forcibly apply UI with custom selection strategy here.
// P.S. 'wide selection tree ui' appliance decision is taken at the com.intellij.ui.treeStructure.Tree.setUI().
myTree.setUI(new WideSelectionTreeUI(true, wideSelectionCondition));
}
MyCellRenderer renderer = new MyCellRenderer();
myTree.setCellRenderer(renderer);
myTree.setEditable(true);
myTree.setCellEditor(new MyCellEditor(myTree, renderer));
// IJ wide selection tree ui is not applied for some LAFs (e.g. GTK) and default wide selection tree ui doesn't allow
// to customize selection background appliance algorithm (don't draw background for rule components; do draw for
// 'new rule' node).
// That's why we forcibly apply UI with custom selection strategy here.
// P.S. 'wide selection tree ui' appliance decision is taken at the com.intellij.ui.treeStructure.Tree.setUI().
myTree.setUI(new WideSelectionTreeUI(true, wideSelectionCondition) {
@Override
protected boolean shouldPaintExpandControl(TreePath path, int row, boolean isExpanded, boolean hasBeenExpanded, boolean isLeaf) {
return false;
}
});
}
public void updateCanvasWidth(final int width) {
@@ -464,7 +471,7 @@ public class ArrangementRuleTree {
}
if (myGroupingsRoot != null) {
myGroupingRulesManager.applyRules(settings.getGroupings(), myGroupingsRoot);
myGroupingRulesManager.applyRules(settings.getGroupings(), myGroupingsRoot, myTreeModel);
}
List<StdArrangementMatchRule> rules = settings.getRules();
@@ -670,7 +677,7 @@ public class ArrangementRuleTree {
if (parent != null) parent.remove(EMPTY_RENDERER);
}
private class MyCellRenderer implements TreeCellRenderer {
private class MyCellRenderer extends DefaultTreeCellRenderer {
@Override
public Component getTreeCellRendererComponent(JTree tree,
Object value,
@@ -702,6 +709,35 @@ public class ArrangementRuleTree {
}
}
private static class MyCellEditor extends DefaultTreeCellEditor {
MyCellEditor(JTree tree, DefaultTreeCellRenderer renderer) {
super(tree, renderer);
}
@Override
public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) {
if (value instanceof ArrangementEditableNode) {
return ((ArrangementEditableNode)value).getEditor();
}
else {
return super.getTreeCellEditorComponent(tree, value, isSelected, expanded, leaf, row);
}
}
@Override
public boolean isCellEditable(EventObject event) {
if (event instanceof MouseEvent) {
MouseEvent mouseEvent = (MouseEvent)event;
TreePath path = tree.getPathForLocation(mouseEvent.getX(), mouseEvent.getY());
if (path == null) {
return false;
}
return path.getLastPathComponent() instanceof ArrangementEditableNode;
}
return false;
}
}
private class MySelectionModel extends DefaultTreeSelectionModel {
MySelectionModel() {
@@ -15,30 +15,48 @@
*/
package com.intellij.application.options.codeStyle.arrangement.node;
import com.intellij.application.options.codeStyle.arrangement.ArrangementNodeDisplayManager;
import com.intellij.application.options.codeStyle.arrangement.ArrangementTreeNode;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.util.Consumer;
import com.intellij.util.ui.GridBag;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
/**
* @author Denis Zhdanov
* @since 9/27/12 3:10 PM
*/
public class ArrangementCheckBoxNode extends ArrangementTreeNode implements ArrangementRepresentationAwareNode {
public class ArrangementCheckBoxNode<T> extends ArrangementTreeNode
implements ArrangementRepresentationAwareNode, ArrangementEditableNode
{
@NotNull private final JPanel myRenderer = new JPanel(new GridBagLayout());
@NotNull private final JBCheckBox myCheckBox = new JBCheckBox();
@NotNull private final JPanel myRenderer = new JPanel(new GridBagLayout());
@NotNull private final T myData;
public ArrangementCheckBoxNode(@NotNull String text) {
@Nullable private Consumer<ArrangementCheckBoxNode<T>> myListener;
public ArrangementCheckBoxNode(@NotNull ArrangementNodeDisplayManager displayManager, @NotNull T data) {
super(null);
JBCheckBox checkBox = new JBCheckBox(text);
checkBox.setBackground(UIUtil.getTreeBackground());
myRenderer.add(checkBox, new GridBag().anchor(GridBagConstraints.WEST));
myCheckBox.setText(displayManager.getDisplayValue(data));
myData = data;
myCheckBox.setBackground(UIUtil.getTreeBackground());
myRenderer.add(myCheckBox, new GridBag().anchor(GridBagConstraints.WEST));
myCheckBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (myListener != null) {
myListener.consume(ArrangementCheckBoxNode.this);
}
}
});
}
@NotNull
@@ -46,4 +64,32 @@ public class ArrangementCheckBoxNode extends ArrangementTreeNode implements Arr
public JComponent getRenderer() {
return myRenderer;
}
@NotNull
@Override
public JComponent getEditor() {
return myRenderer;
}
public boolean isSelected() {
return myCheckBox.isSelected();
}
public void setSelected(boolean selected) {
myCheckBox.setSelected(selected);
}
@NotNull
public T getValue() {
return myData;
}
public void setChangeCallback(@Nullable Consumer<ArrangementCheckBoxNode<T>> listener) {
myListener = listener;
}
@Override
public String toString() {
return myData.toString();
}
}
@@ -0,0 +1,87 @@
/*
* 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.node;
import com.intellij.application.options.codeStyle.arrangement.ArrangementNodeDisplayManager;
import com.intellij.application.options.codeStyle.arrangement.ArrangementTreeNode;
import com.intellij.util.ui.GridBag;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author Denis Zhdanov
* @since 9/27/12 5:01 PM
*/
public class ArrangementComboBoxNode<T> extends ArrangementTreeNode implements ArrangementRepresentationAwareNode, ArrangementEditableNode {
@NotNull private final JPanel myControl = new JPanel(new GridBagLayout());
@NotNull private final JLabel myLabel = new JLabel();
@NotNull private final JComboBox myComboBox = new JComboBox();
@NotNull private final List<T> myData = new ArrayList<T>();
public ArrangementComboBoxNode(@NotNull ArrangementNodeDisplayManager displayManager, @NotNull String text, @NotNull Iterable<T> data) {
super(null);
for (T t : data) {
myData.add(t);
myComboBox.addItem(displayManager.getDisplayValue(t));
}
myLabel.setText(text);
myControl.add(myLabel, new GridBag().setColumn(0).insets(0, 3, 0, 0));
myControl.add(myComboBox, new GridBag().setColumn(1));
}
@NotNull
@Override
public JComponent getEditor() {
return myControl;
}
@NotNull
@Override
public JComponent getRenderer() {
return myControl;
}
public void setEnabled(boolean enabled) {
myLabel.setEnabled(enabled);
myComboBox.setEnabled(enabled);
}
@NotNull
public T getSelectedValue() {
int index = myComboBox.getSelectedIndex();
if (index < 0 || index >= myData.size()) {
return myData.get(0);
}
return myData.get(index);
}
public void setSelectedValue(@NotNull T value) {
int i = myData.indexOf(value);
if (i >= 0) {
myComboBox.setSelectedIndex(i);
}
}
@Override
public String toString() {
return myData.toString();
}
}
@@ -0,0 +1,31 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.application.options.codeStyle.arrangement.node;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import javax.swing.tree.TreeNode;
/**
* @author Denis Zhdanov
* @since 9/27/12 4:44 PM
*/
public interface ArrangementEditableNode extends TreeNode {
@NotNull
JComponent getEditor();
}
@@ -565,10 +565,11 @@ arrangement.settings.section.match=Matching rules
arrangement.settings.groups.dependent.methods=Keep dependent methods together
arrangement.settings.groups.overridden.methods=Keep overridden methods together
arrangement.settings.groups.getters.and.setters.together=Keep getters and setters together
arrangement.settings.order.type.keep=Keep
arrangement.settings.order.type.by.name=By name
arrangement.settings.order.type.depth.first=Depth-first
arrangement.settings.order.type.breadth.first=Breadth-first
arrangement.settings.order.type.keep=keep
arrangement.settings.order.type.by.name=by name
arrangement.settings.order.type.depth.first=depth-first
arrangement.settings.order.type.breadth.first=breadth-first
arrangement.settings.label.order=order
checkbox.spaces.around.lambda.arrow=Lambda arrow
checkbox.spaces.around.method.ref.dbl.colon.arrow=Method reference double colon