blob: 0928cbadfdc6690b0e7a7644b43b7ac733db5425 [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.openapi.editor.actions;
import com.intellij.CommonBundle;
import com.intellij.icons.AllIcons;
import com.intellij.ide.ui.SplitterProportionsDataImpl;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.colors.EditorColorsScheme;
import com.intellij.openapi.editor.colors.EditorFontType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.SplitterProportionsData;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.ui.*;
import com.intellij.ui.components.JBList;
import com.intellij.ui.speedSearch.FilteringListModel;
import com.intellij.ui.speedSearch.ListWithFilter;
import com.intellij.util.Alarm;
import com.intellij.util.Function;
import com.intellij.util.ObjectUtils;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public abstract class ContentChooser<Data> extends DialogWrapper {
private List<Data> myAllContents;
private Editor myViewer;
private final boolean myUseIdeaEditor;
private final JList myList;
private final JBSplitter mySplitter;
private final Project myProject;
private final boolean myAllowMultipleSelections;
private final Alarm myUpdateAlarm;
private Icon myListEntryIcon = AllIcons.FileTypes.Text;
public ContentChooser(Project project, String title, boolean useIdeaEditor) {
this(project, title, useIdeaEditor, false);
}
public ContentChooser(Project project, String title, boolean useIdeaEditor, boolean allowMultipleSelections) {
super(project, true);
myProject = project;
myUseIdeaEditor = useIdeaEditor;
myAllowMultipleSelections = allowMultipleSelections;
myUpdateAlarm = new Alarm(getDisposable());
mySplitter = new JBSplitter(true, 0.3f);
mySplitter.setSplitterProportionKey(getDimensionServiceKey() + ".splitter");
myList = new JBList(new CollectionListModel<Item>());
setOKButtonText(CommonBundle.getOkButtonText());
setTitle(title);
init();
}
public void setContentIcon(@Nullable Icon icon) {
myListEntryIcon = icon;
}
public void setSplitterOrientation(boolean vertical) {
mySplitter.setOrientation(vertical);
}
@Override
public JComponent getPreferredFocusedComponent() {
return myList;
}
@Override
protected JComponent createCenterPanel() {
final int selectionMode = myAllowMultipleSelections ? ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
: ListSelectionModel.SINGLE_SELECTION;
myList.setSelectionMode(selectionMode);
if (myUseIdeaEditor) {
EditorColorsScheme scheme = EditorColorsManager.getInstance().getGlobalScheme();
myList.setFont(scheme.getFont(EditorFontType.PLAIN));
Color fg = ObjectUtils.chooseNotNull(scheme.getDefaultForeground(), UIUtil.getListForeground());
Color bg = ObjectUtils.chooseNotNull(scheme.getDefaultBackground(), UIUtil.getListBackground());
myList.setForeground(fg);
myList.setBackground(bg);
}
new DoubleClickListener() {
@Override
protected boolean onDoubleClick(MouseEvent e) {
close(OK_EXIT_CODE);
return true;
}
}.installOn(myList);
myList.setCellRenderer(new MyListCellRenderer());
myList.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_DELETE) {
int newSelectionIndex = -1;
for (Object o : myList.getSelectedValues()) {
int i = ((Item)o).index;
removeContentAt(myAllContents.get(i));
if (newSelectionIndex < 0) {
newSelectionIndex = i;
}
}
rebuildListContent();
if (myAllContents.isEmpty()) {
close(CANCEL_EXIT_CODE);
return;
}
newSelectionIndex = Math.min(newSelectionIndex, myAllContents.size() - 1);
myList.setSelectedIndex(newSelectionIndex);
}
else if (e.getKeyCode() == KeyEvent.VK_ENTER) {
doOKAction();
}
else {
final char aChar = e.getKeyChar();
if (aChar >= '0' && aChar <= '9') {
int idx = aChar == '0' ? 9 : aChar - '1';
if (idx < myAllContents.size()) {
myList.setSelectedIndex(idx);
e.consume();
doOKAction();
}
}
}
}
});
mySplitter.setFirstComponent(ListWithFilter.wrap(myList, ScrollPaneFactory.createScrollPane(myList), new Function<Object, String>() {
@Override
public String fun(Object o) {
return ((Item)o).longText;
}
}));
mySplitter.setSecondComponent(new JPanel());
rebuildListContent();
ListScrollingUtil.installActions(myList);
ListScrollingUtil.ensureSelectionExists(myList);
updateViewerForSelection();
myList.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
myUpdateAlarm.cancelAllRequests();
myUpdateAlarm.addRequest(new Runnable() {
@Override
public void run() {
updateViewerForSelection();
}
}, 100);
}
});
mySplitter.setPreferredSize(new Dimension(500, 500));
SplitterProportionsData d = new SplitterProportionsDataImpl();
d.externalizeToDimensionService(getClass().getName());
d.restoreSplitterProportions(mySplitter);
return mySplitter;
}
protected abstract void removeContentAt(final Data content);
@Override
protected String getDimensionServiceKey() {
return getClass().getName(); // store different values for multi-paste, history and commit messages
}
@Override
protected void doOKAction() {
if (getSelectedIndex() < 0) return;
super.doOKAction();
}
private void updateViewerForSelection() {
if (myAllContents.isEmpty()) return;
String fullString = getSelectedText();
if (myViewer != null) {
EditorFactory.getInstance().releaseEditor(myViewer);
}
if (myUseIdeaEditor) {
myViewer = createIdeaEditor(fullString);
JComponent component = myViewer.getComponent();
component.setPreferredSize(new Dimension(300, 500));
mySplitter.setSecondComponent(component);
} else {
final JTextArea textArea = new JTextArea(fullString);
textArea.setRows(3);
textArea.setWrapStyleWord(true);
textArea.setLineWrap(true);
textArea.setSelectionStart(0);
textArea.setSelectionEnd(textArea.getText().length());
textArea.setEditable(false);
mySplitter.setSecondComponent(ScrollPaneFactory.createScrollPane(textArea));
}
mySplitter.revalidate();
}
protected Editor createIdeaEditor(String text) {
Document doc = EditorFactory.getInstance().createDocument(text);
Editor editor = EditorFactory.getInstance().createViewer(doc, myProject);
editor.getSettings().setFoldingOutlineShown(false);
editor.getSettings().setLineNumbersShown(false);
editor.getSettings().setLineMarkerAreaShown(false);
editor.getSettings().setIndentGuidesShown(false);
return editor;
}
@Override
public void dispose() {
super.dispose();
SplitterProportionsData d = new SplitterProportionsDataImpl();
d.externalizeToDimensionService(getClass().getName());
d.saveSplitterProportions(mySplitter);
if (myViewer != null) {
EditorFactory.getInstance().releaseEditor(myViewer);
myViewer = null;
}
}
private void rebuildListContent() {
ArrayList<Item> items = new ArrayList<Item>();
int i = 0;
List<Data> contents = new ArrayList<Data>(getContents());
for (Data content : contents) {
String fullString = getStringRepresentationFor(content);
if (fullString != null) {
String shortString;
fullString = StringUtil.convertLineSeparators(fullString);
int newLineIdx = fullString.indexOf('\n');
if (newLineIdx == -1) {
shortString = fullString.trim();
}
else {
int lastLooked = 0;
do {
int nextLineIdx = fullString.indexOf("\n", lastLooked);
if (nextLineIdx > lastLooked) {
shortString = fullString.substring(lastLooked, nextLineIdx).trim() + " ...";
break;
}
else if (nextLineIdx == -1) {
shortString = " ...";
break;
}
lastLooked = nextLineIdx + 1;
} while (true);
}
items.add(new Item(i ++, shortString, fullString));
}
}
myAllContents = contents;
FilteringListModel listModel = (FilteringListModel)myList.getModel();
((CollectionListModel)listModel.getOriginalModel()).removeAll();
listModel.addAll(items);
ListWithFilter listWithFilter = UIUtil.getParentOfType(ListWithFilter.class, myList);
if (listWithFilter != null) {
listWithFilter.getSpeedSearch().update();
if (listModel.getSize() == 0) listWithFilter.resetFilter();
}
}
protected abstract String getStringRepresentationFor(final Data content);
protected abstract List<Data> getContents();
public int getSelectedIndex() {
Object o = myList.getSelectedValue();
return o == null? -1 : ((Item)o).index;
}
public void setSelectedIndex(int index) {
myList.setSelectedIndex(index);
ListScrollingUtil.ensureIndexIsVisible(myList, index, 0);
updateViewerForSelection();
}
@NotNull
public int[] getSelectedIndices() {
Object[] values = myList.getSelectedValues();
int[] result = new int[values.length];
for (int i = 0, length = values.length; i < length; i++) {
result[i] = ((Item)values[i]).index;
}
return result;
}
public List<Data> getAllContents() {
return myAllContents;
}
@NotNull
public String getSelectedText() {
StringBuilder sb = new StringBuilder();
for (Object o : myList.getSelectedValues()) {
String s = ((Item)o).longText;
sb.append(StringUtil.convertLineSeparators(s));
}
return sb.toString();
}
private class MyListCellRenderer extends ColoredListCellRenderer {
@Override
protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
setIcon(myListEntryIcon);
if (myUseIdeaEditor) {
int max = list.getModel().getSize();
String indexString = String.valueOf(index + 1);
int count = String.valueOf(max).length() - indexString.length();
char[] spaces = new char[count];
Arrays.fill(spaces, ' ');
String prefix = indexString + new String(spaces) + " ";
append(prefix, SimpleTextAttributes.GRAYED_ATTRIBUTES);
}
else if (UIUtil.isUnderGTKLookAndFeel()) {
// Fix GTK background
Color background = selected ? UIUtil.getListSelectionBackground() : UIUtil.getListBackground();
UIUtil.changeBackGround(this, background);
}
String text = ((Item)value).shortText;
FontMetrics metrics = list.getFontMetrics(list.getFont());
int charWidth = metrics.charWidth('m');
int maxLength = list.getParent().getParent().getWidth() * 3 / charWidth / 2;
text = StringUtil.first(text, maxLength, true); // do not paint long strings
append(text, SimpleTextAttributes.REGULAR_ATTRIBUTES);
}
}
private static class Item {
final int index;
final String shortText;
final String longText;
private Item(int index, String shortText, String longText) {
this.index = index;
this.shortText = shortText;
this.longText = longText;
}
}
}