/* * 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.ObjectUtil; 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 = ObjectUtil.chooseNotNull(scheme.getDefaultForeground(), UIUtil.getListForeground()); Color bg = ObjectUtil.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; } } }