| /* |
| * 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.find.actions; |
| |
| import com.intellij.codeInsight.hint.HintManager; |
| import com.intellij.codeInsight.hint.HintUtil; |
| import com.intellij.featureStatistics.FeatureUsageTracker; |
| import com.intellij.find.FindManager; |
| import com.intellij.find.FindSettings; |
| import com.intellij.find.findUsages.*; |
| import com.intellij.find.impl.FindManagerImpl; |
| import com.intellij.icons.AllIcons; |
| import com.intellij.ide.DataManager; |
| import com.intellij.ide.util.gotoByName.ModelDiff; |
| import com.intellij.openapi.Disposable; |
| import com.intellij.openapi.actionSystem.*; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.fileEditor.FileEditor; |
| import com.intellij.openapi.fileEditor.FileEditorLocation; |
| import com.intellij.openapi.fileEditor.TextEditor; |
| import com.intellij.openapi.keymap.KeymapUtil; |
| import com.intellij.openapi.progress.ProgressIndicator; |
| import com.intellij.openapi.project.DumbAwareAction; |
| import com.intellij.openapi.project.Project; |
| 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.util.Comparing; |
| import com.intellij.openapi.util.Computable; |
| import com.intellij.openapi.util.Condition; |
| import com.intellij.openapi.util.Disposer; |
| 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.search.GlobalSearchScope; |
| import com.intellij.psi.search.PsiElementProcessor; |
| 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.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.PlatformIcons; |
| import com.intellij.util.Processor; |
| import com.intellij.util.messages.MessageBusConnection; |
| import com.intellij.util.ui.AsyncProcessIcon; |
| import com.intellij.util.ui.ColumnInfo; |
| import com.intellij.util.ui.ListTableModel; |
| 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.event.ListSelectionEvent; |
| import javax.swing.event.ListSelectionListener; |
| import javax.swing.table.TableColumn; |
| import java.awt.*; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.ActionListener; |
| 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 { |
| private final boolean showSettingsDialogBefore; |
| private static final int USAGES_PAGE_SIZE = 100; |
| |
| static final Usage MORE_USAGES_SEPARATOR = NullUsage.INSTANCE; |
| private static final UsageNode MORE_USAGES_SEPARATOR_NODE = UsageViewImpl.NULL_NODE; |
| static final Usage USAGES_OUTSIDE_SCOPE_SEPARATOR = new UsageAdapter(); |
| private static final UsageNode USAGES_OUTSIDE_SCOPE_NODE = new UsageNode(USAGES_OUTSIDE_SCOPE_SEPARATOR, new UsageViewTreeModelBuilder(new UsageViewPresentation(), UsageTarget.EMPTY_ARRAY)); |
| |
| private static final Comparator<UsageNode> USAGE_NODE_COMPARATOR = new Comparator<UsageNode>() { |
| @Override |
| public int compare(UsageNode c1, UsageNode c2) { |
| if (c1 instanceof StringNode) return 1; |
| if (c2 instanceof StringNode) return -1; |
| 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; |
| if (weight1 != weight2) return weight1 - weight2; |
| |
| VirtualFile v1 = UsageListCellRenderer.getVirtualFile(o1); |
| VirtualFile v2 = UsageListCellRenderer.getVirtualFile(o2); |
| String name1 = v1 == null ? null : v1.getName(); |
| String name2 = v2 == null ? null : v2.getName(); |
| int i = Comparing.compare(name1, name2); |
| if (i != 0) return i; |
| |
| if (o1 instanceof Comparable && o2 instanceof Comparable) { |
| return ((Comparable)o1).compareTo(o2); |
| } |
| |
| FileEditorLocation loc1 = o1.getLocation(); |
| FileEditorLocation loc2 = o2.getLocation(); |
| return Comparing.compare(loc1, loc2); |
| } |
| }; |
| private static final Runnable HIDE_HINTS_ACTION = new Runnable() { |
| @Override |
| public void run() { |
| hideHints(); |
| } |
| }; |
| @NotNull private final UsageViewSettings myUsageViewSettings; |
| @Nullable private Runnable mySearchEverywhereRunnable; |
| |
| // used from plugin.xml |
| @SuppressWarnings({"UnusedDeclaration"}) |
| public ShowUsagesAction() { |
| this(false); |
| } |
| |
| private ShowUsagesAction(boolean showDialogBefore) { |
| setInjectedContext(true); |
| showSettingsDialogBefore = showDialogBefore; |
| |
| final UsageViewSettings usageViewSettings = UsageViewSettings.getInstance(); |
| myUsageViewSettings = new UsageViewSettings(); |
| myUsageViewSettings.loadState(usageViewSettings); |
| myUsageViewSettings.GROUP_BY_FILE_STRUCTURE = false; |
| myUsageViewSettings.GROUP_BY_MODULE = false; |
| myUsageViewSettings.GROUP_BY_PACKAGE = false; |
| myUsageViewSettings.GROUP_BY_USAGE_TYPE = false; |
| myUsageViewSettings.GROUP_BY_SCOPE = false; |
| } |
| |
| |
| @Override |
| public void actionPerformed(@NotNull AnActionEvent e) { |
| final Project project = e.getData(CommonDataKeys.PROJECT); |
| if (project == null) return; |
| |
| Runnable searchEverywhere = mySearchEverywhereRunnable; |
| mySearchEverywhereRunnable = null; |
| hideHints(); |
| |
| if (searchEverywhere != null) { |
| searchEverywhere.run(); |
| return; |
| } |
| |
| final RelativePoint popupPosition = JBPopupFactory.getInstance().guessBestPopupLocation(e.getDataContext()); |
| PsiDocumentManager.getInstance(project).commitAllDocuments(); |
| FeatureUsageTracker.getInstance().triggerFeatureUsed("navigation.goto.usages"); |
| |
| UsageTarget[] usageTargets = e.getData(UsageView.USAGE_TARGETS_KEY); |
| final Editor editor = e.getData(CommonDataKeys.EDITOR); |
| if (usageTargets == null) { |
| FindUsagesAction.chooseAmbiguousTargetAndPerform(project, editor, new PsiElementProcessor<PsiElement>() { |
| @Override |
| public boolean execute(@NotNull final PsiElement element) { |
| startFindUsages(element, popupPosition, editor, USAGES_PAGE_SIZE); |
| return false; |
| } |
| }); |
| } |
| else { |
| PsiElement element = ((PsiElementUsageTarget)usageTargets[0]).getElement(); |
| if (element != null) { |
| startFindUsages(element, popupPosition, editor, USAGES_PAGE_SIZE); |
| } |
| } |
| } |
| |
| private static void hideHints() { |
| HintManager.getInstance().hideHints(HintManager.HIDE_BY_ANY_KEY, false, false); |
| } |
| |
| private void startFindUsages(@NotNull PsiElement element, @NotNull RelativePoint popupPosition, Editor editor, int maxUsages) { |
| Project project = element.getProject(); |
| FindUsagesManager findUsagesManager = ((FindManagerImpl)FindManager.getInstance(project)).getFindUsagesManager(); |
| FindUsagesHandler handler = findUsagesManager.getFindUsagesHandler(element, false); |
| if (handler == null) return; |
| if (showSettingsDialogBefore) { |
| showDialogAndFindUsages(handler, popupPosition, editor, maxUsages); |
| return; |
| } |
| 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) { |
| ApplicationManager.getApplication().assertIsDispatchThread(); |
| final UsageViewSettings usageViewSettings = UsageViewSettings.getInstance(); |
| final UsageViewSettings savedGlobalSettings = new UsageViewSettings(); |
| |
| savedGlobalSettings.loadState(usageViewSettings); |
| usageViewSettings.loadState(myUsageViewSettings); |
| |
| final Project project = handler.getProject(); |
| UsageViewManager manager = UsageViewManager.getInstance(project); |
| FindUsagesManager findUsagesManager = ((FindManagerImpl)FindManager.getInstance(project)).getFindUsagesManager(); |
| final UsageViewPresentation presentation = findUsagesManager.createPresentation(handler, options); |
| presentation.setDetachedMode(true); |
| final UsageViewImpl usageView = (UsageViewImpl)manager.createUsageView(UsageTarget.EMPTY_ARRAY, Usage.EMPTY_ARRAY, presentation, null); |
| |
| Disposer.register(usageView, new Disposable() { |
| @Override |
| public void dispose() { |
| myUsageViewSettings.loadState(usageViewSettings); |
| usageViewSettings.loadState(savedGlobalSettings); |
| } |
| }); |
| final AtomicInteger outOfScopeUsages = new AtomicInteger(); |
| |
| final List<Usage> usages = new ArrayList<Usage>(); |
| final Set<UsageNode> visibleNodes = new LinkedHashSet<UsageNode>(); |
| |
| final MyTable table = new MyTable(); |
| final AsyncProcessIcon processIcon = new AsyncProcessIcon("xxx"); |
| |
| addUsageNodes(usageView.getRoot(), usageView, new ArrayList<UsageNode>()); |
| |
| TableScrollingUtil.installActions(table); |
| |
| final List<UsageNode> data = collectData(usages, visibleNodes, usageView, presentation); |
| setTableModel(table, usageView, data, outOfScopeUsages, options.searchScope); |
| |
| SpeedSearchBase<JTable> speedSearch = new MySpeedSearch(table); |
| speedSearch.setComparator(new SpeedSearchComparator(false)); |
| |
| final JBPopup popup = createUsagePopup(usages, visibleNodes, handler, editor, popupPosition, |
| maxUsages, usageView, options, table, presentation, processIcon); |
| |
| Disposer.register(popup, usageView); |
| |
| // show popup only if find usages takes more than 300ms, otherwise it would flicker needlessly |
| Alarm alarm = new Alarm(usageView); |
| alarm.addRequest(new Runnable() { |
| @Override |
| public void run() { |
| showPopupIfNeedTo(popup, popupPosition); |
| } |
| }, 300); |
| |
| final PingEDT pingEDT = new PingEDT("Rebuild popup in EDT", new Condition<Object>() { |
| @Override |
| public boolean value(Object o) { |
| return popup.isDisposed(); |
| } |
| }, 100, new Runnable() { |
| @Override |
| public void run() { |
| if (popup.isDisposed()) return; |
| |
| final List<UsageNode> nodes = new ArrayList<UsageNode>(); |
| List<Usage> copy; |
| synchronized (usages) { |
| // open up popup as soon as several usages 've been found |
| if (!popup.isVisible() && (usages.size() <= 1 || !showPopupIfNeedTo(popup, popupPosition))) { |
| return; |
| } |
| addUsageNodes(usageView.getRoot(), usageView, nodes); |
| copy = new ArrayList<Usage>(usages); |
| } |
| |
| rebuildPopup(usageView, copy, nodes, table, popup, presentation, popupPosition, !processIcon.isDisposed(), outOfScopeUsages, |
| options.searchScope); |
| } |
| }); |
| |
| final MessageBusConnection messageBusConnection = project.getMessageBus().connect(usageView); |
| messageBusConnection.subscribe(UsageFilteringRuleProvider.RULES_CHANGED, new Runnable() { |
| @Override |
| public void run() { |
| pingEDT.ping(); |
| } |
| }); |
| |
| final UsageTarget[] myUsageTarget = {new PsiElement2UsageTargetAdapter(handler.getPsiElement())}; |
| Processor<Usage> collect = new Processor<Usage>() { |
| @Override |
| public boolean process(@NotNull final Usage usage) { |
| if (!UsageViewManagerImpl.isInScope(usage, options.searchScope)) { |
| if (outOfScopeUsages.getAndIncrement() == 0) { |
| visibleNodes.add(USAGES_OUTSIDE_SCOPE_NODE); |
| usages.add(USAGES_OUTSIDE_SCOPE_SEPARATOR); |
| } |
| return true; |
| } |
| synchronized (usages) { |
| if (visibleNodes.size() >= maxUsages) return false; |
| if(UsageViewManager.isSelfUsage(usage, myUsageTarget)) return true; |
| UsageNode node = ApplicationManager.getApplication().runReadAction(new Computable<UsageNode>() { |
| @Override |
| public UsageNode compute() { |
| return usageView.doAppendUsage(usage); |
| } |
| }); |
| usages.add(usage); |
| if (node != null) { |
| visibleNodes.add(node); |
| boolean continueSearch = true; |
| if (visibleNodes.size() == maxUsages) { |
| visibleNodes.add(MORE_USAGES_SEPARATOR_NODE); |
| usages.add(MORE_USAGES_SEPARATOR); |
| continueSearch = false; |
| } |
| pingEDT.ping(); |
| |
| return continueSearch; |
| } |
| } |
| |
| return true; |
| } |
| }; |
| |
| final ProgressIndicator indicator = FindUsagesManager.startProcessUsages(handler, handler.getPrimaryElements(), handler.getSecondaryElements(), collect, options, new Runnable() { |
| @Override |
| public void run() { |
| ApplicationManager.getApplication().invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| Disposer.dispose(processIcon); |
| Container parent = processIcon.getParent(); |
| parent.remove(processIcon); |
| parent.repaint(); |
| pingEDT.ping(); // repaint title |
| synchronized (usages) { |
| if (visibleNodes.isEmpty()) { |
| if (usages.isEmpty()) { |
| String text = UsageViewBundle.message("no.usages.found.in", searchScopePresentableName(options)); |
| hint(editor, text, handler, popupPosition, maxUsages, options, false); |
| popup.cancel(); |
| } |
| else { |
| // all usages filtered out |
| } |
| } |
| else if (visibleNodes.size() == 1) { |
| if (usages.size() == 1) { |
| //the only usage |
| Usage usage = visibleNodes.iterator().next().getUsage(); |
| if (usage == USAGES_OUTSIDE_SCOPE_SEPARATOR) { |
| hint(editor, UsageViewManagerImpl.outOfScopeMessage(outOfScopeUsages.get(), options.searchScope), handler, popupPosition, maxUsages, options, true); |
| } |
| else { |
| String message = UsageViewBundle.message("show.usages.only.usage", searchScopePresentableName(options)); |
| navigateAndHint(usage, message, handler, popupPosition, maxUsages, options); |
| } |
| popup.cancel(); |
| } |
| else { |
| assert usages.size() > 1 : usages; |
| // usage view can filter usages down to one |
| Usage visibleUsage = visibleNodes.iterator().next().getUsage(); |
| if (areAllUsagesInOneLine(visibleUsage, usages)) { |
| String hint = UsageViewBundle.message("all.usages.are.in.this.line", usages.size(), searchScopePresentableName(options)); |
| navigateAndHint(visibleUsage, hint, handler, popupPosition, maxUsages, options); |
| popup.cancel(); |
| } |
| } |
| } |
| else { |
| String title = presentation.getTabText(); |
| boolean shouldShowMoreSeparator = visibleNodes.contains(MORE_USAGES_SEPARATOR_NODE); |
| String fullTitle = getFullTitle(usages, title, shouldShowMoreSeparator, visibleNodes.size() - (shouldShowMoreSeparator ? 1 : 0), false); |
| ((AbstractPopup)popup).setCaption(fullTitle); |
| } |
| } |
| } |
| }, project.getDisposed()); |
| } |
| }); |
| Disposer.register(popup, new Disposable() { |
| @Override |
| public void dispose() { |
| indicator.cancel(); |
| } |
| }); |
| } |
| |
| @NotNull |
| private 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(new ColumnInfo[list.size()]); |
| } |
| |
| @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); |
| return true; |
| } |
| else { |
| return false; |
| } |
| } |
| |
| |
| @NotNull |
| private JComponent createHintComponent(@NotNull String text, |
| @NotNull final FindUsagesHandler handler, |
| @NotNull final RelativePoint popupPosition, |
| final Editor editor, |
| @NotNull final Runnable cancelAction, |
| final int maxUsages, |
| @NotNull final FindUsagesOptions options, |
| boolean isWarning) { |
| JComponent label = HintUtil.createInformationLabel(suggestSecondInvocation(options, handler, text + " ")); |
| if (isWarning) { |
| label.setBackground(MessageType.WARNING.getPopupBackground()); |
| } |
| InplaceButton button = createSettingsButton(handler, popupPosition, editor, maxUsages, cancelAction); |
| |
| JPanel panel = new JPanel(new BorderLayout()) { |
| @Override |
| public void addNotify() { |
| mySearchEverywhereRunnable = new Runnable() { |
| @Override |
| public void run() { |
| searchEverywhere(options, handler, editor, popupPosition, maxUsages); |
| } |
| }; |
| super.addNotify(); |
| } |
| |
| @Override |
| public void removeNotify() { |
| mySearchEverywhereRunnable = null; |
| super.removeNotify(); |
| } |
| }; |
| button.setBackground(label.getBackground()); |
| panel.setBackground(label.getBackground()); |
| label.setOpaque(false); |
| label.setBorder(null); |
| panel.setBorder(HintUtil.createHintBorder()); |
| panel.add(label, BorderLayout.CENTER); |
| panel.add(button, BorderLayout.EAST); |
| return panel; |
| } |
| |
| @NotNull |
| private InplaceButton createSettingsButton(@NotNull final FindUsagesHandler handler, |
| @NotNull final RelativePoint popupPosition, |
| final Editor editor, |
| final int maxUsages, |
| @NotNull final Runnable cancelAction) { |
| String shortcutText = ""; |
| KeyboardShortcut shortcut = UsageViewImpl.getShowUsagesWithSettingsShortcut(); |
| if (shortcut != null) { |
| shortcutText = "(" + KeymapUtil.getShortcutText(shortcut) + ")"; |
| } |
| return new InplaceButton("Settings..." + shortcutText, AllIcons.General.Settings, new ActionListener() { |
| @Override |
| public void actionPerformed(ActionEvent e) { |
| SwingUtilities.invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| showDialogAndFindUsages(handler, popupPosition, editor, maxUsages); |
| } |
| }); |
| cancelAction.run(); |
| } |
| }); |
| } |
| |
| private void showDialogAndFindUsages(@NotNull FindUsagesHandler handler, |
| @NotNull RelativePoint popupPosition, |
| Editor editor, |
| int maxUsages) { |
| AbstractFindUsagesDialog dialog = handler.getFindUsagesDialog(false, false, false); |
| dialog.show(); |
| if (dialog.isOK()) { |
| dialog.calcFindUsagesOptions(); |
| FindUsagesOptions options = handler.getFindUsagesOptions(DataManager.getInstance().getDataContext()); |
| showElementUsages(editor, popupPosition, handler, maxUsages, options); |
| } |
| } |
| |
| @NotNull |
| private static String searchScopePresentableName(@NotNull FindUsagesOptions options) { |
| return options.searchScope.getDisplayName(); |
| } |
| |
| @NotNull |
| private JBPopup createUsagePopup(@NotNull final List<Usage> usages, |
| @NotNull Set<UsageNode> visibleNodes, |
| @NotNull final FindUsagesHandler handler, |
| final Editor editor, |
| @NotNull final RelativePoint popupPosition, |
| final int maxUsages, |
| @NotNull final UsageViewImpl usageView, |
| @NotNull final FindUsagesOptions options, |
| @NotNull final JTable table, |
| @NotNull final UsageViewPresentation presentation, |
| @NotNull final AsyncProcessIcon processIcon) { |
| 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)); |
| |
| PopupChooserBuilder builder = new PopupChooserBuilder(table); |
| final String title = presentation.getTabText(); |
| if (title != null) { |
| String result = getFullTitle(usages, title, false, visibleNodes.size() - 1, true); |
| builder.setTitle(result); |
| builder.setAdText(getSecondInvocationTitle(options, handler)); |
| } |
| |
| builder.setMovable(true).setResizable(true); |
| final AtomicReference<Object> selectedUsage = new AtomicReference<Object>(); |
| final AtomicBoolean moreUsagesSelected = new AtomicBoolean(); |
| final AtomicBoolean outsideScopeUsagesSelected = new AtomicBoolean(); |
| table.getSelectionModel().addListSelectionListener(new ListSelectionListener() { |
| @Override |
| public void valueChanged(ListSelectionEvent e) { |
| selectedUsage.set(null); |
| int[] selected = table.getSelectedRows(); |
| for (int i : selected) { |
| Object value = table.getValueAt(i, 0); |
| if (value instanceof UsageNode) { |
| Usage usage = ((UsageNode)value).getUsage(); |
| outsideScopeUsagesSelected.set(false); |
| moreUsagesSelected.set(false); |
| if (usage == USAGES_OUTSIDE_SCOPE_SEPARATOR) { |
| outsideScopeUsagesSelected.set(true); |
| selectedUsage.set(null); |
| } |
| else if (usage == MORE_USAGES_SEPARATOR) { |
| moreUsagesSelected.set(true); |
| selectedUsage.set(null); |
| } |
| else { |
| selectedUsage.set(usage instanceof UsageInfo2UsageAdapter ? ((UsageInfo2UsageAdapter)usage).getUsageInfo().copy() : usage); |
| } |
| break; |
| } |
| } |
| } |
| }); |
| builder.setMovable(true).setResizable(true); |
| builder.setItemChoosenCallback(new Runnable() { |
| @Override |
| public void run() { |
| 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; |
| } |
| Object usage = selectedUsage.get(); |
| if (usage instanceof UsageInfo) { |
| UsageViewUtil.navigateTo((UsageInfo)usage, true); |
| } |
| else if (usage instanceof Navigatable) { |
| ((Navigatable)usage).navigate(true); |
| } |
| } |
| }); |
| final JBPopup[] popup = new JBPopup[1]; |
| |
| KeyboardShortcut shortcut = UsageViewImpl.getShowUsagesWithSettingsShortcut(); |
| if (shortcut != null) { |
| new DumbAwareAction() { |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| popup[0].cancel(); |
| showDialogAndFindUsages(handler, popupPosition, editor, maxUsages); |
| } |
| }.registerCustomShortcutSet(new CustomShortcutSet(shortcut.getFirstKeyStroke()), table); |
| } |
| shortcut = getShowUsagesShortcut(); |
| if (shortcut != null) { |
| new DumbAwareAction() { |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| popup[0].cancel(); |
| searchEverywhere(options, handler, editor, popupPosition, maxUsages); |
| } |
| }.registerCustomShortcutSet(new CustomShortcutSet(shortcut.getFirstKeyStroke()), table); |
| } |
| |
| InplaceButton settingsButton = createSettingsButton(handler, popupPosition, editor, maxUsages, new Runnable() { |
| @Override |
| public void run() { |
| popup[0].cancel(); |
| } |
| }); |
| |
| ActiveComponent spinningProgress = new ActiveComponent() { |
| @Override |
| public void setActive(boolean active) { |
| } |
| |
| @Override |
| public JComponent getComponent() { |
| return processIcon; |
| } |
| }; |
| final DefaultActionGroup pinGroup = new DefaultActionGroup(); |
| final ActiveComponent pin = createPinButton(handler, usageView, options, popup, pinGroup); |
| builder.setCommandButton(new CompositeActiveComponent(spinningProgress, settingsButton, pin)); |
| |
| DefaultActionGroup toolbar = new DefaultActionGroup(); |
| usageView.addFilteringActions(toolbar); |
| |
| toolbar.add(UsageGroupingRuleProviderImpl.createGroupByFileStructureAction(usageView)); |
| ActionToolbar actionToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.USAGE_VIEW_TOOLBAR, toolbar, true); |
| actionToolbar.setReservePlaceAutoPopupIcon(false); |
| final JComponent toolBar = actionToolbar.getComponent(); |
| toolBar.setOpaque(false); |
| builder.setSettingButton(toolBar); |
| |
| popup[0] = builder.createPopup(); |
| JComponent content = popup[0].getContent(); |
| |
| myWidth = (int)(toolBar.getPreferredSize().getWidth() |
| + new JLabel(getFullTitle(usages, title, false, visibleNodes.size() - 1, true)).getPreferredSize().getWidth() |
| + settingsButton.getPreferredSize().getWidth()); |
| myWidth = -1; |
| for (AnAction action : toolbar.getChildren(null)) { |
| action.unregisterCustomShortcutSet(usageView.getComponent()); |
| action.registerCustomShortcutSet(action.getShortcutSet(), content); |
| } |
| |
| for (AnAction action : pinGroup.getChildren(null)) { |
| action.unregisterCustomShortcutSet(usageView.getComponent()); |
| action.registerCustomShortcutSet(action.getShortcutSet(), content); |
| } |
| |
| return popup[0]; |
| } |
| |
| private ActiveComponent createPinButton(@NotNull final FindUsagesHandler handler, |
| @NotNull final UsageViewImpl usageView, |
| @NotNull final FindUsagesOptions options, |
| @NotNull final JBPopup[] popup, |
| @NotNull DefaultActionGroup pinGroup) { |
| final AnAction pinAction = |
| new AnAction("Open Find Usages Toolwindow", "Show all usages in a separate toolwindow", AllIcons.General.AutohideOff) { |
| { |
| AnAction action = ActionManager.getInstance().getAction(IdeActions.ACTION_FIND_USAGES); |
| setShortcutSet(action.getShortcutSet()); |
| } |
| |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| hideHints(); |
| popup[0].cancel(); |
| FindUsagesManager findUsagesManager = ((FindManagerImpl)FindManager.getInstance(usageView.getProject())).getFindUsagesManager(); |
| findUsagesManager.findUsages(handler.getPrimaryElements(), handler.getSecondaryElements(), handler, options, |
| FindSettings.getInstance().isSkipResultsWithOneUsage()); |
| } |
| }; |
| pinGroup.add(pinAction); |
| final ActionToolbar pinToolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.USAGE_VIEW_TOOLBAR, pinGroup, true); |
| pinToolbar.setReservePlaceAutoPopupIcon(false); |
| final JComponent pinToolBar = pinToolbar.getComponent(); |
| pinToolBar.setBorder(null); |
| pinToolBar.setOpaque(false); |
| |
| return new ActiveComponent() { |
| @Override |
| public void setActive(boolean active) { |
| } |
| |
| @Override |
| public JComponent getComponent() { |
| return pinToolBar; |
| } |
| }; |
| } |
| |
| @NotNull |
| private static String getFullTitle(@NotNull List<Usage> usages, |
| @NotNull String title, |
| boolean hadMoreSeparator, |
| int visibleNodesCount, |
| boolean findUsagesInProgress) { |
| String s; |
| if (hadMoreSeparator) { |
| s = "<b>Some</b> " + title + " " + "<b>(Only " + visibleNodesCount + " usages shown" +(findUsagesInProgress ? " so far" : "")+")</b>"; |
| } |
| else { |
| s = title + " (" + UsageViewBundle.message("usages.n", usages.size()) + (findUsagesInProgress ? " so far" : "") + ")"; |
| } |
| return "<html><nobr>" + s + "</nobr></html>"; |
| } |
| |
| @NotNull |
| private static String suggestSecondInvocation(@NotNull FindUsagesOptions options, @NotNull FindUsagesHandler handler, @NotNull String text) { |
| final String title = getSecondInvocationTitle(options, handler); |
| |
| if (title != null) { |
| text += "<br><small> " + title + "</small>"; |
| } |
| return XmlStringUtil.wrapInHtml(text); |
| } |
| |
| @Nullable |
| private static String getSecondInvocationTitle(@NotNull FindUsagesOptions options, @NotNull FindUsagesHandler handler) { |
| if (getShowUsagesShortcut() != null) { |
| GlobalSearchScope maximalScope = FindUsagesManager.getMaximalScope(handler); |
| if (!options.searchScope.equals(maximalScope)) { |
| return "Press " + KeymapUtil.getShortcutText(getShowUsagesShortcut()) + " again to search in " + maximalScope.getDisplayName(); |
| } |
| } |
| return null; |
| } |
| |
| private void searchEverywhere(@NotNull FindUsagesOptions options, |
| @NotNull FindUsagesHandler handler, |
| Editor editor, |
| @NotNull RelativePoint popupPosition, |
| int maxUsages) { |
| FindUsagesOptions cloned = options.clone(); |
| cloned.searchScope = FindUsagesManager.getMaximalScope(handler); |
| showElementUsages(editor, popupPosition, handler, maxUsages, cloned); |
| } |
| |
| @Nullable |
| private static KeyboardShortcut getShowUsagesShortcut() { |
| return ActionManager.getInstance().getKeyboardShortcut("ShowUsages"); |
| } |
| |
| private static int filtered(@NotNull List<Usage> usages, @NotNull UsageViewImpl usageView) { |
| int count=0; |
| for (Usage usage : usages) { |
| if (!usageView.isVisible(usage)) count++; |
| } |
| return count; |
| } |
| |
| private static int getUsageOffset(@NotNull Usage usage) { |
| if (!(usage instanceof UsageInfo2UsageAdapter)) return -1; |
| PsiElement element = ((UsageInfo2UsageAdapter)usage).getElement(); |
| if (element == null) return -1; |
| return element.getTextRange().getStartOffset(); |
| } |
| |
| private static boolean areAllUsagesInOneLine(@NotNull Usage visibleUsage, @NotNull List<Usage> usages) { |
| Editor editor = getEditorFor(visibleUsage); |
| if (editor == null) return false; |
| int offset = getUsageOffset(visibleUsage); |
| if (offset == -1) return false; |
| int lineNumber = editor.getDocument().getLineNumber(offset); |
| for (Usage other : usages) { |
| Editor otherEditor = getEditorFor(other); |
| if (otherEditor != editor) return false; |
| int otherOffset = getUsageOffset(other); |
| if (otherOffset == -1) return false; |
| int otherLine = otherEditor.getDocument().getLineNumber(otherOffset); |
| if (otherLine != lineNumber) return false; |
| } |
| 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.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<Usage> usages, |
| @NotNull Collection<UsageNode> visibleNodes, |
| @NotNull UsageViewImpl usageView, |
| @NotNull UsageViewPresentation presentation) { |
| @NotNull List<UsageNode> data = new ArrayList<UsageNode>(); |
| int filtered = filtered(usages, usageView); |
| if (filtered != 0) { |
| data.add(createStringNode(UsageViewBundle.message("usages.were.filtered.out", filtered))); |
| } |
| data.addAll(visibleNodes); |
| if (data.isEmpty()) { |
| String progressText = StringUtil.escapeXml(UsageViewManagerImpl.getProgressTitle(presentation)); |
| data.add(createStringNode(progressText)); |
| } |
| Collections.sort(data, USAGE_NODE_COMPARATOR); |
| return data; |
| } |
| |
| private static int calcMaxWidth(JTable table) { |
| int colsNum = table.getColumnModel().getColumnCount(); |
| |
| int totalWidth = 0; |
| for (int col = 0; col < colsNum -1; col++) { |
| TableColumn column = table.getColumnModel().getColumn(col); |
| int preferred = column.getPreferredWidth(); |
| int width = Math.max(preferred, columnMaxWidth(table, col)); |
| totalWidth += width; |
| column.setMinWidth(width); |
| column.setMaxWidth(width); |
| column.setWidth(width); |
| column.setPreferredWidth(width); |
| } |
| |
| totalWidth += columnMaxWidth(table, colsNum - 1); |
| |
| return totalWidth; |
| } |
| |
| private static int columnMaxWidth(@NotNull JTable table, int col) { |
| TableColumn column = table.getColumnModel().getColumn(col); |
| int width = 0; |
| for (int row = 0; row < table.getRowCount(); row++) { |
| Component component = table.prepareRenderer(column.getCellRenderer(), row, col); |
| |
| int rendererWidth = component.getPreferredSize().width; |
| width = Math.max(width, rendererWidth + table.getIntercellSpacing().width); |
| } |
| return width; |
| } |
| |
| private int myWidth; |
| |
| private void rebuildPopup(@NotNull final UsageViewImpl usageView, |
| @NotNull final List<Usage> usages, |
| @NotNull List<UsageNode> nodes, |
| @NotNull final JTable table, |
| @NotNull final JBPopup popup, |
| @NotNull final UsageViewPresentation presentation, |
| @NotNull final RelativePoint popupPosition, |
| boolean findUsagesInProgress, |
| @NotNull AtomicInteger outOfScopeUsages, |
| @NotNull SearchScope searchScope) { |
| ApplicationManager.getApplication().assertIsDispatchThread(); |
| |
| boolean shouldShowMoreSeparator = usages.contains(MORE_USAGES_SEPARATOR); |
| if (shouldShowMoreSeparator) { |
| nodes.add(MORE_USAGES_SEPARATOR_NODE); |
| } |
| boolean hasOutsideScopeUsages = usages.contains(USAGES_OUTSIDE_SCOPE_SEPARATOR); |
| if (hasOutsideScopeUsages && !shouldShowMoreSeparator) { |
| nodes.add(USAGES_OUTSIDE_SCOPE_NODE); |
| } |
| |
| String title = presentation.getTabText(); |
| String fullTitle = getFullTitle(usages, title, shouldShowMoreSeparator || hasOutsideScopeUsages, nodes.size() - (shouldShowMoreSeparator || hasOutsideScopeUsages ? 1 : 0), findUsagesInProgress); |
| |
| ((AbstractPopup)popup).setCaption(fullTitle); |
| |
| List<UsageNode> data = collectData(usages, nodes, usageView, presentation); |
| MyModel tableModel = setTableModel(table, usageView, data, outOfScopeUsages, searchScope); |
| List<UsageNode> existingData = tableModel.getItems(); |
| |
| int row = table.getSelectedRow(); |
| |
| int newSelection = updateModel(tableModel, existingData, data, row == -1 ? 0 : row); |
| if (newSelection < 0 || newSelection >= tableModel.getRowCount()) { |
| TableScrollingUtil.ensureSelectionExists(table); |
| newSelection = table.getSelectedRow(); |
| } |
| else { |
| table.getSelectionModel().setSelectionInterval(newSelection, newSelection); |
| } |
| TableScrollingUtil.ensureIndexIsVisible(table, newSelection, 0); |
| |
| setSizeAndDimensions(table, popup, popupPosition, data); |
| } |
| |
| // returns new selection |
| private static int updateModel(@NotNull MyModel tableModel, @NotNull List<UsageNode> listOld, @NotNull List<UsageNode> listNew, int oldSelection) { |
| UsageNode[] oa = listOld.toArray(new UsageNode[listOld.size()]); |
| UsageNode[] na = listNew.toArray(new UsageNode[listNew.size()]); |
| List<ModelDiff.Cmd> cmds = ModelDiff.createDiffCmds(tableModel, oa, na); |
| int selection = oldSelection; |
| if (cmds != null) { |
| for (ModelDiff.Cmd cmd : cmds) { |
| selection = cmd.translateSelection(selection); |
| cmd.apply(); |
| } |
| } |
| return selection; |
| } |
| |
| private void setSizeAndDimensions(@NotNull JTable table, |
| @NotNull JBPopup popup, |
| @NotNull RelativePoint popupPosition, |
| @NotNull List<UsageNode> data) { |
| JComponent content = popup.getContent(); |
| Window window = SwingUtilities.windowForComponent(content); |
| Dimension d = window.getSize(); |
| |
| int width = calcMaxWidth(table); |
| width = (int)Math.max(d.getWidth(), width); |
| Dimension headerSize = ((AbstractPopup)popup).getHeaderPreferredSize(); |
| width = Math.max((int)headerSize.getWidth(), width); |
| width = Math.max(myWidth, width); |
| |
| if (myWidth == -1) myWidth = width; |
| int newWidth = Math.max(width, d.width + width - myWidth); |
| |
| myWidth = newWidth; |
| |
| int rowsToShow = Math.min(30, data.size()); |
| Dimension dimension = new Dimension(newWidth, table.getRowHeight() * rowsToShow); |
| Rectangle rectangle = fitToScreen(dimension, popupPosition, table); |
| dimension = rectangle.getSize(); |
| Point location = window.getLocation(); |
| if (!location.equals(rectangle.getLocation())) { |
| window.setLocation(rectangle.getLocation()); |
| } |
| |
| if (!data.isEmpty()) { |
| TableScrollingUtil.ensureSelectionExists(table); |
| } |
| table.setSize(dimension); |
| //table.setPreferredSize(dimension); |
| //table.setMaximumSize(dimension); |
| //table.setPreferredScrollableViewportSize(dimension); |
| |
| |
| Dimension footerSize = ((AbstractPopup)popup).getFooterPreferredSize(); |
| |
| int newHeight = (int)(dimension.height + headerSize.getHeight() + footerSize.getHeight()) + 4/* invisible borders, margins etc*/; |
| Dimension newDim = new Dimension(dimension.width, newHeight); |
| window.setSize(newDim); |
| window.setMinimumSize(newDim); |
| window.setMaximumSize(newDim); |
| |
| window.validate(); |
| window.repaint(); |
| table.revalidate(); |
| table.repaint(); |
| } |
| |
| private static Rectangle fitToScreen(@NotNull Dimension newDim, @NotNull RelativePoint popupPosition, JTable table) { |
| Rectangle rectangle = new Rectangle(popupPosition.getScreenPoint(), newDim); |
| ScreenUtil.fitToScreen(rectangle); |
| if (rectangle.getHeight() != newDim.getHeight()) { |
| int newHeight = (int)rectangle.getHeight(); |
| int roundedHeight = newHeight - newHeight % table.getRowHeight(); |
| rectangle.setSize((int)rectangle.getWidth(), Math.max(roundedHeight, table.getRowHeight())); |
| } |
| return rectangle; |
| |
| } |
| |
| private void appendMoreUsages(Editor editor, |
| @NotNull RelativePoint popupPosition, |
| @NotNull FindUsagesHandler handler, |
| int maxUsages, |
| @NotNull FindUsagesOptions options) { |
| showElementUsages(editor, popupPosition, handler, maxUsages+USAGES_PAGE_SIZE, options); |
| } |
| |
| private static void addUsageNodes(@NotNull GroupNode root, @NotNull final UsageViewImpl usageView, @NotNull List<UsageNode> outNodes) { |
| for (UsageNode node : root.getUsageNodes()) { |
| Usage usage = node.getUsage(); |
| if (usageView.isVisible(usage)) { |
| node.setParent(root); |
| outNodes.add(node); |
| } |
| } |
| for (GroupNode groupNode : root.getSubGroups()) { |
| groupNode.setParent(root); |
| addUsageNodes(groupNode, usageView, outNodes); |
| } |
| } |
| |
| @Override |
| public void update(AnActionEvent e){ |
| FindUsagesInFileAction.updateFindUsagesAction(e); |
| } |
| |
| private void navigateAndHint(@NotNull Usage usage, |
| @Nullable final String hint, |
| @NotNull final FindUsagesHandler handler, |
| @NotNull final RelativePoint popupPosition, |
| final int maxUsages, |
| @NotNull final FindUsagesOptions options) { |
| usage.navigate(true); |
| if (hint == null) return; |
| final Editor newEditor = getEditorFor(usage); |
| if (newEditor == null) return; |
| hint(newEditor, hint, handler, popupPosition, maxUsages, options, false); |
| } |
| |
| private void showHint(@Nullable final Editor editor, |
| @NotNull String hint, |
| @NotNull FindUsagesHandler handler, |
| @NotNull final RelativePoint popupPosition, |
| int maxUsages, |
| @NotNull FindUsagesOptions options, |
| boolean isWarning) { |
| JComponent label = createHintComponent(hint, handler, popupPosition, editor, HIDE_HINTS_ACTION, maxUsages, options, isWarning); |
| if (editor == null || editor.isDisposed() || !editor.getComponent().isShowing()) { |
| HintManager.getInstance().showHint(label, popupPosition, HintManager.HIDE_BY_ANY_KEY | |
| HintManager.HIDE_BY_TEXT_CHANGE | HintManager.HIDE_BY_SCROLLING, 0); |
| } |
| else { |
| HintManager.getInstance().showInformationHint(editor, label); |
| } |
| } |
| |
| private void hint(@Nullable final Editor editor, |
| @NotNull final String hint, |
| @NotNull final FindUsagesHandler handler, |
| @NotNull final RelativePoint popupPosition, |
| final int maxUsages, |
| @NotNull final FindUsagesOptions options, |
| final boolean isWarning) { |
| final Project project = handler.getProject(); |
| //opening editor is performing in invokeLater |
| IdeFocusManager.getInstance(project).doWhenFocusSettlesDown(new Runnable() { |
| @Override |
| public void run() { |
| Runnable runnable = new Runnable() { |
| @Override |
| public void run() { |
| // after new editor created, some editor resizing events are still bubbling. To prevent hiding hint, invokeLater this |
| IdeFocusManager.getInstance(project).doWhenFocusSettlesDown(new Runnable() { |
| @Override |
| public void run() { |
| showHint(editor, hint, handler, popupPosition, maxUsages, options, isWarning); |
| } |
| }); |
| } |
| }; |
| if (editor == null) runnable.run(); else editor.getScrollingModel().runActionOnScrollingFinished(runnable); |
| } |
| }); |
| } |
| |
| @Nullable |
| private static Editor getEditorFor(@NotNull Usage usage) { |
| FileEditorLocation location = usage.getLocation(); |
| FileEditor newFileEditor = location == null ? null : location.getEditor(); |
| return newFileEditor instanceof TextEditor ? ((TextEditor)newFileEditor).getEditor() : null; |
| } |
| |
| private static class MyTable extends JBTableWithHintProvider implements DataProvider { |
| @Override |
| public boolean getScrollableTracksViewportWidth() { |
| return true; |
| } |
| |
| @Override |
| public Object getData(@NonNls String dataId) { |
| if (CommonDataKeys.PSI_ELEMENT.is(dataId)) { |
| final int[] selected = getSelectedRows(); |
| if (selected.length == 1) { |
| return getPsiElementForHint(getValueAt(selected[0], 0)); |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| @Nullable |
| protected 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; |
| |
| public StringNode(@NotNull Object string) { |
| super(NullUsage.INSTANCE, new UsageViewTreeModelBuilder(new UsageViewPresentation(), UsageTarget.EMPTY_ARRAY)); |
| myString = string; |
| } |
| |
| @Override |
| public String toString() { |
| return myString.toString(); |
| } |
| } |
| |
| private static class MySpeedSearch extends SpeedSearchBase<JTable> { |
| public 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(); |
| return usage.getPresentation().getPlainText() + group; |
| } |
| |
| @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; |
| } |
| } |
| } |