blob: 7a427b9fc56f7458fab015dbbb7c9cad40a62010 [file] [log] [blame]
/*
* 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.refactoring.util;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.refactoring.ui.TypeSelector;
import com.intellij.refactoring.ui.TypeSelectorManager;
import com.intellij.refactoring.ui.TypeSelectorManagerImpl;
import com.intellij.ui.BooleanTableCellRenderer;
import com.intellij.ui.ListCellRendererWrapper;
import com.intellij.ui.TableUtil;
import com.intellij.ui.ToolbarDecorator;
import com.intellij.ui.components.JBComboBoxLabel;
import com.intellij.ui.components.editors.JBComboBoxTableCellEditorComponent;
import com.intellij.ui.table.JBTable;
import com.intellij.util.Function;
import com.intellij.util.ui.AbstractTableCellEditor;
import com.intellij.util.ui.EditableModel;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableColumn;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
/**
* @author Konstantin Bulenkov
*/
public abstract class ParameterTablePanel extends JPanel {
private final Project myProject;
private final VariableData[] myVariableData;
private final TypeSelector[] myParameterTypeSelectors;
private final JBTable myTable;
private final MyTableModel myTableModel;
private final JComboBox myTypeRendererCombo;
public VariableData[] getVariableData() {
return myVariableData;
}
protected abstract void updateSignature();
protected abstract void doEnterAction();
protected abstract void doCancelAction();
protected boolean areTypesDirected() {
return true;
}
public ParameterTablePanel(Project project, VariableData[] variableData, final PsiElement... scopeElements) {
super(new BorderLayout());
myProject = project;
myVariableData = variableData;
myTableModel = new MyTableModel();
myTable = new JBTable(myTableModel);
DefaultCellEditor defaultEditor = (DefaultCellEditor)myTable.getDefaultEditor(Object.class);
defaultEditor.setClickCountToStart(1);
myTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
myTable.setCellSelectionEnabled(true);
TableColumn checkboxColumn = myTable.getColumnModel().getColumn(MyTableModel.CHECKMARK_COLUMN);
TableUtil.setupCheckboxColumn(checkboxColumn);
checkboxColumn.setCellRenderer(new CheckBoxTableCellRenderer());
myTable.getColumnModel().getColumn(MyTableModel.PARAMETER_NAME_COLUMN).setCellRenderer(new DefaultTableCellRenderer() {
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
VariableData data = getVariableData()[row];
setText(data.name);
return this;
}
});
myParameterTypeSelectors = new TypeSelector[getVariableData().length];
for (int i = 0; i < myParameterTypeSelectors.length; i++) {
final PsiVariable variable = getVariableData()[i].variable;
final PsiExpression[] occurrences = findVariableOccurrences(scopeElements, variable);
final TypeSelectorManager manager = new TypeSelectorManagerImpl(myProject, getVariableData()[i].type, occurrences, areTypesDirected()) {
@Override
protected boolean isUsedAfter() {
return ParameterTablePanel.this.isUsedAfter(variable);
}
};
myParameterTypeSelectors[i] = manager.getTypeSelector();
getVariableData()[i].type = myParameterTypeSelectors[i].getSelectedType(); //reverse order
}
myTypeRendererCombo = new JComboBox(getVariableData());
myTypeRendererCombo.setOpaque(true);
myTypeRendererCombo.setBorder(null);
myTypeRendererCombo.setRenderer(new ListCellRendererWrapper<VariableData>() {
@Override
public void customize(JList list, VariableData value, int index, boolean selected, boolean hasFocus) {
if (value != null) {
setText(value.type.getPresentableText());
}
}
});
final TableColumn typeColumn = myTable.getColumnModel().getColumn(MyTableModel.PARAMETER_TYPE_COLUMN);
typeColumn.setCellEditor(new AbstractTableCellEditor() {
TypeSelector myCurrentSelector;
final JBComboBoxTableCellEditorComponent myEditorComponent = new JBComboBoxTableCellEditorComponent();
@Nullable
public Object getCellEditorValue() {
return myEditorComponent.getEditorValue();
}
public Component getTableCellEditorComponent(final JTable table,
final Object value,
final boolean isSelected,
final int row,
final int column) {
myEditorComponent.setCell(table, row, column);
myEditorComponent.setOptions(myParameterTypeSelectors[row].getTypes());
myEditorComponent.setDefaultValue(getVariableData()[row].type);
myEditorComponent.setToString(new Function<Object, String>() {
@Override
public String fun(Object o) {
return ((PsiType)o).getPresentableText();
}
});
myCurrentSelector = myParameterTypeSelectors[row];
return myEditorComponent;
}
});
myTable.getColumnModel().getColumn(MyTableModel.PARAMETER_TYPE_COLUMN).setCellRenderer(new DefaultTableCellRenderer() {
private JBComboBoxLabel myLabel = new JBComboBoxLabel();
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
myLabel.setText(String.valueOf(value));
myLabel.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
myLabel.setForeground(isSelected ? table.getSelectionForeground() : table.getForeground());
if (isSelected) {
myLabel.setSelectionIcon();
} else {
myLabel.setRegularIcon();
}
return myLabel;
}
});
myTable.setPreferredScrollableViewportSize(new Dimension(250, myTable.getRowHeight() * 5));
myTable.setShowGrid(false);
myTable.setIntercellSpacing(new Dimension(0, 0));
@NonNls final InputMap inputMap = myTable.getInputMap();
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "enable_disable");
@NonNls final ActionMap actionMap = myTable.getActionMap();
actionMap.put("enable_disable", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
if (myTable.isEditing()) return;
int[] rows = myTable.getSelectedRows();
if (rows.length > 0) {
boolean valueToBeSet = false;
for (int row : rows) {
if (!getVariableData()[row].passAsParameter) {
valueToBeSet = true;
break;
}
}
for (int row : rows) {
getVariableData()[row].passAsParameter = valueToBeSet;
}
myTableModel.fireTableRowsUpdated(rows[0], rows[rows.length - 1]);
TableUtil.selectRows(myTable, rows);
}
}
});
//// F2 should edit the name
//inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0), "edit_parameter_name");
//actionMap.put("edit_parameter_name", new AbstractAction() {
// public void actionPerformed(ActionEvent e) {
// if (!myTable.isEditing()) {
// int row = myTable.getSelectedRow();
// if (row >= 0 && row < myTableModel.getRowCount()) {
// TableUtil.editCellAt(myTable, row, MyTableModel.PARAMETER_NAME_COLUMN);
// }
// }
// }
//});
//// make ENTER work when the table has focus
//inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "invokeImpl");
//actionMap.put("invokeImpl", new AbstractAction() {
// public void actionPerformed(ActionEvent e) {
// TableCellEditor editor = myTable.getCellEditor();
// if (editor != null) {
// editor.stopCellEditing();
// }
// else {
// doEnterAction();
// }
// }
//});
// make ESCAPE work when the table has focus
actionMap.put("doCancel", new AbstractAction() {
public void actionPerformed(ActionEvent e) {
TableCellEditor editor = myTable.getCellEditor();
if (editor != null) {
editor.stopCellEditing();
}
else {
doCancelAction();
}
}
});
JPanel listPanel = ToolbarDecorator.createDecorator(myTable).disableAddAction().disableRemoveAction().createPanel();
add(listPanel, BorderLayout.CENTER);
if (getVariableData().length > 1) {
myTable.getSelectionModel().setSelectionInterval(0, 0);
}
}
protected boolean isUsedAfter(PsiVariable variable) {
return false;
}
public static PsiExpression[] findVariableOccurrences(final PsiElement[] scopeElements, final PsiVariable variable) {
final ArrayList<PsiExpression> result = new ArrayList<PsiExpression>();
for (final PsiElement element : scopeElements) {
element.accept(new JavaRecursiveElementWalkingVisitor() {
@Override public void visitReferenceExpression(final PsiReferenceExpression expression) {
super.visitReferenceExpression(expression);
if (!expression.isQualified() && expression.isReferenceTo(variable)) {
result.add(expression);
}
}
});
}
return result.toArray(new PsiExpression[result.size()]);
}
public void setEnabled(boolean enabled) {
myTable.setEnabled(enabled);
super.setEnabled(enabled);
}
private class MyTableModel extends AbstractTableModel implements EditableModel {
public static final int CHECKMARK_COLUMN = 0;
public static final int PARAMETER_TYPE_COLUMN = 1;
public static final int PARAMETER_NAME_COLUMN = 2;
public int getRowCount() {
return getVariableData().length;
}
public int getColumnCount() {
return 3;
}
public Object getValueAt(int rowIndex, int columnIndex) {
switch (columnIndex) {
case CHECKMARK_COLUMN: {
return getVariableData()[rowIndex].passAsParameter ? Boolean.TRUE : Boolean.FALSE;
}
case PARAMETER_NAME_COLUMN: {
return getVariableData()[rowIndex].name;
}
case PARAMETER_TYPE_COLUMN: {
return getVariableData()[rowIndex].type.getPresentableText();
}
}
assert false;
return null;
}
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
switch (columnIndex) {
case CHECKMARK_COLUMN: {
getVariableData()[rowIndex].passAsParameter = ((Boolean)aValue).booleanValue();
fireTableRowsUpdated(rowIndex, rowIndex);
myTable.getSelectionModel().setSelectionInterval(rowIndex, rowIndex);
updateSignature();
break;
}
case PARAMETER_NAME_COLUMN: {
VariableData data = getVariableData()[rowIndex];
String name = (String)aValue;
if (PsiNameHelper.getInstance(myProject).isIdentifier(name)) {
data.name = name;
}
updateSignature();
break;
}
case PARAMETER_TYPE_COLUMN: {
VariableData data = getVariableData()[rowIndex];
data.type = (PsiType)aValue;
updateSignature();
break;
}
}
}
@Override
public String getColumnName(int column) {
switch (column) {
case CHECKMARK_COLUMN: return "";
case PARAMETER_TYPE_COLUMN: return "Type";
case PARAMETER_NAME_COLUMN: return "Name";
}
return "";
}
public boolean isCellEditable(int rowIndex, int columnIndex) {
switch (columnIndex) {
case CHECKMARK_COLUMN:
return isEnabled();
case PARAMETER_NAME_COLUMN:
return isEnabled() && getVariableData()[rowIndex].passAsParameter;
case PARAMETER_TYPE_COLUMN:
return isEnabled() && getVariableData()[rowIndex].passAsParameter && !(myParameterTypeSelectors[rowIndex].getComponent() instanceof JLabel);
default:
return false;
}
}
public Class getColumnClass(int columnIndex) {
if (columnIndex == CHECKMARK_COLUMN) {
return Boolean.class;
}
return super.getColumnClass(columnIndex);
}
@Override
public void addRow() {
throw new IllegalAccessError("Not implemented");
}
@Override
public void removeRow(int index) {
throw new IllegalAccessError("Not implemented");
}
@Override
public void exchangeRows(int row, int targetRow) {
if (row < 0 || row >= getVariableData().length) return;
if (targetRow < 0 || targetRow >= getVariableData().length) return;
final VariableData currentItem = getVariableData()[row];
getVariableData()[row] = getVariableData()[targetRow];
getVariableData()[targetRow] = currentItem;
TypeSelector currentSelector = myParameterTypeSelectors[row];
myParameterTypeSelectors[row] = myParameterTypeSelectors[targetRow];
myParameterTypeSelectors[targetRow] = currentSelector;
myTypeRendererCombo.setModel(new DefaultComboBoxModel(getVariableData()));
myTableModel.fireTableRowsUpdated(Math.min(targetRow, row), Math.max(targetRow, row));
myTable.getSelectionModel().setSelectionInterval(targetRow, targetRow);
updateSignature();
}
@Override
public boolean canExchangeRows(int row, int targetRow) {
if (row < 0 || row >= getVariableData().length) return false;
if (targetRow < 0 || targetRow >= getVariableData().length) return false;
return true;
}
}
private class CheckBoxTableCellRenderer extends BooleanTableCellRenderer {
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Component rendererComponent = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
rendererComponent.setEnabled(ParameterTablePanel.this.isEnabled());
return rendererComponent;
}
}
}