OMG asterisk instead of arrow for IDEA-167629 Show Usages popup: indicate the current usage in search results

This commit is contained in:
Alexey Kudravtsev
2018-09-26 16:13:02 +03:00
parent b004ea25b9
commit 0e3b78883f
4 changed files with 395 additions and 369 deletions
@@ -31,7 +31,6 @@ import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.PopupChooserBuilder;
import com.intellij.openapi.ui.popup.util.PopupUtil;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Disposer;
import com.intellij.openapi.util.Pair;
@@ -39,7 +38,6 @@ import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.pom.Navigatable;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
@@ -48,39 +46,27 @@ import com.intellij.psi.search.SearchScope;
import com.intellij.ui.*;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.popup.AbstractPopup;
import com.intellij.ui.popup.HintUpdateSupply;
import com.intellij.ui.popup.PopupUpdateProcessor;
import com.intellij.ui.table.JBTable;
import com.intellij.usageView.UsageInfo;
import com.intellij.usageView.UsageViewBundle;
import com.intellij.usageView.UsageViewUtil;
import com.intellij.usages.*;
import com.intellij.usages.impl.*;
import com.intellij.usages.rules.UsageFilteringRuleProvider;
import com.intellij.util.Alarm;
import com.intellij.util.ArrayUtil;
import com.intellij.util.PlatformIcons;
import com.intellij.util.Processor;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.util.ui.*;
import com.intellij.xml.util.XmlStringUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.*;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
public class ShowUsagesAction extends AnAction implements PopupAction {
public static final String ID = "ShowUsages";
@@ -89,11 +75,8 @@ public class ShowUsagesAction extends AnAction implements PopupAction {
return Math.max(1, Registry.intValue("ide.usages.page.size", 100));
}
static final Usage MORE_USAGES_SEPARATOR = NullUsage.INSTANCE;
static final Usage USAGES_OUTSIDE_SCOPE_SEPARATOR = new UsageAdapter();
private static final UsageNode MORE_USAGES_SEPARATOR_NODE = UsageViewImpl.NULL_NODE;
private static final UsageNode USAGES_OUTSIDE_SCOPE_NODE = new UsageNode(null, USAGES_OUTSIDE_SCOPE_SEPARATOR);
private static final UsageNode USAGES_OUTSIDE_SCOPE_NODE = new UsageNode(null, ShowUsagesTable.USAGES_OUTSIDE_SCOPE_SEPARATOR);
private static final Comparator<UsageNode> USAGE_NODE_COMPARATOR = (c1, c2) -> {
if (c1 instanceof StringNode || c2 instanceof StringNode) {
@@ -105,8 +88,8 @@ public class ShowUsagesAction extends AnAction implements PopupAction {
Usage o1 = c1.getUsage();
Usage o2 = c2.getUsage();
int weight1 = o1 == USAGES_OUTSIDE_SCOPE_SEPARATOR ? 2 : o1 == MORE_USAGES_SEPARATOR ? 1 : 0;
int weight2 = o2 == USAGES_OUTSIDE_SCOPE_SEPARATOR ? 2 : o2 == MORE_USAGES_SEPARATOR ? 1 : 0;
int weight1 = o1 == ShowUsagesTable.USAGES_OUTSIDE_SCOPE_SEPARATOR ? 2 : o1 == ShowUsagesTable.MORE_USAGES_SEPARATOR ? 1 : 0;
int weight2 = o2 == ShowUsagesTable.USAGES_OUTSIDE_SCOPE_SEPARATOR ? 2 : o2 == ShowUsagesTable.MORE_USAGES_SEPARATOR ? 1 : 0;
if (weight1 != weight2) return weight1 - weight2;
if (o1 instanceof Comparable && o2 instanceof Comparable) {
@@ -213,11 +196,11 @@ public class ShowUsagesAction extends AnAction implements PopupAction {
showElementUsages(editor, popupPosition, handler, maxUsages, handler.getFindUsagesOptions(DataManager.getInstance().getDataContext()));
}
private void showElementUsages(final Editor editor,
@NotNull final RelativePoint popupPosition,
@NotNull final FindUsagesHandler handler,
final int maxUsages,
@NotNull final FindUsagesOptions options) {
void showElementUsages(final Editor editor,
@NotNull final RelativePoint popupPosition,
@NotNull final FindUsagesHandler handler,
final int maxUsages,
@NotNull final FindUsagesOptions options) {
ApplicationManager.getApplication().assertIsDispatchThread();
final UsageViewSettings usageViewSettings = UsageViewSettings.getInstance();
final ShowUsagesSettings showUsagesSettings = ShowUsagesSettings.getInstance();
@@ -245,7 +228,7 @@ public class ShowUsagesAction extends AnAction implements PopupAction {
usageViewSettings.loadState(savedGlobalSettings);
});
final MyTable table = new MyTable();
final ShowUsagesTable table = new ShowUsagesTable();
final AsyncProcessIcon processIcon = new AsyncProcessIcon("xxx");
addUsageNodes(usageView.getRoot(), usageView, new ArrayList<>());
@@ -254,11 +237,11 @@ public class ShowUsagesAction extends AnAction implements PopupAction {
final Set<UsageNode> visibleNodes = new LinkedHashSet<>();
final List<UsageNode> data = collectData(usages, visibleNodes, usageView, presentation);
final AtomicInteger outOfScopeUsages = new AtomicInteger();
setTableModel(table, usageView, data, outOfScopeUsages, options.searchScope);
table.setTableModel(usageView, data, outOfScopeUsages, options.searchScope);
boolean isPreviewMode = Boolean.TRUE == PreviewManager.SERVICE.preview(handler.getProject(), UsagesPreviewPanelProvider.ID, Pair.create(usageView, table), false);
Runnable itemChosenCallback = prepareTable(table, editor, popupPosition, handler, maxUsages, options, isPreviewMode);
Runnable itemChosenCallback = table.prepareTable(editor, popupPosition, handler, maxUsages, options, isPreviewMode, this);
@Nullable final JBPopup popup = isPreviewMode ? null : createUsagePopup(usages, visibleNodes, handler, editor, popupPosition,
maxUsages, usageView, options, table, itemChosenCallback, presentation, processIcon);
@@ -296,7 +279,7 @@ public class ShowUsagesAction extends AnAction implements PopupAction {
if (!UsageViewManagerImpl.isInScope(usage, options.searchScope)) {
if (outOfScopeUsages.getAndIncrement() == 0) {
visibleNodes.add(USAGES_OUTSIDE_SCOPE_NODE);
usages.add(USAGES_OUTSIDE_SCOPE_SEPARATOR);
usages.add(ShowUsagesTable.USAGES_OUTSIDE_SCOPE_SEPARATOR);
}
return true;
}
@@ -310,7 +293,7 @@ public class ShowUsagesAction extends AnAction implements PopupAction {
boolean continueSearch = true;
if (visibleNodes.size() == maxUsages) {
visibleNodes.add(MORE_USAGES_SEPARATOR_NODE);
usages.add(MORE_USAGES_SEPARATOR);
usages.add(ShowUsagesTable.MORE_USAGES_SEPARATOR);
continueSearch = false;
}
pingEDT.ping();
@@ -344,7 +327,7 @@ public class ShowUsagesAction extends AnAction implements PopupAction {
if (usages.size() == 1) {
//the only usage
Usage usage = visibleNodes.iterator().next().getUsage();
if (usage == USAGES_OUTSIDE_SCOPE_SEPARATOR) {
if (usage == ShowUsagesTable.USAGES_OUTSIDE_SCOPE_SEPARATOR) {
hint(editor, UsageViewManagerImpl.outOfScopeMessage(outOfScopeUsages.get(), options.searchScope), handler, popupPosition, maxUsages, options, true);
}
else {
@@ -381,48 +364,10 @@ public class ShowUsagesAction extends AnAction implements PopupAction {
}
@NotNull
private static UsageNode createStringNode(@NotNull final Object string) {
static UsageNode createStringNode(@NotNull final Object string) {
return new StringNode(string);
}
private static class MyModel extends ListTableModel<UsageNode> implements ModelDiff.Model<Object> {
private MyModel(@NotNull List<UsageNode> data, int cols) {
super(cols(cols), data, 0);
}
@NotNull
private static ColumnInfo[] cols(int cols) {
ColumnInfo<UsageNode, UsageNode> o = new ColumnInfo<UsageNode, UsageNode>("") {
@Nullable
@Override
public UsageNode valueOf(UsageNode node) {
return node;
}
};
List<ColumnInfo<UsageNode, UsageNode>> list = Collections.nCopies(cols, o);
return list.toArray(ColumnInfo.EMPTY_ARRAY);
}
@Override
public void addToModel(int idx, Object element) {
UsageNode node = element instanceof UsageNode ? (UsageNode)element : createStringNode(element);
if (idx < getRowCount()) {
insertRow(idx, node);
}
else {
addRow(node);
}
}
@Override
public void removeRangeFromModel(int start, int end) {
for (int i=end; i>=start; i--) {
removeRow(i);
}
}
}
private static boolean showPopupIfNeedTo(@NotNull JBPopup popup, @NotNull RelativePoint popupPosition) {
if (!popup.isDisposed() && !popup.isVisible()) {
popup.show(popupPosition);
@@ -504,106 +449,6 @@ public class ShowUsagesAction extends AnAction implements PopupAction {
return options.searchScope.getDisplayName();
}
@NotNull
private Runnable prepareTable(final MyTable table,
final Editor editor,
final RelativePoint popupPosition,
final FindUsagesHandler handler,
final int maxUsages,
@NotNull final FindUsagesOptions options,
final boolean previewMode) {
SpeedSearchBase<JTable> speedSearch = new MySpeedSearch(table);
speedSearch.setComparator(new SpeedSearchComparator(false));
table.setRowHeight(PlatformIcons.CLASS_ICON.getIconHeight()+2);
table.setShowGrid(false);
table.setShowVerticalLines(false);
table.setShowHorizontalLines(false);
table.setTableHeader(null);
table.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
table.setIntercellSpacing(new Dimension(0, 0));
final AtomicReference<List<Object>> selectedUsages = new AtomicReference<>();
final AtomicBoolean moreUsagesSelected = new AtomicBoolean();
final AtomicBoolean outsideScopeUsagesSelected = new AtomicBoolean();
table.getSelectionModel().addListSelectionListener(e -> {
selectedUsages.set(null);
outsideScopeUsagesSelected.set(false);
moreUsagesSelected.set(false);
List<Object> usages = null;
for (int i : table.getSelectedRows()) {
Object value = table.getValueAt(i, 0);
if (value instanceof UsageNode) {
Usage usage = ((UsageNode)value).getUsage();
if (usage == USAGES_OUTSIDE_SCOPE_SEPARATOR) {
outsideScopeUsagesSelected.set(true);
usages = null;
break;
}
else if (usage == MORE_USAGES_SEPARATOR) {
moreUsagesSelected.set(true);
usages = null;
break;
}
else {
if (usages == null) usages = new ArrayList<>();
usages.add(usage instanceof UsageInfo2UsageAdapter ? ((UsageInfo2UsageAdapter)usage).getUsageInfo().copy() : usage);
}
}
}
selectedUsages.set(usages);
});
final Runnable itemChosenCallback = () -> {
if (moreUsagesSelected.get()) {
appendMoreUsages(editor, popupPosition, handler, maxUsages, options);
return;
}
if (outsideScopeUsagesSelected.get()) {
options.searchScope = GlobalSearchScope.projectScope(handler.getProject());
showElementUsages(editor, popupPosition, handler, maxUsages, options);
return;
}
List<Object> usages = selectedUsages.get();
if (usages != null) {
for (Object usage : usages) {
if (usage instanceof UsageInfo) {
UsageViewUtil.navigateTo((UsageInfo)usage, true);
}
else if (usage instanceof Navigatable) {
((Navigatable)usage).navigate(true);
}
}
}
};
if (previewMode) {
table.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
if (UIUtil.isActionClick(e, MouseEvent.MOUSE_RELEASED) && !UIUtil.isSelectionButtonDown(e) && !e.isConsumed()) {
itemChosenCallback.run();
}
}
});
table.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
itemChosenCallback.run();
}
}
});
}
return itemChosenCallback;
}
@NotNull
private JBPopup createUsagePopup(@NotNull final List<Usage> usages,
@NotNull Set<UsageNode> visibleNodes,
@@ -840,33 +685,6 @@ public class ShowUsagesAction extends AnAction implements PopupAction {
return true;
}
@NotNull
private static MyModel setTableModel(@NotNull JTable table,
@NotNull UsageViewImpl usageView,
@NotNull final List<UsageNode> data,
@NotNull AtomicInteger outOfScopeUsages,
@NotNull SearchScope searchScope) {
ApplicationManager.getApplication().assertIsDispatchThread();
final int columnCount = calcColumnCount(data);
MyModel model = table.getModel() instanceof MyModel ? (MyModel)table.getModel() : null;
if (model == null || model.getColumnCount() != columnCount) {
model = new MyModel(data, columnCount);
table.setModel(model);
ShowUsagesTableCellRenderer renderer = new ShowUsagesTableCellRenderer(usageView, outOfScopeUsages, searchScope);
for (int i=0;i<table.getColumnModel().getColumnCount();i++) {
TableColumn column = table.getColumnModel().getColumn(i);
column.setPreferredWidth(0);
column.setCellRenderer(renderer);
}
}
return model;
}
private static int calcColumnCount(@NotNull List<UsageNode> data) {
return data.isEmpty() || data.get(0) instanceof StringNode ? 1 : 3;
}
@NotNull
private static List<UsageNode> collectData(@NotNull List<? extends Usage> usages,
@NotNull Collection<? extends UsageNode> visibleNodes,
@@ -923,7 +741,7 @@ public class ShowUsagesAction extends AnAction implements PopupAction {
private void rebuildTable(@NotNull final UsageViewImpl usageView,
@NotNull final List<Usage> usages,
@NotNull List<UsageNode> nodes,
@NotNull final JTable table,
@NotNull final ShowUsagesTable table,
@Nullable final JBPopup popup,
@NotNull final UsageViewPresentation presentation,
@NotNull final RelativePoint popupPosition,
@@ -932,11 +750,11 @@ public class ShowUsagesAction extends AnAction implements PopupAction {
@NotNull SearchScope searchScope) {
ApplicationManager.getApplication().assertIsDispatchThread();
boolean shouldShowMoreSeparator = usages.contains(MORE_USAGES_SEPARATOR);
boolean shouldShowMoreSeparator = usages.contains(ShowUsagesTable.MORE_USAGES_SEPARATOR);
if (shouldShowMoreSeparator) {
nodes.add(MORE_USAGES_SEPARATOR_NODE);
}
boolean hasOutsideScopeUsages = usages.contains(USAGES_OUTSIDE_SCOPE_SEPARATOR);
boolean hasOutsideScopeUsages = usages.contains(ShowUsagesTable.USAGES_OUTSIDE_SCOPE_SEPARATOR);
if (hasOutsideScopeUsages && !shouldShowMoreSeparator) {
nodes.add(USAGES_OUTSIDE_SCOPE_NODE);
}
@@ -948,7 +766,7 @@ public class ShowUsagesAction extends AnAction implements PopupAction {
}
List<UsageNode> data = collectData(usages, nodes, usageView, presentation);
MyModel tableModel = setTableModel(table, usageView, data, outOfScopeUsages, searchScope);
ShowUsagesTable.MyModel tableModel = table.setTableModel(usageView, data, outOfScopeUsages, searchScope);
List<UsageNode> existingData = tableModel.getItems();
int row = table.getSelectedRow();
@@ -976,7 +794,7 @@ public class ShowUsagesAction extends AnAction implements PopupAction {
}
// returns new selection
private static int updateModel(@NotNull MyModel tableModel, @NotNull List<UsageNode> listOld, @NotNull List<UsageNode> listNew, int oldSelection) {
private static int updateModel(@NotNull ShowUsagesTable.MyModel tableModel, @NotNull List<UsageNode> listOld, @NotNull List<UsageNode> listNew, int oldSelection) {
UsageNode[] oa = listOld.toArray(new UsageNode[0]);
UsageNode[] na = listNew.toArray(new UsageNode[0]);
List<ModelDiff.Cmd> cmds = ModelDiff.createDiffCmds(tableModel, oa, na);
@@ -1044,7 +862,7 @@ public class ShowUsagesAction extends AnAction implements PopupAction {
return bounds;
}
private void appendMoreUsages(Editor editor,
void appendMoreUsages(Editor editor,
@NotNull RelativePoint popupPosition,
@NotNull FindUsagesHandler handler,
int maxUsages,
@@ -1137,64 +955,6 @@ public class ShowUsagesAction extends AnAction implements PopupAction {
return newFileEditor instanceof TextEditor ? ((TextEditor)newFileEditor).getEditor() : null;
}
private static class MyTable extends JBTable implements DataProvider {
private static final int MARGIN = 2;
MyTable() {
ScrollingUtil.installActions(this);
HintUpdateSupply.installDataContextHintUpdateSupply(this);
}
@Override
public boolean getScrollableTracksViewportWidth() {
return true;
}
@Override
public Object getData(@NotNull @NonNls String dataId) {
if (CommonDataKeys.PSI_ELEMENT.is(dataId)) {
final int[] selected = getSelectedRows();
if (selected.length == 1) {
return getPsiElementForHint(getValueAt(selected[0], 0));
}
}
else if (LangDataKeys.POSITION_ADJUSTER_POPUP.is(dataId)) {
return PopupUtil.getPopupContainerFor(this);
}
return null;
}
@Override
public int getRowHeight() {
return super.getRowHeight() + 2 * MARGIN;
}
@NotNull
@Override
public Component prepareRenderer(@NotNull TableCellRenderer renderer, int row, int column) {
Component component = super.prepareRenderer(renderer, row, column);
if (component instanceof JComponent) {
((JComponent)component).setBorder(JBUI.Borders.empty(MARGIN, MARGIN, MARGIN, 0));
}
return component;
}
@Nullable
private static PsiElement getPsiElementForHint(Object selectedValue) {
if (selectedValue instanceof UsageNode) {
final Usage usage = ((UsageNode)selectedValue).getUsage();
if (usage instanceof UsageInfo2UsageAdapter) {
final PsiElement element = ((UsageInfo2UsageAdapter)usage).getElement();
if (element != null) {
final PsiElement view = UsageToPsiElementProvider.findAppropriateParentFrom(element);
return view == null ? element : view;
}
}
}
return null;
}
}
static class StringNode extends UsageNode {
@NotNull private final Object myString;
@@ -1208,52 +968,4 @@ public class ShowUsagesAction extends AnAction implements PopupAction {
return myString.toString();
}
}
private static class MySpeedSearch extends SpeedSearchBase<JTable> {
MySpeedSearch(@NotNull MyTable table) {
super(table);
}
@Override
protected int getSelectedIndex() {
return getTable().getSelectedRow();
}
@Override
protected int convertIndexToModel(int viewIndex) {
return getTable().convertRowIndexToModel(viewIndex);
}
@NotNull
@Override
protected Object[] getAllElements() {
return ((MyModel)getTable().getModel()).getItems().toArray();
}
@Override
protected String getElementText(@NotNull Object element) {
if (!(element instanceof UsageNode)) return element.toString();
UsageNode node = (UsageNode)element;
if (node instanceof StringNode) return "";
Usage usage = node.getUsage();
if (usage == MORE_USAGES_SEPARATOR || usage == USAGES_OUTSIDE_SCOPE_SEPARATOR) return "";
GroupNode group = (GroupNode)node.getParent();
String groupText = group == null ? "" : group.getGroup().getText(null);
return groupText + usage.getPresentation().getPlainText();
}
@Override
protected void selectElement(Object element, String selectedText) {
List<UsageNode> data = ((MyModel)getTable().getModel()).getItems();
int i = data.indexOf(element);
if (i == -1) return;
final int viewRow = getTable().convertRowIndexToView(i);
getTable().getSelectionModel().setSelectionInterval(viewRow, viewRow);
TableUtil.scrollSelectionToVisible(getTable());
}
private MyTable getTable() {
return (MyTable)myComponent;
}
}
}
@@ -0,0 +1,323 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.find.actions;
import com.intellij.find.findUsages.FindUsagesHandler;
import com.intellij.find.findUsages.FindUsagesOptions;
import com.intellij.ide.util.gotoByName.ModelDiff;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.actionSystem.DataProvider;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.ui.popup.util.PopupUtil;
import com.intellij.pom.Navigatable;
import com.intellij.psi.PsiElement;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.ui.ScrollingUtil;
import com.intellij.ui.SpeedSearchBase;
import com.intellij.ui.SpeedSearchComparator;
import com.intellij.ui.TableUtil;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.popup.HintUpdateSupply;
import com.intellij.ui.table.JBTable;
import com.intellij.usageView.UsageInfo;
import com.intellij.usageView.UsageViewUtil;
import com.intellij.usages.Usage;
import com.intellij.usages.UsageInfo2UsageAdapter;
import com.intellij.usages.UsageToPsiElementProvider;
import com.intellij.usages.impl.*;
import com.intellij.util.PlatformIcons;
import com.intellij.util.ui.ColumnInfo;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.ListTableModel;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
class ShowUsagesTable extends JBTable implements DataProvider {
static final Usage MORE_USAGES_SEPARATOR = NullUsage.INSTANCE;
static final Usage USAGES_OUTSIDE_SCOPE_SEPARATOR = new UsageAdapter();
private static final int MARGIN = 2;
ShowUsagesTable() {
ScrollingUtil.installActions(this);
HintUpdateSupply.installDataContextHintUpdateSupply(this);
}
@Override
public boolean getScrollableTracksViewportWidth() {
return true;
}
@Override
public Object getData(@NotNull @NonNls String dataId) {
if (CommonDataKeys.PSI_ELEMENT.is(dataId)) {
final int[] selected = getSelectedRows();
if (selected.length == 1) {
return getPsiElementForHint(getValueAt(selected[0], 0));
}
}
else if (LangDataKeys.POSITION_ADJUSTER_POPUP.is(dataId)) {
return PopupUtil.getPopupContainerFor(this);
}
return null;
}
@Override
public int getRowHeight() {
return super.getRowHeight() + 2 * MARGIN;
}
@NotNull
@Override
public Component prepareRenderer(@NotNull TableCellRenderer renderer, int row, int column) {
Component component = super.prepareRenderer(renderer, row, column);
if (component instanceof JComponent) {
((JComponent)component).setBorder(JBUI.Borders.empty(MARGIN, MARGIN, MARGIN, 0));
}
return component;
}
@NotNull
Runnable prepareTable(final Editor editor,
@NotNull RelativePoint popupPosition,
@NotNull FindUsagesHandler handler,
final int maxUsages,
@NotNull final FindUsagesOptions options,
final boolean previewMode,
@NotNull ShowUsagesAction action) {
SpeedSearchBase<JTable> speedSearch = new MySpeedSearch(this);
speedSearch.setComparator(new SpeedSearchComparator(false));
setRowHeight(PlatformIcons.CLASS_ICON.getIconHeight() + 2);
setShowGrid(false);
setShowVerticalLines(false);
setShowHorizontalLines(false);
setTableHeader(null);
setAutoResizeMode(AUTO_RESIZE_LAST_COLUMN);
setIntercellSpacing(new Dimension(0, 0));
final AtomicReference<java.util.List<Object>> selectedUsages = new AtomicReference<>();
final AtomicBoolean moreUsagesSelected = new AtomicBoolean();
final AtomicBoolean outsideScopeUsagesSelected = new AtomicBoolean();
getSelectionModel().addListSelectionListener(e -> {
selectedUsages.set(null);
outsideScopeUsagesSelected.set(false);
moreUsagesSelected.set(false);
java.util.List<Object> usages = null;
for (int i : getSelectedRows()) {
Object value = getValueAt(i, 0);
if (value instanceof UsageNode) {
Usage usage = ((UsageNode)value).getUsage();
if (usage == USAGES_OUTSIDE_SCOPE_SEPARATOR) {
outsideScopeUsagesSelected.set(true);
usages = null;
break;
}
else if (usage == MORE_USAGES_SEPARATOR) {
moreUsagesSelected.set(true);
usages = null;
break;
}
else {
if (usages == null) usages = new ArrayList<>();
usages.add(usage instanceof UsageInfo2UsageAdapter ? ((UsageInfo2UsageAdapter)usage).getUsageInfo().copy() : usage);
}
}
}
selectedUsages.set(usages);
});
final Runnable itemChosenCallback = () -> {
if (moreUsagesSelected.get()) {
action.appendMoreUsages(editor, popupPosition, handler, maxUsages, options);
return;
}
if (outsideScopeUsagesSelected.get()) {
options.searchScope = GlobalSearchScope.projectScope(handler.getProject());
action.showElementUsages(editor, popupPosition, handler, maxUsages, options);
return;
}
List<Object> usages = selectedUsages.get();
if (usages != null) {
for (Object usage : usages) {
if (usage instanceof UsageInfo) {
UsageViewUtil.navigateTo((UsageInfo)usage, true);
}
else if (usage instanceof Navigatable) {
((Navigatable)usage).navigate(true);
}
}
}
};
if (previewMode) {
addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
if (UIUtil.isActionClick(e, MouseEvent.MOUSE_RELEASED) && !UIUtil.isSelectionButtonDown(e) && !e.isConsumed()) {
itemChosenCallback.run();
}
}
});
addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
itemChosenCallback.run();
}
}
});
}
return itemChosenCallback;
}
@Nullable
private static PsiElement getPsiElementForHint(Object selectedValue) {
if (selectedValue instanceof UsageNode) {
final Usage usage = ((UsageNode)selectedValue).getUsage();
if (usage instanceof UsageInfo2UsageAdapter) {
final PsiElement element = ((UsageInfo2UsageAdapter)usage).getElement();
if (element != null) {
final PsiElement view = UsageToPsiElementProvider.findAppropriateParentFrom(element);
return view == null ? element : view;
}
}
}
return null;
}
private static int calcColumnCount(@NotNull List<UsageNode> data) {
return data.isEmpty() || data.get(0) instanceof ShowUsagesAction.StringNode ? 1 : 4;
}
@NotNull
MyModel setTableModel(@NotNull UsageViewImpl usageView,
@NotNull final List<UsageNode> data,
@NotNull AtomicInteger outOfScopeUsages,
@NotNull SearchScope searchScope) {
ApplicationManager.getApplication().assertIsDispatchThread();
final int columnCount = calcColumnCount(data);
MyModel model = getModel() instanceof MyModel ? (MyModel)getModel() : null;
if (model == null || model.getColumnCount() != columnCount) {
model = new MyModel(data, columnCount);
setModel(model);
ShowUsagesTableCellRenderer renderer = new ShowUsagesTableCellRenderer(usageView, outOfScopeUsages, searchScope);
for (int i = 0; i < getColumnModel().getColumnCount(); i++) {
TableColumn column = getColumnModel().getColumn(i);
column.setPreferredWidth(0);
column.setCellRenderer(renderer);
}
}
return model;
}
private static class MySpeedSearch extends SpeedSearchBase<JTable> {
MySpeedSearch(@NotNull ShowUsagesTable table) {
super(table);
}
@Override
protected int getSelectedIndex() {
return getTable().getSelectedRow();
}
@Override
protected int convertIndexToModel(int viewIndex) {
return getTable().convertRowIndexToModel(viewIndex);
}
@NotNull
@Override
protected Object[] getAllElements() {
return ((MyModel)getTable().getModel()).getItems().toArray();
}
@Override
protected String getElementText(@NotNull Object element) {
if (!(element instanceof UsageNode)) return element.toString();
UsageNode node = (UsageNode)element;
if (node instanceof ShowUsagesAction.StringNode) return "";
Usage usage = node.getUsage();
if (usage == MORE_USAGES_SEPARATOR || usage == USAGES_OUTSIDE_SCOPE_SEPARATOR) return "";
GroupNode group = (GroupNode)node.getParent();
String groupText = group == null ? "" : group.getGroup().getText(null);
return groupText + usage.getPresentation().getPlainText();
}
@Override
protected void selectElement(Object element, String selectedText) {
List<UsageNode> data = ((MyModel)getTable().getModel()).getItems();
int i = data.indexOf(element);
if (i == -1) return;
final int viewRow = getTable().convertRowIndexToView(i);
getTable().getSelectionModel().setSelectionInterval(viewRow, viewRow);
TableUtil.scrollSelectionToVisible(getTable());
}
private ShowUsagesTable getTable() {
return (ShowUsagesTable)myComponent;
}
}
static class MyModel extends ListTableModel<UsageNode> implements ModelDiff.Model<Object> {
private MyModel(@NotNull List<UsageNode> data, int cols) {
super(cols(cols), data, 0);
}
@NotNull
private static ColumnInfo[] cols(int cols) {
ColumnInfo<UsageNode, UsageNode> o = new ColumnInfo<UsageNode, UsageNode>("") {
@Nullable
@Override
public UsageNode valueOf(UsageNode node) {
return node;
}
};
List<ColumnInfo<UsageNode, UsageNode>> list = Collections.nCopies(cols, o);
return list.toArray(ColumnInfo.EMPTY_ARRAY);
}
@Override
public void addToModel(int idx, Object element) {
UsageNode node = element instanceof UsageNode ? (UsageNode)element : ShowUsagesAction.createStringNode(element);
if (idx < getRowCount()) {
insertRow(idx, node);
}
else {
addRow(node);
}
}
@Override
public void removeRangeFromModel(int start, int end) {
for (int i=end; i>=start; i--) {
removeRow(i);
}
}
}
}
@@ -16,10 +16,9 @@
package com.intellij.find.actions;
import com.intellij.icons.AllIcons;
import com.intellij.ide.actions.Switcher;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.IconLoader;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.newvfs.VfsPresentationUtil;
import com.intellij.psi.search.SearchScope;
@@ -36,7 +35,6 @@ import com.intellij.usages.impl.UsageNode;
import com.intellij.usages.impl.UsageViewImpl;
import com.intellij.usages.impl.UsageViewManagerImpl;
import com.intellij.usages.rules.UsageInFile;
import com.intellij.util.IconUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.ui.EmptyIcon;
import com.intellij.util.ui.JBUI;
@@ -51,9 +49,6 @@ import javax.swing.table.TableColumnModel;
import java.awt.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author cdr
*/
class ShowUsagesTableCellRenderer implements TableCellRenderer {
private final UsageViewImpl myUsageView;
@NotNull private final AtomicInteger myOutOfScopeUsages;
@@ -65,12 +60,13 @@ class ShowUsagesTableCellRenderer implements TableCellRenderer {
mySearchScope = searchScope;
}
private static final int FILE_GROUP_COL = 0;
private static final int LINE_NUMBER_COL = 1;
private static final int USAGE_TEXT_COL = 2;
private static final int CURRENT_ASTERISK_COL = 0;
private static final int FILE_GROUP_COL = 1;
private static final int LINE_NUMBER_COL = 2;
private static final int USAGE_TEXT_COL = 3;
@Override
public Component getTableCellRendererComponent(JTable list, Object value, boolean isSelected, boolean hasFocus, int row,
@MagicConstant(intValues = {FILE_GROUP_COL, LINE_NUMBER_COL, USAGE_TEXT_COL}) int column) {
@MagicConstant(intValues = {CURRENT_ASTERISK_COL, FILE_GROUP_COL, LINE_NUMBER_COL, USAGE_TEXT_COL}) int column) {
UsageNode usageNode = value instanceof UsageNode ? (UsageNode)value : null;
Usage usage = usageNode == null ? null : usageNode.getUsage();
@@ -85,14 +81,14 @@ class ShowUsagesTableCellRenderer implements TableCellRenderer {
textChunks.append(ObjectUtils.notNull(value, "").toString(), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
return textComponentSpanningWholeRow(textChunks, rowBackground, rowForeground, column, list);
}
if (usage == ShowUsagesAction.MORE_USAGES_SEPARATOR) {
if (usage == ShowUsagesTable.MORE_USAGES_SEPARATOR) {
SimpleColoredComponent textChunks = new SimpleColoredComponent();
textChunks.append("...<");
textChunks.append("more usages", SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
textChunks.append(">...");
return textComponentSpanningWholeRow(textChunks, rowBackground, rowForeground, column, list);
}
if (usage == ShowUsagesAction.USAGES_OUTSIDE_SCOPE_SEPARATOR) {
if (usage == ShowUsagesTable.USAGES_OUTSIDE_SCOPE_SEPARATOR) {
SimpleColoredComponent textChunks = new SimpleColoredComponent();
textChunks.append("...<");
textChunks.append(UsageViewManagerImpl.outOfScopeMessage(myOutOfScopeUsages.get(), mySearchScope), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES);
@@ -100,9 +96,7 @@ class ShowUsagesTableCellRenderer implements TableCellRenderer {
return textComponentSpanningWholeRow(textChunks, rowBackground, rowForeground, column, list);
}
// want to be able to right-align the "current" word
LayoutManager layout = column == USAGE_TEXT_COL
? new BorderLayout() : new FlowLayout(column == LINE_NUMBER_COL ? FlowLayout.RIGHT : FlowLayout.LEFT, 0, 0) {
JPanel panel = new JPanel(new FlowLayout(column == LINE_NUMBER_COL ? FlowLayout.RIGHT : FlowLayout.LEFT, 0, 0) {
@Override
public void layoutContainer(Container container) {
super.layoutContainer(container);
@@ -112,8 +106,7 @@ class ShowUsagesTableCellRenderer implements TableCellRenderer {
component.setBounds(b.x, b.y, b.width, container.getSize().height - insets.top - insets.bottom);
}
}
};
JPanel panel = new JPanel(layout);
});
panel.setFont(null);
// greying the current usage the "find usages" was originated from
@@ -128,21 +121,29 @@ class ShowUsagesTableCellRenderer implements TableCellRenderer {
panel.setBackground(rowBackground);
panel.setForeground(rowForeground);
if (column == FILE_GROUP_COL) {
appendGroupText(list, (GroupNode)usageNode.getParent(), panel, fileBgColor, isSelected);
}
else {
SimpleColoredComponent textChunks = new SimpleColoredComponent();
UsagePresentation presentation = usage.getPresentation();
TextChunk[] text = presentation.getText();
SimpleColoredComponent textChunks = new SimpleColoredComponent();
UsagePresentation presentation = usage.getPresentation();
TextChunk[] text = presentation.getText();
if (column == LINE_NUMBER_COL) {
switch(column) {
case CURRENT_ASTERISK_COL:
if (isOriginUsage) panel.add(Switcher.createPaleLabel(" *"));
break;
case FILE_GROUP_COL:
appendGroupText(list, (GroupNode)usageNode.getParent(), panel, fileBgColor, isSelected);
break;
case LINE_NUMBER_COL: {
if (text.length != 0) {
TextChunk chunk = text[0];
textChunks.append(chunk.getText(), getAttributes(isSelected, fileBgColor, selectionBg, selectionFg, chunk));
}
SpeedSearchUtil.applySpeedSearchHighlighting(list, textChunks, false, isSelected);
panel.add(textChunks);
break;
}
else if (column == USAGE_TEXT_COL) {
case USAGE_TEXT_COL: {
Icon icon = presentation.getIcon();
textChunks.setIcon(icon == null ? EmptyIcon.ICON_16 : icon);
textChunks.append("").appendTextPadding(JBUI.scale(16 + 5));
@@ -150,28 +151,14 @@ class ShowUsagesTableCellRenderer implements TableCellRenderer {
TextChunk chunk = text[i];
textChunks.append(chunk.getText(), getAttributes(isSelected, fileBgColor, selectionBg, selectionFg, chunk));
}
}
else {
assert false : column;
}
SpeedSearchUtil.applySpeedSearchHighlighting(list, textChunks, false, isSelected);
SpeedSearchUtil.applySpeedSearchHighlighting(list, textChunks, false, isSelected);
panel.add(textChunks);
if (isOriginUsage && column == USAGE_TEXT_COL) {
SimpleColoredComponent origin = new SimpleColoredComponent();
origin.setIconTextGap(JBUI.scale(5)); // for this particular icon it looks better
Icon arrowLeftIcon = ObjectUtils.assertNotNull(IconLoader.getDisabledIcon(AllIcons.General.ArrowLeft));
origin.setIcon(isSelected ? slightlyDifferentColor(arrowLeftIcon) : arrowLeftIcon);
// use attributes of "line number" to show "Current" word
SimpleTextAttributes attributes =
text.length == 0 ? SimpleTextAttributes.REGULAR_ATTRIBUTES.derive(-1, new Color(0x808080), null, null) :
getAttributes(isSelected, fileBgColor, selectionBg, selectionFg, text[0]);
origin.append("Current", attributes);
origin.appendTextPadding(JBUI.scale(45));
panel.add(origin, BorderLayout.EAST);
panel.add(textChunks);
break;
}
default:
throw new IllegalStateException("unknown column: " + column);
}
return panel;
@@ -184,10 +171,10 @@ class ShowUsagesTableCellRenderer implements TableCellRenderer {
ColorUtil.hackBrightness(back, 1, 1/1.05f); // Olga insisted on very-pale almost invisible gray. oh well
}
@NotNull
private static Icon slightlyDifferentColor(@NotNull Icon icon) {
return EditorColorsManager.getInstance().isDarkEditor() ? IconUtil.brighter(icon, 3) : IconUtil.darker(icon, 8);
}
//@NotNull
//private static Icon slightlyDifferentColor(@NotNull Icon icon) {
// return EditorColorsManager.getInstance().isDarkEditor() ? IconUtil.brighter(icon, 3) : IconUtil.darker(icon, 8);
//}
@NotNull
private static SimpleTextAttributes getAttributes(boolean isSelected, Color fileBgColor, Color selectionBg, Color selectionFg, @NotNull TextChunk chunk) {
@@ -459,20 +459,11 @@ public class Switcher extends AnAction implements DumbAware {
final VirtualFilesRenderer filesRenderer = new VirtualFilesRenderer(this) {
JPanel myPanel = new JPanel(new BorderLayout());
JLabel myLabel = new JLabel() {
@Override
protected void paintComponent(@NotNull Graphics g) {
GraphicsConfig config = new GraphicsConfig(g);
((Graphics2D)g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f));
super.paintComponent(g);
config.restore();
}
};
JLabel myLabel = createPaleLabel("* ");
{
myPanel.setOpaque(false);
myPanel.setBackground(UIUtil.getListBackground());
myLabel.setText("* ");
}
@NotNull
@@ -1249,4 +1240,17 @@ public class Switcher extends AnAction implements DumbAware {
return myNameForRendering;
}
}
@NotNull
public static JLabel createPaleLabel(@NotNull String text) {
return new JLabel(text) {
@Override
protected void paintComponent(@NotNull Graphics g) {
GraphicsConfig config = new GraphicsConfig(g);
((Graphics2D)g).setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.3f));
super.paintComponent(g);
config.restore();
}
};
}
}