blob: 59a5dbf34bd508cca2c58135df542cff1490fd29 [file] [log] [blame]
/*
* Copyright 2000-2009 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.ui;
import com.intellij.codeInsight.editorActions.SelectWordUtil;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.SelectionModel;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.event.DocumentListener;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.ComboBox;
import com.intellij.openapi.util.TextRange;
import com.intellij.ui.EditorComboBoxEditor;
import com.intellij.ui.EditorComboBoxRenderer;
import com.intellij.ui.EditorTextField;
import com.intellij.ui.StringComboboxEditor;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.EventListenerList;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.List;
public class NameSuggestionsField extends JPanel {
private final JComponent myComponent;
private final EventListenerList myListenerList = new EventListenerList();
private final MyComboBoxModel myComboBoxModel;
private final Project myProject;
private MyDocumentListener myDocumentListener;
private MyComboBoxItemListener myComboBoxItemListener;
private boolean myNonHumanChange = false;
public NameSuggestionsField(Project project) {
super(new BorderLayout());
myProject = project;
myComboBoxModel = new MyComboBoxModel();
final ComboBox comboBox = new ComboBox(myComboBoxModel,-1);
myComponent = comboBox;
add(myComponent, BorderLayout.CENTER);
setupComboBox(comboBox, StdFileTypes.JAVA);
}
public NameSuggestionsField(String[] nameSuggestions, Project project) {
this(nameSuggestions, project, StdFileTypes.JAVA);
}
public NameSuggestionsField(String[] nameSuggestions, Project project, FileType fileType) {
super(new BorderLayout());
myProject = project;
if (nameSuggestions == null || nameSuggestions.length <= 1) {
myComponent = createTextFieldForName(nameSuggestions, fileType);
}
else {
final ComboBox combobox = new ComboBox(nameSuggestions);
combobox.setSelectedIndex(0);
setupComboBox(combobox, fileType);
myComponent = combobox;
}
add(myComponent, BorderLayout.CENTER);
myComboBoxModel = null;
}
public NameSuggestionsField(final String[] suggestedNames, final Project project, final FileType fileType, @Nullable final Editor editor) {
this(suggestedNames, project, fileType);
if (editor == null) return;
// later here because EditorTextField creates Editor during addNotify()
final Runnable selectionRunnable = new Runnable() {
@Override
public void run() {
final int offset = editor.getCaretModel().getOffset();
List<TextRange> ranges = new ArrayList<TextRange>();
SelectWordUtil.addWordSelection(editor.getSettings().isCamelWords(), editor.getDocument().getCharsSequence(), offset, ranges);
Editor myEditor = getEditor();
if (myEditor == null) return;
for (TextRange wordRange : ranges) {
String word = editor.getDocument().getText(wordRange);
if (!word.equals(getEnteredName())) continue;
final SelectionModel selectionModel = editor.getSelectionModel();
myEditor.getSelectionModel().removeSelection();
final int wordRangeStartOffset = wordRange.getStartOffset();
int myOffset = offset - wordRangeStartOffset;
myEditor.getCaretModel().moveToOffset(myOffset);
TextRange selected = new TextRange(Math.max(0, selectionModel.getSelectionStart() - wordRangeStartOffset),
Math.max(0, selectionModel.getSelectionEnd() - wordRangeStartOffset));
selected = selected.intersection(new TextRange(0, myEditor.getDocument().getTextLength()));
if (selectionModel.hasSelection() && selected != null && !selected.isEmpty()) {
myEditor.getSelectionModel().setSelection(selected.getStartOffset(), selected.getEndOffset());
}
else if (shouldSelectAll()) {
myEditor.getSelectionModel().setSelection(0, myEditor.getDocument().getTextLength());
}
break;
}
}
};
SwingUtilities.invokeLater(selectionRunnable);
}
protected boolean shouldSelectAll() {
return true;
}
public void selectNameWithoutExtension() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
Editor editor = getEditor();
if (editor == null) return;
final int pos = editor.getDocument().getText().lastIndexOf('.');
if (pos > 0) {
editor.getSelectionModel().setSelection(0, pos);
editor.getCaretModel().moveToOffset(pos);
}
}
});
}
public void setSuggestions(final String[] suggestions) {
if(myComboBoxModel == null) return;
JComboBox comboBox = (JComboBox) myComponent;
final String oldSelectedItem = (String)comboBox.getSelectedItem();
final String oldItemFromTextField = (String) comboBox.getEditor().getItem();
final boolean shouldUpdateTextField =
oldItemFromTextField.equals(oldSelectedItem) || oldItemFromTextField.trim().length() == 0;
myComboBoxModel.setSuggestions(suggestions);
if(suggestions.length > 0 && shouldUpdateTextField) {
if (oldSelectedItem != null) {
comboBox.setSelectedItem(oldSelectedItem);
} else {
comboBox.setSelectedIndex(0);
}
}
else {
comboBox.getEditor().setItem(oldItemFromTextField);
}
}
public JComponent getComponent() {
return this;
}
public JComponent getFocusableComponent() {
if(myComponent instanceof JComboBox) {
return (JComponent) ((JComboBox) myComponent).getEditor().getEditorComponent();
} else {
return myComponent;
}
}
public String getEnteredName() {
if (myComponent instanceof JComboBox) {
return (String)((JComboBox)myComponent).getEditor().getItem();
} else {
return ((EditorTextField) myComponent).getText();
}
}
private JComponent createTextFieldForName(String[] nameSuggestions, FileType fileType) {
final String text;
if (nameSuggestions != null && nameSuggestions.length > 0 && nameSuggestions[0] != null) {
text = nameSuggestions[0];
}
else {
text = "";
}
EditorTextField field = new EditorTextField(text, myProject, fileType);
field.selectAll();
return field;
}
private static class MyComboBoxModel extends DefaultComboBoxModel {
private String[] mySuggestions;
MyComboBoxModel() {
mySuggestions = ArrayUtil.EMPTY_STRING_ARRAY;
}
// implements javax.swing.ListModel
@Override
public int getSize() {
return mySuggestions.length;
}
// implements javax.swing.ListModel
@Override
public Object getElementAt(int index) {
return mySuggestions[index];
}
public void setSuggestions(String[] suggestions) {
fireIntervalRemoved(this, 0, mySuggestions.length);
mySuggestions = suggestions;
fireIntervalAdded(this, 0, mySuggestions.length);
}
}
private void setupComboBox(final ComboBox combobox, FileType fileType) {
final EditorComboBoxEditor comboEditor = new StringComboboxEditor(myProject, fileType, combobox) {
@Override
public void setItem(Object anObject) {
myNonHumanChange = true;
super.setItem(anObject);
}
};
combobox.setEditor(comboEditor);
combobox.setRenderer(new EditorComboBoxRenderer(comboEditor));
combobox.setEditable(true);
combobox.setMaximumRowCount(8);
comboEditor.selectAll();
}
public Editor getEditor() {
if (myComponent instanceof EditorTextField) {
return ((EditorTextField)myComponent).getEditor();
}
else {
return ((EditorTextField)((JComboBox)myComponent).getEditor().getEditorComponent()).getEditor();
}
}
public interface DataChanged extends EventListener {
void dataChanged();
}
public void addDataChangedListener(DataChanged listener) {
myListenerList.add(DataChanged.class, listener);
attachListeners();
}
public void removeDataChangedListener(DataChanged listener) {
myListenerList.remove(DataChanged.class, listener);
if (myListenerList.getListenerCount() == 0) {
detachListeners();
}
}
private void attachListeners() {
if (myDocumentListener == null) {
myDocumentListener = new MyDocumentListener();
((EditorTextField) getFocusableComponent()).addDocumentListener(myDocumentListener);
}
if (myComboBoxItemListener == null && myComponent instanceof JComboBox) {
myComboBoxItemListener = new MyComboBoxItemListener();
((JComboBox) myComponent).addItemListener(myComboBoxItemListener);
}
}
private void detachListeners() {
if (myDocumentListener != null) {
((EditorTextField) getFocusableComponent()).removeDocumentListener(myDocumentListener);
myDocumentListener = null;
}
if (myComboBoxItemListener != null) {
((JComboBox) myComponent).removeItemListener(myComboBoxItemListener);
myComboBoxItemListener = null;
}
}
private void fireDataChanged() {
Object[] list = myListenerList.getListenerList();
for (Object aList : list) {
if (aList instanceof DataChanged) {
((DataChanged)aList).dataChanged();
}
}
}
@Override
public boolean requestFocusInWindow() {
if(myComponent instanceof JComboBox) {
return ((JComboBox) myComponent).getEditor().getEditorComponent().requestFocusInWindow();
}
else {
return myComponent.requestFocusInWindow();
}
}
@Override
public void setEnabled (boolean enabled) {
myComponent.setEnabled(enabled);
}
private class MyDocumentListener implements DocumentListener {
@Override
public void beforeDocumentChange(DocumentEvent event) {
}
@Override
public void documentChanged(DocumentEvent event) {
if (!myNonHumanChange && myComponent instanceof JComboBox && ((JComboBox)myComponent).isPopupVisible()) {
((JComboBox)myComponent).hidePopup();
}
myNonHumanChange = false;
fireDataChanged();
}
}
private class MyComboBoxItemListener implements ItemListener {
@Override
public void itemStateChanged(ItemEvent e) {
fireDataChanged();
}
}
}