Merge branch 'master' into codeStyleExtractor
[idea/community.git] / platform / lang-impl / src / com / intellij / application / options / codeStyle / OptionTableWithPreviewPanel.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.application.options.codeStyle;
17
18 import com.intellij.openapi.application.ApplicationBundle;
19 import com.intellij.psi.codeStyle.*;
20 import com.intellij.ui.SpeedSearchComparator;
21 import com.intellij.ui.TreeTableSpeedSearch;
22 import com.intellij.ui.components.JBCheckBox;
23 import com.intellij.ui.components.JBScrollPane;
24 import com.intellij.ui.components.editors.JBComboBoxTableCellEditorComponent;
25 import com.intellij.ui.treeStructure.treetable.ListTreeTableModel;
26 import com.intellij.ui.treeStructure.treetable.TreeTable;
27 import com.intellij.ui.treeStructure.treetable.TreeTableCellRenderer;
28 import com.intellij.ui.treeStructure.treetable.TreeTableModel;
29 import com.intellij.util.containers.ContainerUtil;
30 import com.intellij.util.ui.AbstractTableCellEditor;
31 import com.intellij.util.ui.ColumnInfo;
32 import com.intellij.util.ui.UIUtil;
33 import gnu.trove.THashMap;
34 import gnu.trove.THashSet;
35 import org.jetbrains.annotations.NotNull;
36 import org.jetbrains.annotations.Nullable;
37
38 import javax.swing.*;
39 import javax.swing.table.TableCellEditor;
40 import javax.swing.table.TableCellRenderer;
41 import javax.swing.table.TableColumn;
42 import javax.swing.tree.*;
43 import java.awt.*;
44 import java.awt.event.ActionEvent;
45 import java.awt.event.ActionListener;
46 import java.lang.reflect.Field;
47 import java.util.*;
48 import java.util.List;
49
50 /**
51  * @author max
52  */
53 public abstract class OptionTableWithPreviewPanel extends CustomizableLanguageCodeStylePanel {
54   protected TreeTable myTreeTable;
55   private final JPanel myPanel = new JPanel();
56
57   private final List<Option> myOptions = new ArrayList<Option>();
58   private final List<Option> myCustomOptions = new ArrayList<Option>();
59   private final Set<String> myAllowedOptions = new THashSet<String>();
60   private final Map<String, String> myRenamedFields = new THashMap<String, String>();
61   private boolean myShowAllStandardOptions;
62   protected boolean isFirstUpdate = true;
63
64   public OptionTableWithPreviewPanel(CodeStyleSettings settings) {
65     super(settings);
66   }
67
68   @Override
69   protected void init() {
70     super.init();
71
72     myPanel.setLayout(new GridBagLayout());
73     initTables();
74
75     myTreeTable = createOptionsTree(getSettings());
76     myTreeTable.setBackground(UIUtil.getPanelBackground());
77     myTreeTable.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 10));
78     JBScrollPane scrollPane = new JBScrollPane(myTreeTable) {
79       @Override
80       public Dimension getMinimumSize() {
81         return super.getPreferredSize();
82       }
83     };
84     myPanel.add(scrollPane
85       , new GridBagConstraints(0, 0, 1, 1, 0, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
86                                        new Insets(0, 0, 0, 0), 0, 0));
87
88     final JPanel previewPanel = createPreviewPanel();
89     myPanel.add(previewPanel,
90                 new GridBagConstraints(1, 0, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
91                                        new Insets(0, 0, 0, 0), 0, 0));
92
93     installPreviewPanel(previewPanel);
94     addPanelToWatch(myPanel);
95
96     isFirstUpdate = false;
97     customizeSettings();
98   }
99
100   @Override
101   protected void resetDefaultNames() {
102     myRenamedFields.clear();
103   }
104
105   @Override
106   public void showAllStandardOptions() {
107     myShowAllStandardOptions = true;
108     for (Option each : myOptions) {
109       each.setEnabled(true);
110     }
111     for (Option each : myCustomOptions) {
112       each.setEnabled(false);
113     }
114   }
115
116   @Override
117   public void showStandardOptions(String... optionNames) {
118     Collections.addAll(myAllowedOptions, optionNames);
119     for (Option each : myOptions) {
120       each.setEnabled(false);
121       for (String optionName : optionNames) {
122         if (each.field.getName().equals(optionName)) {
123           each.setEnabled(true);
124         }
125       }
126     }
127     for (Option each : myCustomOptions) {
128       each.setEnabled(false);
129     }
130   }
131
132   @Override
133   public void showCustomOption(Class<? extends CustomCodeStyleSettings> settingsClass,
134                                String fieldName,
135                                String title,
136                                String groupName, Object... options) {
137     showCustomOption(settingsClass, fieldName, title, groupName, null, null, options);
138   }
139
140
141   @Override
142   public void showCustomOption(Class<? extends CustomCodeStyleSettings> settingsClass,
143                                String fieldName,
144                                String title,
145                                String groupName,
146                                @Nullable OptionAnchor anchor,
147                                @Nullable String anchorFieldName,
148                                Object... options) {
149     if (isFirstUpdate) {
150       Option option;
151       if (options.length == 2) {
152         option =
153           new SelectionOption(settingsClass, fieldName, title, groupName, anchor, anchorFieldName, (String[])options[0], (int[])options[1]);
154       }
155       else {
156         option = new BooleanOption(settingsClass, fieldName, title, groupName, anchor, anchorFieldName);
157       }
158       myCustomOptions.add(option);
159       option.setEnabled(true);
160     }
161     else {
162       for (Option each : myCustomOptions) {
163         if (each.clazz == settingsClass && each.field.getName().equals(fieldName)) {
164           each.setEnabled(true);
165         }
166       }
167     }
168   }
169
170   @Override
171   public void renameStandardOption(String fieldName, String newTitle) {
172     myRenamedFields.put(fieldName, newTitle);
173   }
174
175   protected TreeTable createOptionsTree(CodeStyleSettings settings) {
176     DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode();
177     Map<String, DefaultMutableTreeNode> groupsMap = new THashMap<String, DefaultMutableTreeNode>();
178
179     List<Option> sorted = sortOptions(ContainerUtil.concat(myOptions, myCustomOptions));
180     for (Option each : sorted) {
181       if (!(myCustomOptions.contains(each) || myAllowedOptions.contains(each.field.getName()) || myShowAllStandardOptions)) continue;
182
183       String group = each.groupName;
184       MyTreeNode newNode = new MyTreeNode(each, each.title, settings);
185
186       DefaultMutableTreeNode groupNode = groupsMap.get(group);
187       if (groupNode != null) {
188         groupNode.add(newNode);
189       }
190       else {
191         String groupName;
192
193         if (group == null) {
194           groupName = each.title;
195           groupNode = newNode;
196         }
197         else {
198           groupName = group;
199           groupNode = new DefaultMutableTreeNode(groupName);
200           groupNode.add(newNode);
201         }
202         groupsMap.put(groupName, groupNode);
203         rootNode.add(groupNode);
204       }
205     }
206
207     ListTreeTableModel model = new ListTreeTableModel(rootNode, COLUMNS);
208     TreeTable treeTable = new TreeTable(model) {
209       @Override
210       public TreeTableCellRenderer createTableRenderer(TreeTableModel treeTableModel) {
211         TreeTableCellRenderer tableRenderer = super.createTableRenderer(treeTableModel);
212         UIUtil.setLineStyleAngled(tableRenderer);
213         tableRenderer.setRootVisible(false);
214         tableRenderer.setShowsRootHandles(true);
215
216         return tableRenderer;
217       }
218
219       @Override
220       public TableCellRenderer getCellRenderer(int row, int column) {
221         TreePath treePath = getTree().getPathForRow(row);
222         if (treePath == null) return super.getCellRenderer(row, column);
223
224         Object node = treePath.getLastPathComponent();
225
226         @SuppressWarnings("unchecked")
227         TableCellRenderer renderer = COLUMNS[column].getRenderer(node);
228         return renderer == null ? super.getCellRenderer(row, column) : renderer;
229       }
230
231       @Override
232       public TableCellEditor getCellEditor(int row, int column) {
233         TreePath treePath = getTree().getPathForRow(row);
234         if (treePath == null) return super.getCellEditor(row, column);
235
236         Object node = treePath.getLastPathComponent();
237         @SuppressWarnings("unchecked")
238         TableCellEditor editor = COLUMNS[column].getEditor(node);
239         return editor == null ? super.getCellEditor(row, column) : editor;
240       }
241     };
242     new TreeTableSpeedSearch(treeTable).setComparator(new SpeedSearchComparator(false));
243
244     treeTable.setRootVisible(false);
245
246     final JTree tree = treeTable.getTree();
247     tree.setCellRenderer(myTitleRenderer);
248     tree.setShowsRootHandles(true);
249     //myTreeTable.setRowHeight(new JComboBox(new String[]{"Sample Text"}).getPreferredSize().height);
250     treeTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
251     treeTable.setTableHeader(null);
252
253     expandTree(tree);
254
255     treeTable.getColumnModel().getSelectionModel().setAnchorSelectionIndex(1);
256     treeTable.getColumnModel().getSelectionModel().setLeadSelectionIndex(1);
257
258     int maxWidth = tree.getPreferredScrollableViewportSize().width + 10;
259     final TableColumn titleColumn = treeTable.getColumnModel().getColumn(0);
260     titleColumn.setPreferredWidth(maxWidth);
261     titleColumn.setMinWidth(maxWidth);
262     titleColumn.setMaxWidth(maxWidth);
263     titleColumn.setResizable(false);
264
265     //final TableColumn levelColumn = treeTable.getColumnModel().getColumn(1);
266     //TODO[max]: better preffered size...
267     //TODO[kb]: Did I fixed it by making the last column floating?
268     //levelColumn.setPreferredWidth(valueSize.width);
269     //levelColumn.setMaxWidth(valueSize.width);
270     //levelColumn.setMinWidth(valueSize.width);
271     //levelColumn.setResizable(false);
272
273     final Dimension valueSize = new JLabel(ApplicationBundle.message("option.table.sizing.text")).getPreferredSize();
274     treeTable.setPreferredScrollableViewportSize(new Dimension(maxWidth + valueSize.width + 10, 20));
275
276     return treeTable;
277   }
278
279   private String getRenamedTitle(String fieldOrGroupName, String defaultName) {
280     String result = myRenamedFields.get(fieldOrGroupName);
281     return result == null ? defaultName : result;
282   }
283
284   public static void expandTree(final JTree tree) {
285     int oldRowCount = 0;
286     do {
287       int rowCount = tree.getRowCount();
288       if (rowCount == oldRowCount) break;
289       oldRowCount = rowCount;
290       for (int i = 0; i < rowCount; i++) {
291         tree.expandRow(i);
292       }
293     }
294     while (true);
295   }
296
297   protected abstract void initTables();
298
299   private static void resetNode(TreeNode node, CodeStyleSettings settings) {
300     if (node instanceof MyTreeNode) {
301       ((MyTreeNode)node).reset(settings);
302     }
303     for (int j = 0; j < node.getChildCount(); j++) {
304       TreeNode child = node.getChildAt(j);
305       resetNode(child, settings);
306     }
307   }
308
309   private static void applyNode(TreeNode node, final CodeStyleSettings settings) {
310     if (node instanceof MyTreeNode) {
311       ((MyTreeNode)node).apply(settings);
312     }
313     for (int j = 0; j < node.getChildCount(); j++) {
314       TreeNode child = node.getChildAt(j);
315       applyNode(child, settings);
316     }
317   }
318
319   private static boolean isModified(TreeNode node, final CodeStyleSettings settings) {
320     if (node instanceof MyTreeNode) {
321       if (((MyTreeNode)node).isModified(settings)) return true;
322     }
323     for (int j = 0; j < node.getChildCount(); j++) {
324       TreeNode child = node.getChildAt(j);
325       if (isModified(child, settings)) {
326         return true;
327       }
328     }
329     return false;
330   }
331
332   protected void addOption(@NotNull String fieldName, @NotNull String title) {
333     addOption(fieldName, title, null);
334   }
335
336   protected void addOption(@NotNull String fieldName, @NotNull String title, @NotNull String[] options, @NotNull int[] values) {
337     addOption(fieldName, title, null, options, values);
338   }
339
340   protected void addOption(@NotNull String fieldName,
341                            @NotNull String title,
342                            @Nullable String groupName,
343                            int minValue,
344                            int maxValue,
345                            int defaultValue,
346                            String defaultValueText) {
347     myOptions.add(new IntOption(null, fieldName, title, groupName, null, null, minValue, maxValue, defaultValue, defaultValueText));
348   }
349
350   protected void addOption(@NotNull String fieldName, @NotNull String title, @Nullable String groupName) {
351     myOptions.add(new BooleanOption(null, fieldName, title, groupName, null, null));
352   }
353
354   protected void addOption(@NotNull String fieldName, @NotNull String title, @Nullable String groupName,
355                            @NotNull String[] options, @NotNull int[] values) {
356     myOptions.add(new SelectionOption(null, fieldName, title, groupName, null, null, options, values));
357   }
358
359   private abstract class Option extends OrderedOption {
360     @Nullable final Class<? extends CustomCodeStyleSettings> clazz;
361     @NotNull final Field field;
362     @NotNull final String title;
363     @Nullable final String groupName;
364     private boolean myEnabled = false;
365
366     public Option(@Nullable Class<? extends CustomCodeStyleSettings> clazz,
367                   @NotNull String fieldName,
368                   @NotNull String title,
369                   @Nullable String groupName,
370                   @Nullable OptionAnchor anchor,
371                   @Nullable String anchorFiledName) {
372       super(fieldName, anchor, anchorFiledName);
373       this.clazz = clazz;
374       this.title = title;
375       this.groupName = groupName;
376
377       try {
378         Class styleSettingsClass = clazz == null ? CommonCodeStyleSettings.class : clazz;
379         this.field = styleSettingsClass.getField(fieldName);
380       }
381       catch (NoSuchFieldException e) {
382         throw new RuntimeException(e);
383       }
384     }
385
386     public void setEnabled(boolean enabled) {
387       myEnabled = enabled;
388     }
389
390     public boolean isEnabled() {
391       return myEnabled;
392     }
393
394     public abstract Object getValue(CodeStyleSettings settings);
395
396     public abstract void setValue(Object value, CodeStyleSettings settings);
397
398     protected Object getSettings(CodeStyleSettings settings) {
399       if (clazz != null) return settings.getCustomSettings(clazz);
400       return settings.getCommonSettings(getDefaultLanguage());
401     }
402   }
403
404   private class BooleanOption extends Option {
405     private BooleanOption(Class<? extends CustomCodeStyleSettings> clazz,
406                           @NotNull String fieldName,
407                           @NotNull String title,
408                           @Nullable String groupName,
409                           @Nullable OptionAnchor anchor,
410                           @Nullable String anchorFiledName) {
411       super(clazz, fieldName, title, groupName, anchor, anchorFiledName);
412     }
413
414     @Override
415     public Object getValue(CodeStyleSettings settings) {
416       try {
417         return field.getBoolean(getSettings(settings));
418       }
419       catch (IllegalAccessException ignore) {
420         return null;
421       }
422     }
423
424     @Override
425     public void setValue(Object value, CodeStyleSettings settings) {
426       try {
427         field.setBoolean(getSettings(settings), ((Boolean)value).booleanValue());
428       }
429       catch (IllegalAccessException ignored) {
430       }
431     }
432   }
433
434   private class SelectionOption extends Option {
435     @NotNull final String[] options;
436     @NotNull final int[] values;
437
438     public SelectionOption(Class<? extends CustomCodeStyleSettings> clazz,
439                            @NotNull String fieldName,
440                            @NotNull String title,
441                            @Nullable String groupName,
442                            @Nullable OptionAnchor anchor,
443                            @Nullable String anchorFiledName,
444                            @NotNull String[] options,
445                            @NotNull int[] values) {
446       super(clazz, fieldName, title, groupName, anchor, anchorFiledName);
447       this.options = options;
448       this.values = values;
449     }
450
451     @Override
452     public Object getValue(CodeStyleSettings settings) {
453       try {
454         int value = field.getInt(getSettings(settings));
455         for (int i = 0; i < values.length; i++) {
456           if (values[i] == value) return options[i];
457         }
458       }
459       catch (IllegalAccessException ignore) {
460       }
461       return null;
462     }
463
464     @Override
465     public void setValue(Object value, CodeStyleSettings settings) {
466       try {
467         for (int i = 0; i < values.length; i++) {
468           if (options[i].equals(value)) {
469             field.setInt(getSettings(settings), values[i]);
470             return;
471           }
472         }
473       }
474       catch (IllegalAccessException ignore) {
475       }
476     }
477   }
478
479   private class IntOption extends Option {
480
481     private final int myMinValue;
482     private final int myMaxValue;
483     private final int myDefaultValue;
484     @Nullable private String myDefaultValueText;
485
486     public IntOption(Class<? extends CustomCodeStyleSettings> clazz,
487                      @NotNull String fieldName,
488                      @NotNull String title,
489                      @Nullable String groupName, 
490                      @Nullable OptionAnchor anchor, 
491                      @Nullable String anchorFiledName,
492                      int minValue,
493                      int maxValue,
494                      int defaultValue,
495                      @Nullable String defaultValueText) {
496       super(clazz, fieldName, title, groupName, anchor, anchorFiledName);
497       myMinValue = minValue;
498       myMaxValue = maxValue;
499       myDefaultValue = defaultValue;
500       myDefaultValueText = defaultValueText;
501     }
502
503     @Override
504     public Object getValue(CodeStyleSettings settings) {
505       try {
506         return field.getInt(getSettings(settings));
507       }
508       catch (IllegalAccessException e) {
509         return null;
510       }      
511     }
512
513     @Override
514     public void setValue(Object value, CodeStyleSettings settings) {
515       //noinspection EmptyCatchBlock
516       try {
517         if (value instanceof Integer) {
518           field.setInt(getSettings(settings), ((Integer)value).intValue());
519         }
520         else {
521           field.setInt(getSettings(settings), myDefaultValue);
522         }
523       }
524       catch (IllegalAccessException e) {
525       }
526     }
527
528     public int getMinValue() {
529       return myMinValue;
530     }
531
532     public int getMaxValue() {
533       return myMaxValue;
534     }
535
536     public int getDefaultValue() {
537       return myDefaultValue;
538     }
539
540     public boolean isDefaultValue(Object value) {
541       return value instanceof Integer && ((Integer)value).intValue() == myDefaultValue;
542     }
543
544     @Nullable
545     public String getDefaultValueText() {
546       return myDefaultValueText;
547     }
548   }
549
550   @SuppressWarnings({"HardCodedStringLiteral"})
551   public final ColumnInfo TITLE = new ColumnInfo("TITLE") {
552     @Override
553     public Object valueOf(Object o) {
554       if (o instanceof MyTreeNode) {
555         MyTreeNode node = (MyTreeNode)o;
556         return node.getText();
557       }
558       return o.toString();
559     }
560
561     @Override
562     public Class getColumnClass() {
563       return TreeTableModel.class;
564     }
565   };
566
567   @SuppressWarnings({"HardCodedStringLiteral"})
568   public final ColumnInfo VALUE = new ColumnInfo("VALUE") {
569     private final TableCellEditor myEditor = new MyValueEditor();
570     private final TableCellRenderer myRenderer = new MyValueRenderer();
571
572     @Override
573     public Object valueOf(Object o) {
574       if (o instanceof MyTreeNode) {
575         MyTreeNode node = (MyTreeNode)o;
576         return node.getValue();
577       }
578
579       return null;
580     }
581
582     @Override
583     public TableCellRenderer getRenderer(Object o) {
584       return myRenderer;
585     }
586
587     @Override
588     public TableCellEditor getEditor(Object item) {
589       return myEditor;
590     }
591
592     @Override
593     public boolean isCellEditable(Object o) {
594       return o instanceof MyTreeNode && ((MyTreeNode)o).isEnabled();
595     }
596
597     @Override
598     public void setValue(Object o, Object o1) {
599       MyTreeNode node = (MyTreeNode)o;
600       node.setValue(o1);
601     }
602   };
603
604   public final ColumnInfo[] COLUMNS = new ColumnInfo[]{TITLE, VALUE};
605
606   private final TreeCellRenderer myTitleRenderer = new TreeCellRenderer() {
607     private final JLabel myLabel = new JLabel();
608
609     @NotNull
610     @Override
611     public Component getTreeCellRendererComponent(@NotNull JTree tree,
612                                                   Object value,
613                                                   boolean selected,
614                                                   boolean expanded,
615                                                   boolean leaf,
616                                                   int row,
617                                                   boolean hasFocus) {
618       if (value instanceof MyTreeNode) {
619         MyTreeNode node = (MyTreeNode)value;
620         myLabel.setText(getRenamedTitle(node.getKey().field.getName(), node.getText()));
621         myLabel.setFont(myLabel.getFont().deriveFont(node.getKey().groupName == null ? Font.BOLD : Font.PLAIN));
622         myLabel.setEnabled(node.isEnabled());
623       }
624       else {
625         myLabel.setText(getRenamedTitle(value.toString(), value.toString()));
626         myLabel.setFont(myLabel.getFont().deriveFont(Font.BOLD));
627         myLabel.setEnabled(true);
628       }
629
630       Color foreground = selected ? UIUtil.getTableSelectionForeground() : UIUtil.getTableForeground();
631       myLabel.setForeground(foreground);
632
633       return myLabel;
634     }
635   };
636
637   private static class MyTreeNode extends DefaultMutableTreeNode {
638     private final Option myKey;
639     private final String myText;
640     private Object myValue;
641
642     public MyTreeNode(Option key, String text, CodeStyleSettings settings) {
643       myKey = key;
644       myText = text;
645       myValue = key.getValue(settings);
646       setUserObject(myText);
647     }
648
649     public Option getKey() {
650       return myKey;
651     }
652
653     public String getText() {
654       return myText;
655     }
656
657     public Object getValue() {
658       return myValue;
659     }
660
661     public void setValue(Object value) {
662       myValue = value;
663     }
664
665     public void reset(CodeStyleSettings settings) {
666       setValue(myKey.getValue(settings));
667     }
668
669     public boolean isModified(final CodeStyleSettings settings) {
670       return !myValue.equals(myKey.getValue(settings));
671     }
672
673     public void apply(final CodeStyleSettings settings) {
674       myKey.setValue(myValue, settings);
675     }
676
677     public boolean isEnabled() {
678       return myKey.isEnabled();
679     }
680   }
681
682   private static class MyValueRenderer implements TableCellRenderer {
683     private final JLabel myComboBox = new JLabel();
684     private final JCheckBox myCheckBox = new JBCheckBox();
685     private final JPanel myEmptyLabel = new JPanel();
686     private final JLabel myIntLabel = new JLabel();
687
688     @NotNull
689     @Override
690     public Component getTableCellRendererComponent(@NotNull JTable table,
691                                                    Object value,
692                                                    boolean isSelected,
693                                                    boolean hasFocus,
694                                                    int row,
695                                                    int column) {
696       boolean isEnabled = true;
697       final DefaultMutableTreeNode node = (DefaultMutableTreeNode)((TreeTable)table).getTree().
698         getPathForRow(row).getLastPathComponent();
699       Option key = null;
700       if (node instanceof MyTreeNode) {
701         isEnabled = ((MyTreeNode)node).isEnabled();
702         key = ((MyTreeNode)node).getKey();
703       }
704       if (!table.isEnabled()) {
705         isEnabled = false;
706       }
707
708       Color background = table.getBackground();
709       if (value instanceof Boolean) {
710         myCheckBox.setSelected(((Boolean)value).booleanValue());
711         myCheckBox.setBackground(background);
712         myCheckBox.setEnabled(isEnabled);
713         return myCheckBox;
714       }
715       else if (value instanceof String) {
716         /*
717         myComboBox.removeAllItems();
718         myComboBox.addItem(value);
719         */
720         myComboBox.setText((String)value);
721         myComboBox.setBackground(background);
722         myComboBox.setEnabled(isEnabled);
723         return myComboBox;
724       }
725       else if (value instanceof Integer) {
726         if (key instanceof IntOption && ((IntOption)key).isDefaultValue(value)) {
727           myIntLabel.setText(((IntOption)key).getDefaultValueText());
728         }
729         else {
730           myIntLabel.setText(value.toString());
731         }
732         return myIntLabel;
733       }
734
735       myCheckBox.putClientProperty("JComponent.sizeVariant", "small");
736       myComboBox.putClientProperty("JComponent.sizeVariant", "small");
737
738       myEmptyLabel.setBackground(background);
739       return myEmptyLabel;
740     }
741   }
742
743   private static class MyIntOptionEditor extends JTextField {
744     private int myMinValue;
745     private int myMaxValue;
746     private int myDefaultValue;
747
748     private MyIntOptionEditor() {
749       super();
750     }
751
752     public Object getPresentableValue() {
753       return validateAndGetIntOption();
754     }
755
756     private int validateAndGetIntOption() {
757       try {
758         int value = Integer.parseInt(getText());
759         return value >= myMinValue && value <= myMaxValue ? value : myDefaultValue;
760       }
761       catch (NumberFormatException nfe) {
762         return myDefaultValue;
763       }
764     }
765
766     public void setMinValue(int minValue) {
767       myMinValue = minValue;
768     }
769
770     public void setMaxValue(int maxValue) {
771       myMaxValue = maxValue;
772     }
773
774     public void setDefaultValue(int defaultValue) {
775       myDefaultValue = defaultValue;
776     }
777   }
778
779   /**
780    * @author Konstantin Bulenkov
781    */
782   private class MyValueEditor extends AbstractTableCellEditor {
783     private final JCheckBox myBooleanEditor = new JBCheckBox();
784     private JBComboBoxTableCellEditorComponent myOptionsEditor = new JBComboBoxTableCellEditorComponent();
785     private MyIntOptionEditor myIntOptionsEditor = new MyIntOptionEditor();
786     private Component myCurrentEditor = null;
787     private MyTreeNode myCurrentNode = null;
788
789     public MyValueEditor() {
790       final ActionListener itemChoosen = new ActionListener() {
791         @Override
792         public void actionPerformed(@NotNull ActionEvent e) {
793           if (myCurrentNode != null) {
794             myCurrentNode.setValue(getCellEditorValue());
795             somethingChanged();
796           }
797         }
798       };
799       myBooleanEditor.addActionListener(itemChoosen);
800       myOptionsEditor.addActionListener(itemChoosen);
801       myBooleanEditor.putClientProperty("JComponent.sizeVariant", "small");
802       myOptionsEditor.putClientProperty("JComponent.sizeVariant", "small");
803     }
804
805     @Override
806     public Object getCellEditorValue() {
807       if (myCurrentEditor == myOptionsEditor) {
808         //new Alarm(Alarm.ThreadToUse.SWING_THREAD).addRequest(new Runnable() {
809         //                                                       @Override
810         //                                                       public void run() {
811         //                                                         somethingChanged();
812         //                                                       }
813         //                                                     }, 100);
814         return myOptionsEditor.getEditorValue();
815       }
816       else if (myCurrentEditor == myBooleanEditor) {
817         return myBooleanEditor.isSelected();
818       }
819       else if (myCurrentEditor == myIntOptionsEditor) {
820         return myIntOptionsEditor.getPresentableValue();
821       }
822
823       return null;
824     }
825
826     @Override
827     public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
828       final DefaultMutableTreeNode defaultNode = (DefaultMutableTreeNode)((TreeTable)table).getTree().
829         getPathForRow(row).getLastPathComponent();
830       myCurrentEditor = null;
831       myCurrentNode = null;
832       if (defaultNode instanceof MyTreeNode) {
833         MyTreeNode node = (MyTreeNode)defaultNode;
834         myCurrentNode = node;
835         if (node.getKey() instanceof BooleanOption) {
836           myCurrentEditor = myBooleanEditor;
837           myBooleanEditor.setSelected(node.getValue() == Boolean.TRUE);
838           myBooleanEditor.setEnabled(node.isEnabled());
839         }
840         else if (node.getKey() instanceof IntOption) {
841           IntOption intOption = (IntOption)node.getKey();
842           myCurrentEditor = myIntOptionsEditor;
843           myIntOptionsEditor.setText(intOption.isDefaultValue(node.getValue()) ? "" : node.getValue().toString());
844           myIntOptionsEditor.setMinValue(intOption.getMinValue());
845           myIntOptionsEditor.setMaxValue(intOption.getMaxValue());
846           myIntOptionsEditor.setDefaultValue(intOption.getDefaultValue());
847         }
848         else {
849           myCurrentEditor = myOptionsEditor;
850           myOptionsEditor.setCell(table, row, column);
851           myOptionsEditor.setText(String.valueOf(node.getValue()));
852           myOptionsEditor.setOptions(((SelectionOption)node.getKey()).options);
853           myOptionsEditor.setDefaultValue(node.getValue());
854         }
855       }
856
857       if (myCurrentEditor != null) {
858         myCurrentEditor.setBackground(table.getBackground());
859       }
860       return myCurrentEditor;
861     }
862   }
863
864   @Override
865   public void apply(CodeStyleSettings settings) {
866     TreeModel treeModel = myTreeTable.getTree().getModel();
867     TreeNode root = (TreeNode)treeModel.getRoot();
868     applyNode(root, settings);
869   }
870
871   @Override
872   public boolean isModified(CodeStyleSettings settings) {
873     TreeModel treeModel = myTreeTable.getTree().getModel();
874     TreeNode root = (TreeNode)treeModel.getRoot();
875     if (isModified(root, settings)) {
876       return true;
877     }
878     return false;
879   }
880
881   @Override
882   public JComponent getPanel() {
883     return myPanel;
884   }
885
886   @Override
887   protected void resetImpl(final CodeStyleSettings settings) {
888     TreeModel treeModel = myTreeTable.getTree().getModel();
889     TreeNode root = (TreeNode)treeModel.getRoot();
890     resetNode(root, settings);
891   }
892
893   @Override
894   public Set<String> processListOptions() {
895     Set<String> options = new HashSet<String>();
896     collectOptions(options, myOptions);
897     collectOptions(options, myCustomOptions);
898     return options;
899   }
900
901   private void collectOptions(Set<String> optionNames, final List<Option> optionList) {
902     for (Option option : optionList) {
903       if (option.groupName != null) {
904         optionNames.add(option.groupName);
905       }
906       optionNames.add(option.title);
907     }
908   }
909 }