ComponentWithBrowseButton - optional remove listener on hide
[fedora-idea.git] / java / java-impl / src / com / intellij / refactoring / util / ParameterTablePanel.java
blobfcdde9be0f3539e83f062b8f2d550a640107f075
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.intellij.refactoring.util;
18 import com.intellij.openapi.project.Project;
19 import com.intellij.psi.*;
20 import com.intellij.refactoring.ui.TypeSelector;
21 import com.intellij.refactoring.ui.TypeSelectorManager;
22 import com.intellij.refactoring.ui.TypeSelectorManagerImpl;
23 import com.intellij.ui.BooleanTableCellRenderer;
24 import com.intellij.ui.ScrollPaneFactory;
25 import com.intellij.ui.TableUtil;
26 import com.intellij.ui.UIBundle;
27 import com.intellij.util.ui.AbstractTableCellEditor;
28 import com.intellij.util.ui.Table;
29 import org.jetbrains.annotations.NonNls;
31 import javax.swing.*;
32 import javax.swing.event.ListSelectionEvent;
33 import javax.swing.event.ListSelectionListener;
34 import javax.swing.table.AbstractTableModel;
35 import javax.swing.table.DefaultTableCellRenderer;
36 import javax.swing.table.TableCellEditor;
37 import java.awt.*;
38 import java.awt.event.ActionEvent;
39 import java.awt.event.ActionListener;
40 import java.awt.event.KeyEvent;
41 import java.util.ArrayList;
43 public abstract class ParameterTablePanel extends JPanel {
44 private final Project myProject;
45 private final VariableData[] myVariableData;
46 private final TypeSelector[] myParameterTypeSelectors;
48 private final Table myTable;
49 private final MyTableModel myTableModel;
50 private final JButton myUpButton;
51 private final JButton myDownButton;
52 private final JComboBox myTypeRendererCombo;
54 public VariableData[] getVariableData() {
55 return myVariableData;
58 public static class VariableData {
59 public final PsiVariable variable;
60 public PsiType type;
61 public String name;
62 public boolean passAsParameter;
64 public VariableData(PsiVariable var) {
65 variable = var;
66 type = var.getType();
69 public VariableData(PsiVariable var, PsiType type) {
70 variable = var;
71 this.type = type;
75 protected abstract void updateSignature();
77 protected abstract void doEnterAction();
79 protected abstract void doCancelAction();
81 protected boolean areTypesDirected() {
82 return true;
85 public ParameterTablePanel(Project project, VariableData[] variableData, final PsiElement... scopeElements) {
86 super(new BorderLayout());
87 myProject = project;
88 myVariableData = variableData;
90 myTableModel = new MyTableModel();
91 myTable = new Table(myTableModel);
92 DefaultCellEditor defaultEditor = (DefaultCellEditor)myTable.getDefaultEditor(Object.class);
93 defaultEditor.setClickCountToStart(1);
95 myTable.setTableHeader(null);
96 myTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
97 myTable.getColumnModel().getColumn(MyTableModel.CHECKMARK_COLUMN).setCellRenderer(new CheckBoxTableCellRenderer());
98 myTable.getColumnModel().getColumn(MyTableModel.CHECKMARK_COLUMN).setMaxWidth(new JCheckBox().getPreferredSize().width);
99 myTable.getColumnModel().getColumn(MyTableModel.PARAMETER_NAME_COLUMN).setCellRenderer(new DefaultTableCellRenderer() {
100 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
101 super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
102 VariableData data = getVariableData()[row];
103 setText(data.name);
104 return this;
108 myParameterTypeSelectors = new TypeSelector[getVariableData().length];
109 for (int i = 0; i < myParameterTypeSelectors.length; i++) {
110 final PsiExpression[] occurrences = findVariableOccurrences(scopeElements, getVariableData()[i].variable);
111 final TypeSelectorManager manager = new TypeSelectorManagerImpl(myProject, getVariableData()[i].type, occurrences, areTypesDirected());
112 myParameterTypeSelectors[i] = manager.getTypeSelector();
113 getVariableData()[i].type = myParameterTypeSelectors[i].getSelectedType(); //reverse order
116 myTypeRendererCombo = new JComboBox(getVariableData());
117 myTypeRendererCombo.setOpaque(true);
118 myTypeRendererCombo.setBorder(null);
120 myTypeRendererCombo.setRenderer(new DefaultListCellRenderer() {
122 public Component getListCellRendererComponent(final JList list,
123 final Object value,
124 final int index, final boolean isSelected, final boolean cellHasFocus) {
125 setText(((VariableData)value).type.getPresentableText());
126 return this;
130 myTable.getColumnModel().getColumn(MyTableModel.PARAMETER_TYPE_COLUMN).setCellEditor(new AbstractTableCellEditor() {
131 TypeSelector myCurrentSelector;
132 public Object getCellEditorValue() {
133 return myCurrentSelector.getSelectedType();
136 public Component getTableCellEditorComponent(final JTable table,
137 final Object value,
138 final boolean isSelected,
139 final int row,
140 final int column) {
141 myCurrentSelector = myParameterTypeSelectors[row];
142 return myCurrentSelector.getComponent();
146 myTable.getColumnModel().getColumn(MyTableModel.PARAMETER_TYPE_COLUMN).setCellRenderer(new DefaultTableCellRenderer() {
147 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
148 if (myParameterTypeSelectors[row].getComponent() instanceof JComboBox) {
149 myTypeRendererCombo.setSelectedIndex(row);
150 return myTypeRendererCombo;
153 super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
154 VariableData data = getVariableData()[row];
155 setText(data.type.getPresentableText());
156 return this;
160 myTable.setPreferredScrollableViewportSize(new Dimension(250, myTable.getRowHeight() * 5));
161 myTable.setShowGrid(false);
162 myTable.setIntercellSpacing(new Dimension(0, 0));
163 @NonNls final InputMap inputMap = myTable.getInputMap();
164 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "enable_disable");
165 @NonNls final ActionMap actionMap = myTable.getActionMap();
166 actionMap.put("enable_disable", new AbstractAction() {
167 public void actionPerformed(ActionEvent e) {
168 if (myTable.isEditing()) return;
169 int[] rows = myTable.getSelectedRows();
170 if (rows.length > 0) {
171 boolean valueToBeSet = false;
172 for (int row : rows) {
173 if (!getVariableData()[row].passAsParameter) {
174 valueToBeSet = true;
175 break;
178 for (int row : rows) {
179 getVariableData()[row].passAsParameter = valueToBeSet;
181 myTableModel.fireTableRowsUpdated(rows[0], rows[rows.length - 1]);
182 TableUtil.selectRows(myTable, rows);
186 // F2 should edit the name
187 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0), "edit_parameter_name");
188 actionMap.put("edit_parameter_name", new AbstractAction() {
189 public void actionPerformed(ActionEvent e) {
190 if (!myTable.isEditing()) {
191 int row = myTable.getSelectedRow();
192 if (row >= 0 && row < myTableModel.getRowCount()) {
193 TableUtil.editCellAt(myTable, row, MyTableModel.PARAMETER_NAME_COLUMN);
199 // make ENTER work when the table has focus
200 inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "invokeImpl");
201 actionMap.put("invokeImpl", new AbstractAction() {
202 public void actionPerformed(ActionEvent e) {
203 TableCellEditor editor = myTable.getCellEditor();
204 if (editor != null) {
205 editor.stopCellEditing();
207 else {
208 doEnterAction();
213 // make ESCAPE work when the table has focus
214 actionMap.put("doCancel", new AbstractAction() {
215 public void actionPerformed(ActionEvent e) {
216 TableCellEditor editor = myTable.getCellEditor();
217 if (editor != null) {
218 editor.stopCellEditing();
220 else {
221 doCancelAction();
226 JPanel listPanel = new JPanel(new BorderLayout());
227 JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(myTable);
228 listPanel.add(scrollPane, BorderLayout.CENTER);
229 listPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
230 add(listPanel, BorderLayout.CENTER);
232 JPanel buttonsPanel = new JPanel();
233 buttonsPanel.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
234 add(buttonsPanel, BorderLayout.EAST);
236 buttonsPanel.setLayout(new GridBagLayout());
237 GridBagConstraints gbConstraints = new GridBagConstraints();
238 gbConstraints.gridwidth = GridBagConstraints.REMAINDER;
239 gbConstraints.fill = GridBagConstraints.HORIZONTAL;
240 gbConstraints.insets = new Insets(2, 4, 2, 4);
242 myUpButton = new JButton();
243 myUpButton.setText(UIBundle.message("row.move.up"));
244 myUpButton.setDefaultCapable(false);
245 buttonsPanel.add(myUpButton, gbConstraints);
247 myDownButton = new JButton();
248 myDownButton.setText(UIBundle.message("row.move.down"));
249 myDownButton.setDefaultCapable(false);
250 buttonsPanel.add(myDownButton, gbConstraints);
252 gbConstraints.weighty = 1;
253 buttonsPanel.add(new JPanel(), gbConstraints);
255 myUpButton.addActionListener(new ActionListener() {
256 public void actionPerformed(ActionEvent e) {
257 if (myTable.isEditing()) {
258 final boolean isStopped = myTable.getCellEditor().stopCellEditing();
259 if (!isStopped) return;
261 moveSelectedItem(-1);
262 updateSignature();
263 myTable.requestFocus();
267 myDownButton.addActionListener(new ActionListener() {
268 public void actionPerformed(ActionEvent e) {
269 if (myTable.isEditing()) {
270 final boolean isStopped = myTable.getCellEditor().stopCellEditing();
271 if (!isStopped) return;
273 moveSelectedItem(+1);
274 updateSignature();
275 myTable.requestFocus();
279 myTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
280 public void valueChanged(ListSelectionEvent e) {
281 updateMoveButtons();
284 if (getVariableData().length <= 1) {
285 myUpButton.setEnabled(false);
286 myDownButton.setEnabled(false);
288 else {
289 myTable.getSelectionModel().setSelectionInterval(0, 0);
291 updateMoveButtons();
294 private static PsiExpression[] findVariableOccurrences(final PsiElement[] scopeElements, final PsiVariable variable) {
295 final ArrayList<PsiExpression> result = new ArrayList<PsiExpression>();
296 for (final PsiElement element : scopeElements) {
297 element.accept(new JavaRecursiveElementWalkingVisitor() {
298 @Override public void visitReferenceExpression(final PsiReferenceExpression expression) {
299 super.visitReferenceExpression(expression);
300 if (!expression.isQualified() && expression.isReferenceTo(variable)) {
301 result.add(expression);
306 return result.toArray(new PsiExpression[result.size()]);
309 private void updateMoveButtons() {
310 int row = myTable.getSelectedRow();
311 if (0 <= row && row < getVariableData().length) {
312 myUpButton.setEnabled(row > 0);
313 myDownButton.setEnabled(row < getVariableData().length - 1);
315 else {
316 myUpButton.setEnabled(false);
317 myDownButton.setEnabled(false);
321 private void moveSelectedItem(int moveIncrement) {
322 int row = myTable.getSelectedRow();
323 if (row < 0 || row >= getVariableData().length) return;
324 int targetRow = row + moveIncrement;
325 if (targetRow < 0 || targetRow >= getVariableData().length) return;
327 VariableData currentItem = getVariableData()[row];
328 getVariableData()[row] = getVariableData()[targetRow];
329 getVariableData()[targetRow] = currentItem;
331 TypeSelector currentSelector = myParameterTypeSelectors[row];
332 myParameterTypeSelectors[row] = myParameterTypeSelectors[targetRow];
333 myParameterTypeSelectors[targetRow] = currentSelector;
335 myTypeRendererCombo.setModel(new DefaultComboBoxModel(getVariableData()));
337 myTableModel.fireTableRowsUpdated(Math.min(targetRow, row), Math.max(targetRow, row));
338 myTable.getSelectionModel().setSelectionInterval(targetRow, targetRow);
341 public void setEnabled(boolean enabled) {
342 myTable.setEnabled(enabled);
343 if (!enabled) {
344 myUpButton.setEnabled(false);
345 myDownButton.setEnabled(false);
347 else {
348 updateMoveButtons();
350 super.setEnabled(enabled);
353 private class MyTableModel extends AbstractTableModel {
354 public static final int CHECKMARK_COLUMN = 0;
355 public static final int PARAMETER_TYPE_COLUMN = 1;
356 public static final int PARAMETER_NAME_COLUMN = 2;
358 public int getRowCount() {
359 return getVariableData().length;
362 public int getColumnCount() {
363 return 3;
366 public Object getValueAt(int rowIndex, int columnIndex) {
367 switch (columnIndex) {
368 case CHECKMARK_COLUMN: {
369 return getVariableData()[rowIndex].passAsParameter ? Boolean.TRUE : Boolean.FALSE;
371 case PARAMETER_NAME_COLUMN: {
372 return getVariableData()[rowIndex].name;
374 case PARAMETER_TYPE_COLUMN: {
375 return getVariableData()[rowIndex].type.getPresentableText();
378 assert false;
379 return null;
382 public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
383 switch (columnIndex) {
384 case CHECKMARK_COLUMN: {
385 getVariableData()[rowIndex].passAsParameter = ((Boolean)aValue).booleanValue();
386 fireTableRowsUpdated(rowIndex, rowIndex);
387 myTable.getSelectionModel().setSelectionInterval(rowIndex, rowIndex);
388 updateSignature();
389 break;
391 case PARAMETER_NAME_COLUMN: {
392 VariableData data = getVariableData()[rowIndex];
393 String name = (String)aValue;
394 if (JavaPsiFacade.getInstance(myProject).getNameHelper().isIdentifier(name)) {
395 data.name = name;
397 updateSignature();
398 break;
400 case PARAMETER_TYPE_COLUMN: {
401 VariableData data = getVariableData()[rowIndex];
402 data.type = (PsiType)aValue;
403 updateSignature();
404 break;
409 public boolean isCellEditable(int rowIndex, int columnIndex) {
410 switch (columnIndex) {
411 case CHECKMARK_COLUMN:
412 return isEnabled();
413 case PARAMETER_NAME_COLUMN:
414 return isEnabled() && getVariableData()[rowIndex].passAsParameter;
415 case PARAMETER_TYPE_COLUMN:
416 return isEnabled() && getVariableData()[rowIndex].passAsParameter && !(myParameterTypeSelectors[rowIndex].getComponent() instanceof JLabel);
417 default:
418 return false;
422 public Class getColumnClass(int columnIndex) {
423 if (columnIndex == CHECKMARK_COLUMN) {
424 return Boolean.class;
426 return super.getColumnClass(columnIndex);
430 private class CheckBoxTableCellRenderer extends BooleanTableCellRenderer {
431 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
432 Component rendererComponent = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
433 rendererComponent.setEnabled(ParameterTablePanel.this.isEnabled());
434 return rendererComponent;