diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementGroupingRulesManager.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementGroupingRulesManager.java index 96dc8071b962..e12c11c15c99 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementGroupingRulesManager.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementGroupingRulesManager.java @@ -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 groupingNode + = new ArrangementCheckBoxNode(displayManager, groupingType); + result.add(groupingNode); + List supportedOrderTypes = new ArrayList(); + for (ArrangementEntryOrderType orderType : ArrangementEntryOrderType.values()) { + if (settingsFilter.isEnabled(groupingType, orderType)) { + supportedOrderTypes.add(orderType); + } + } + if (supportedOrderTypes.isEmpty()) { + continue; + } + + final ArrangementComboBoxNode orderTypeNode + = new ArrangementComboBoxNode(displayManager, orderLabel, supportedOrderTypes); + groupingNode.add(orderTypeNode); + groupingNode.setChangeCallback(new Consumer>() { + @Override + public void consume(ArrangementCheckBoxNode node) { + orderTypeNode.setEnabled(groupingNode.isSelected()); + } + }); + groupingNode.setSelected(true); } return result; } - public void applyRules(@NotNull List rules, @NotNull TreeNode groupsRoot) { - // TODO den implement + @SuppressWarnings({"MethodMayBeStatic", "unchecked"}) + public void applyRules(@NotNull List rules, + @NotNull MutableTreeNode groupsRoot, + @NotNull DefaultTreeModel model) + { + for (int i = 0; i < groupsRoot.getChildCount(); i++) { + ArrangementCheckBoxNode child = (ArrangementCheckBoxNode)groupsRoot.getChildAt(i); + child.setSelected(false); + } + for (int i = 0; i < rules.size(); i++) { + ArrangementGroupingRule rule = rules.get(i); + int index = getIndex(groupsRoot, rule.getRule()); + if (index < 0) { + return; + } + + ArrangementCheckBoxNode child = (ArrangementCheckBoxNode)groupsRoot.getChildAt(index); + child.setSelected(true); + + if (i != index) { + model.removeNodeFromParent(child); + int insertionIndex = i < index ? i : i - 1; + model.insertNodeInto(child, groupsRoot, insertionIndex); + } + + if (child.getChildCount() > 0) { + ArrangementComboBoxNode orderTypeNode = + (ArrangementComboBoxNode)child.getChildAt(0); + orderTypeNode.setSelectedValue(rule.getOrderType()); + } + } } + private static int getIndex(@NotNull TreeNode root, @NotNull Object data) { + for (int i = 0; i < root.getChildCount(); i++) { + TreeNode child = root.getChildAt(i); + if (child instanceof ArrangementCheckBoxNode && data.equals(((ArrangementCheckBoxNode)child).getValue())) { + return i; + } + } + return -1; + } + + @SuppressWarnings({"unchecked", "MethodMayBeStatic"}) @NotNull public List buildRules(@NotNull TreeNode groupsRoot) { List result = new ArrayList(); - // TODO den implement + for (int i = 0; i < groupsRoot.getChildCount(); i++) { + ArrangementCheckBoxNode groupNode = + (ArrangementCheckBoxNode)groupsRoot.getChildAt(i); + if (!groupNode.isSelected()) { + continue; + } + ArrangementGroupingType groupingType = groupNode.getValue(); + if (groupNode.getChildCount() < 1) { + result.add(new ArrangementGroupingRule(groupingType)); + continue; + } + + ArrangementComboBoxNode orderNode = + (ArrangementComboBoxNode)groupNode.getChildAt(0); + ArrangementEntryOrderType orderType = orderNode.getSelectedValue(); + result.add(new ArrangementGroupingRule(groupingType, orderType)); + } return result; } } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleTree.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleTree.java index 7d0639b433cc..38d8b5c2c725 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleTree.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/ArrangementRuleTree.java @@ -15,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 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() { diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/ArrangementCheckBoxNode.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/ArrangementCheckBoxNode.java index c9a4babf415f..a12ad7f36fc3 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/ArrangementCheckBoxNode.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/ArrangementCheckBoxNode.java @@ -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 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> 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> listener) { + myListener = listener; + } + + @Override + public String toString() { + return myData.toString(); + } } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/ArrangementComboBoxNode.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/ArrangementComboBoxNode.java new file mode 100644 index 000000000000..4b561e10c4a5 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/ArrangementComboBoxNode.java @@ -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 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 myData = new ArrayList(); + + public ArrangementComboBoxNode(@NotNull ArrangementNodeDisplayManager displayManager, @NotNull String text, @NotNull Iterable 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(); + } +} diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/ArrangementEditableNode.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/ArrangementEditableNode.java new file mode 100644 index 000000000000..3d500a909c12 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/arrangement/node/ArrangementEditableNode.java @@ -0,0 +1,31 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.application.options.codeStyle.arrangement.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(); +} diff --git a/platform/platform-resources-en/src/messages/ApplicationBundle.properties b/platform/platform-resources-en/src/messages/ApplicationBundle.properties index 38f26bd105d3..e73b969af090 100644 --- a/platform/platform-resources-en/src/messages/ApplicationBundle.properties +++ b/platform/platform-resources-en/src/messages/ApplicationBundle.properties @@ -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 \ No newline at end of file