IDEA-51893: Quick Fix "​Create Field" when using Groovy named parameters
[idea/community.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / annotator / intentions / dynamic / ui / DynamicMethodDialog.java
1 /*
2  * Copyright 2000-2009 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 org.jetbrains.plugins.groovy.annotator.intentions.dynamic.ui;
17
18 import com.intellij.psi.PsiType;
19 import com.intellij.util.IncorrectOperationException;
20 import com.intellij.util.ui.AbstractTableCellEditor;
21 import com.intellij.util.ui.ColumnInfo;
22 import com.intellij.util.ui.ListTableModel;
23 import org.jetbrains.plugins.groovy.GroovyBundle;
24 import org.jetbrains.plugins.groovy.annotator.intentions.QuickfixUtil;
25 import org.jetbrains.plugins.groovy.annotator.intentions.dynamic.MyPair;
26 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
27 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
28 import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrReferenceExpression;
29 import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.GroovyExpectedTypesUtil;
30
31 import javax.swing.*;
32 import javax.swing.event.CellEditorListener;
33 import javax.swing.event.ChangeEvent;
34 import javax.swing.event.TableModelEvent;
35 import javax.swing.event.TableModelListener;
36 import javax.swing.table.TableColumn;
37 import java.awt.*;
38 import java.util.EventObject;
39 import java.util.List;
40
41 /**
42  * User: Dmitry.Krasilschikov
43  * Date: 18.02.2008
44  */
45 public class DynamicMethodDialog extends DynamicDialog {
46   private final GrReferenceExpression myReferenceExpression;
47
48   public DynamicMethodDialog(GrReferenceExpression referenceExpression) {
49     super(referenceExpression, QuickfixUtil.createSettings(referenceExpression), GroovyExpectedTypesUtil.calculateTypeConstraints((GrExpression)referenceExpression.getParent()));
50     myReferenceExpression = referenceExpression;
51     assert getSettings().isMethod();
52
53     final List<MyPair> pairs = getSettings().getPairs();
54     setupParameterTable(pairs);
55     setupParameterList(pairs);
56     setTitle(GroovyBundle.message("add.dynamic.method"));
57     setUpTypeLabel(GroovyBundle.message("dynamic.method.return.type"));
58   }
59
60   protected void setUpTableNameLabel(String text) {
61     super.setUpTableNameLabel(getSettings().getPairs().isEmpty() ? GroovyBundle.message("dynamic.properties.table.no.arguments") : text);
62   }
63
64   private void setupParameterTable(final List<MyPair> pairs) {
65     final JTable table = getParametersTable();
66
67     MySuggestedNameCellEditor suggestedNameCellEditor = new MySuggestedNameCellEditor(QuickfixUtil.getArgumentsNames(pairs));
68     table.setDefaultEditor(String.class, suggestedNameCellEditor);
69
70     suggestedNameCellEditor.addCellEditorListener(new CellEditorListener() {
71       public void editingStopped(ChangeEvent e) {
72         final int editingColumn = table.getSelectedColumn();
73         if (editingColumn != 0) return;
74
75         final int editingRow = table.getSelectedRow();
76         if (editingRow < 0 || editingRow >= pairs.size()) return;
77
78         String newNameValue = ((MySuggestedNameCellEditor)e.getSource()).getCellEditorValue();
79
80         final MyPair editingPair = pairs.get(editingRow);
81         editingPair.setFirst(newNameValue);
82       }
83
84       public void editingCanceled(ChangeEvent e) {
85       }
86     });
87   }
88
89   protected boolean isTableVisible() {
90     return true;
91   }
92
93   private void setupParameterList(List<MyPair> arguments) {
94     final JTable table = getParametersTable();
95
96     //TODO: add header
97     final ListTableModel<MyPair> dataModel = new ListTableModel<MyPair>(new NameColumnInfo(), new TypeColumnInfo());
98     dataModel.addTableModelListener(new TableModelListener() {
99       public void tableChanged(TableModelEvent e) {
100         fireDataChanged();
101       }
102     });
103     dataModel.setItems(arguments);
104     table.setModel(dataModel);
105     if (!arguments.isEmpty()) {
106       String max0 = arguments.get(0).first;
107       String max1 = arguments.get(0).second;
108       for (MyPair argument : arguments) {
109         if (argument.first.length() > max0.length()) max0 = argument.first;
110         if (argument.second.length() > max1.length()) max1 = argument.second;
111       }
112
113       final FontMetrics metrics = table.getFontMetrics(table.getFont());
114       final TableColumn column0 = table.getColumnModel().getColumn(0);
115       column0.setPreferredWidth(metrics.stringWidth(max0 + "  "));
116
117       final TableColumn column1 = table.getColumnModel().getColumn(1);
118       column1.setPreferredWidth(metrics.stringWidth(max1 + "  "));
119     }
120   }
121
122
123   private class TypeColumnInfo extends ColumnInfo<MyPair, String> {
124     public TypeColumnInfo() {
125       super(GroovyBundle.message("dynamic.name"));
126     }
127
128     public String valueOf(MyPair pair) {
129       return pair.second;
130     }
131
132     public boolean isCellEditable(MyPair stringPsiTypeMyPair) {
133       return false;
134     }
135
136     public void setValue(MyPair pair, String value) {
137       PsiType type;
138       try {
139         type = GroovyPsiElementFactory.getInstance(getProject()).createTypeElement(value).getType();
140       }
141       catch (IncorrectOperationException e) {
142         return;
143       }
144
145       if (type == null) return;
146       pair.setSecond(type.getCanonicalText());
147     }
148   }
149
150   private static class NameColumnInfo extends ColumnInfo<MyPair, String> {
151     public NameColumnInfo() {
152       super(GroovyBundle.message("dynamic.type"));
153     }
154
155     public boolean isCellEditable(MyPair myPair) {
156       return true;
157     }
158
159     public String valueOf(MyPair pair) {
160       return pair.first;
161     }
162   }
163
164   protected void updateOkStatus() {
165     super.updateOkStatus();
166
167     if (getParametersTable().isEditing()) setOKActionEnabled(false);
168   }
169
170   private static class MySuggestedNameCellEditor extends AbstractTableCellEditor {
171     JTextField myNameField;
172
173     public MySuggestedNameCellEditor(String[] names) {
174       myNameField = names.length == 0 ? new JTextField() : new JTextField(names[0]);
175     }
176
177     public String getCellEditorValue() {
178       return myNameField.getText();
179     }
180
181     public boolean isCellEditable(EventObject e) {
182       return true;
183     }
184
185     public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
186       if (value instanceof String) {
187         myNameField.setText((String)value);
188       }
189       return myNameField;
190     }
191   }
192 }