blob: ef9150a52fd05b46390e3503b942407faaf99aca [file] [log] [blame]
/*
* Copyright 2000-2014 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.rename;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.InputValidatorEx;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiNamedElement;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.rename.naming.AutomaticRenamer;
import com.intellij.refactoring.ui.EnableDisableAction;
import com.intellij.refactoring.ui.StringTableCellEditor;
import com.intellij.refactoring.util.RefactoringUIUtil;
import com.intellij.ui.BooleanTableCellRenderer;
import com.intellij.ui.GuiUtils;
import com.intellij.ui.PopupHandler;
import com.intellij.ui.TableUtil;
import com.intellij.ui.table.JBTable;
import com.intellij.usageView.UsageInfo;
import com.intellij.usages.UsageViewPresentation;
import com.intellij.usages.impl.UsagePreviewPanel;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumnModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import java.util.List;
/**
* @author dsl
*/
public class AutomaticRenamingDialog extends DialogWrapper {
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.rename.AutomaticRenamingDialog");
private static final int CHECK_COLUMN = 0;
private static final int OLD_NAME_COLUMN = 1;
private static final int NEW_NAME_COLUMN = 2;
private final AutomaticRenamer myRenamer;
private boolean[] myShouldRename;
private String[] myNewNames;
private PsiNamedElement[] myRenames;
private MyTableModel myTableModel;
private JBTable myTable;
private JPanel myPanelForPreview;
private JButton mySelectAllButton;
private JButton myUnselectAllButton;
private JPanel myPanel;
private JSplitPane mySplitPane;
private final Project myProject;
private final UsagePreviewPanel myUsagePreviewPanel;
private final JLabel myUsageFileLabel;
private ListSelectionListener myListSelectionListener;
public AutomaticRenamingDialog(Project project, AutomaticRenamer renamer) {
super(project, true);
myProject = project;
myRenamer = renamer;
myUsagePreviewPanel = new UsagePreviewPanel(myProject, new UsageViewPresentation());
myUsageFileLabel = new JLabel();
populateData();
myTableModel = new MyTableModel(renamer.allowChangeSuggestedName());
setTitle(myRenamer.getDialogTitle());
init();
}
private void populateData() {
final Map<PsiNamedElement, String> renames = myRenamer.getRenames();
List<PsiNamedElement> temp = new ArrayList<PsiNamedElement>();
for (final PsiNamedElement namedElement : renames.keySet()) {
final String newName = renames.get(namedElement);
if (newName != null) temp.add(namedElement);
}
myRenames = temp.toArray(new PsiNamedElement[temp.size()]);
Arrays.sort(myRenames, new Comparator<PsiNamedElement>() {
@Override
public int compare(final PsiNamedElement e1, final PsiNamedElement e2) {
return Comparing.compare(e1.getName(), e2.getName());
}
});
myNewNames = new String[myRenames.length];
for (int i = 0; i < myNewNames.length; i++) {
myNewNames[i] = renames.get(myRenames[i]);
}
myShouldRename = new boolean[myRenames.length];
if (myRenamer.isSelectedByDefault()) {
for(int i=0; i<myShouldRename.length; i++) {
myShouldRename [i] = true;
}
}
}
@Override
protected String getDimensionServiceKey() {
return "#com.intellij.refactoring.rename.AutomaticRenamingDialog";
}
@Override
protected JComponent createNorthPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.add(new JLabel(myRenamer.getDialogDescription()), BorderLayout.CENTER);
final DefaultActionGroup actionGroup = new DefaultActionGroup(null, false);
actionGroup.addAction(createRenameSelectedAction()).setAsSecondary(true);
panel.add(ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, actionGroup, true).getComponent(), BorderLayout.EAST);
final Box box = Box.createHorizontalBox();
box.add(panel);
box.add(Box.createHorizontalGlue());
return box;
}
@Override
public void show() {
if (ApplicationManager.getApplication().isUnitTestMode()) return;
super.show();
}
protected void handleChanges() {
final int selectedRow = myTable.getSelectedRow();
if (selectedRow > -1) {
final boolean validName = RenameUtil.isValidName(myProject, myRenames[selectedRow], myNewNames[selectedRow]);
getOKAction().setEnabled(validName);
setErrorText(validName ? null : "Identifier \'" + myNewNames[selectedRow] + "\' is invalid");
}
}
@Override
protected JComponent createCenterPanel() {
myTable.setModel(myTableModel);
myTableModel.getSpaceAction().register();
myTableModel.addTableModelListener(new TableModelListener() {
@Override
public void tableChanged(TableModelEvent e) {
handleChanges();
}
});
myTable.addMouseListener(new PopupHandler() {
@Override
public void invokePopup(Component comp, int x, int y) {
final int[] selectionRows = myTable.getSelectedRows();
if (selectionRows != null) {
compoundPopup().show(comp, x, y);
}
}
});
final TableColumnModel columnModel = myTable.getColumnModel();
columnModel.getColumn(CHECK_COLUMN).setCellRenderer(new BooleanTableCellRenderer());
TableUtil.setupCheckboxColumn(columnModel.getColumn(CHECK_COLUMN));
columnModel.getColumn(NEW_NAME_COLUMN).setCellEditor(new StringTableCellEditor(myProject));
mySelectAllButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < myShouldRename.length; i++) {
myShouldRename[i] = true;
}
fireDataChanged();
}
});
myUnselectAllButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for (int i = 0; i < myShouldRename.length; i++) {
myShouldRename[i] = false;
}
fireDataChanged();
}
});
myListSelectionListener = new ListSelectionListener() {
@Override
public void valueChanged(final ListSelectionEvent e) {
myUsageFileLabel.setText("");
int index = myTable.getSelectionModel().getLeadSelectionIndex();
if (index != -1) {
PsiNamedElement element = myRenames[index];
UsageInfo usageInfo = new UsageInfo(element);
myUsagePreviewPanel.updateLayout(Collections.singletonList(usageInfo));
final PsiFile containingFile = element.getContainingFile();
if (containingFile != null) {
final VirtualFile virtualFile = containingFile.getVirtualFile();
if (virtualFile != null) {
myUsageFileLabel.setText(virtualFile.getName());
}
}
}
else {
myUsagePreviewPanel.updateLayout(null);
}
}
};
myTable.getSelectionModel().addListSelectionListener(myListSelectionListener);
myPanelForPreview.add(myUsagePreviewPanel, BorderLayout.CENTER);
myUsagePreviewPanel.updateLayout(null);
myPanelForPreview.add(myUsageFileLabel, BorderLayout.NORTH);
mySplitPane.setDividerLocation(0.5);
GuiUtils.replaceJSplitPaneWithIDEASplitter(myPanel);
if (myTableModel.getRowCount() != 0) {
myTable.getSelectionModel().addSelectionInterval(0,0);
}
return myPanel;
}
private JPopupMenu compoundPopup() {
final DefaultActionGroup group = new DefaultActionGroup();
group.add(createRenameSelectedAction());
ActionPopupMenu menu = ActionManager.getInstance().createActionPopupMenu(ActionPlaces.UNKNOWN, group);
return menu.getComponent();
}
private RenameSelectedAction createRenameSelectedAction() {
return new RenameSelectedAction(myTable, myTableModel) {
@Override
protected boolean isValidName(String inputString, int selectedRow) {
return RenameUtil.isValidName(myProject, myRenames[selectedRow], inputString);
}
};
}
private void fireDataChanged() {
int[] selectedRows = myTable.getSelectedRows();
myTable.getSelectionModel().removeListSelectionListener(myListSelectionListener);
myTableModel.fireTableDataChanged();
for (int selectedRow : selectedRows) {
myTable.addRowSelectionInterval(selectedRow, selectedRow);
}
myTable.getSelectionModel().addListSelectionListener(myListSelectionListener);
}
@Override
public JComponent getPreferredFocusedComponent() {
return myTable;
}
@Override
protected void doOKAction() {
TableUtil.stopEditing(myTable);
updateRenamer();
super.doOKAction();
}
@Override
protected void dispose() {
Disposer.dispose(myUsagePreviewPanel);
super.dispose();
}
private void updateRenamer() {
for (int i = 0; i < myRenames.length; i++) {
PsiNamedElement element = myRenames[i];
if (myShouldRename[i]) {
myRenamer.setRename(element, myNewNames[i]);
}
else {
myRenamer.doNotRename(element);
}
}
}
private void createUIComponents() {
myTable = new JBTable();
myTable.setRowHeight(myTable.getFontMetrics(UIManager.getFont("Table.font").deriveFont(Font.BOLD)).getHeight() + 4);
}
private class MyTableModel extends AbstractTableModel {
private final boolean myAllowRename;
private MyTableModel(boolean allowRename) {
myAllowRename = allowRename;
}
@Override
public int getColumnCount() {
return 3;
}
@Override
public int getRowCount() {
return myShouldRename.length;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
switch(columnIndex) {
case CHECK_COLUMN:
return Boolean.valueOf(myShouldRename[rowIndex]);
case OLD_NAME_COLUMN:
return "<html><nobr>" + RefactoringUIUtil.getDescription(myRenames[rowIndex], true) + "</nobr></html>";
case NEW_NAME_COLUMN:
return myNewNames[rowIndex];
default:
LOG.assertTrue(false);
return null;
}
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
switch(columnIndex) {
case CHECK_COLUMN:
myShouldRename[rowIndex] = ((Boolean)aValue).booleanValue();
break;
case NEW_NAME_COLUMN:
myNewNames[rowIndex] = (String) aValue;
break;
default:
LOG.assertTrue(false);
}
handleChanges();
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return columnIndex != OLD_NAME_COLUMN && (myAllowRename || columnIndex != NEW_NAME_COLUMN);
}
@Override
public Class getColumnClass(int columnIndex) {
switch(columnIndex) {
case CHECK_COLUMN: return Boolean.class;
case OLD_NAME_COLUMN: return String.class;
case NEW_NAME_COLUMN: return String.class;
default: return null;
}
}
@Override
public String getColumnName(int column) {
switch(column) {
case OLD_NAME_COLUMN:
return RefactoringBundle.message("automatic.renamer.enity.name.column", myRenamer.entityName());
case NEW_NAME_COLUMN:
return RefactoringBundle.message("automatic.renamer.rename.to.column");
default:
return " ";
}
}
private MyEnableDisable getSpaceAction() {
return this.new MyEnableDisable();
}
private class MyEnableDisable extends EnableDisableAction {
@Override
protected JTable getTable() {
return myTable;
}
@Override
protected boolean isRowChecked(int row) {
return myShouldRename[row];
}
@Override
protected void applyValue(int[] rows, boolean valueToBeSet) {
for (final int row : rows) {
myShouldRename[row] = valueToBeSet;
}
fireDataChanged();
}
}
}
public abstract static class RenameSelectedAction extends AnAction {
private JTable myTable;
private AbstractTableModel myModel;
public RenameSelectedAction(JTable table, final AbstractTableModel model) {
super("Rename Selected");
myTable = table;
myModel = model;
}
@Override
public void actionPerformed(AnActionEvent e) {
final int[] selectedRows = myTable.getSelectedRows();
final String newName = Messages.showInputDialog(myTable, "New name", "Rename Selected", null,
(String)myModel.getValueAt(selectedRows[0], NEW_NAME_COLUMN),
new InputValidatorEx() {
@Override
public boolean checkInput(String inputString) {
return getErrorText(inputString) == null;
}
@Override
public boolean canClose(String inputString) {
return checkInput(inputString);
}
@Nullable
@Override
public String getErrorText(String inputString) {
final int selectedRow = myTable.getSelectedRow();
if (!isValidName(inputString, selectedRow)) {
return "Identifier \'" + inputString + "\' is invalid";
}
return null;
}
});
if (newName == null) return;
for (int i : selectedRows) {
myModel.setValueAt(newName, i, NEW_NAME_COLUMN);
}
myModel.fireTableDataChanged();
for (int row : selectedRows) {
myTable.getSelectionModel().addSelectionInterval(row, row);
}
}
protected abstract boolean isValidName(String inputString, int selectedRow);
@Override
public void update(AnActionEvent e) {
e.getPresentation().setEnabled(myTable.getSelectedRows().length > 0);
}
}
}