blob: bfd00da9107df03d49544f6b61efa4110ece3b53 [file] [log] [blame]
/*
* 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.ide.actions;
import com.intellij.codeInsight.navigation.NavigationUtil;
import com.intellij.execution.Executor;
import com.intellij.execution.ExecutorRegistry;
import com.intellij.execution.RunnerAndConfigurationSettings;
import com.intellij.execution.actions.ChooseRunConfigurationPopup;
import com.intellij.execution.actions.ExecutorProvider;
import com.intellij.execution.executors.DefaultRunExecutor;
import com.intellij.execution.impl.RunDialog;
import com.intellij.featureStatistics.FeatureUsageTracker;
import com.intellij.icons.AllIcons;
import com.intellij.ide.DataManager;
import com.intellij.ide.IdeEventQueue;
import com.intellij.ide.IdeTooltipManager;
import com.intellij.ide.SearchTopHitProvider;
import com.intellij.ide.ui.UISettings;
import com.intellij.ide.ui.laf.darcula.ui.DarculaTextBorder;
import com.intellij.ide.ui.laf.darcula.ui.DarculaTextFieldUI;
import com.intellij.ide.ui.search.BooleanOptionDescription;
import com.intellij.ide.ui.search.OptionDescription;
import com.intellij.ide.util.DefaultPsiElementCellRenderer;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.ide.util.gotoByName.*;
import com.intellij.lang.Language;
import com.intellij.lang.LanguagePsiElementExternalizer;
import com.intellij.navigation.ItemPresentation;
import com.intellij.navigation.NavigationItem;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.actionSystem.ex.ActionUtil;
import com.intellij.openapi.actionSystem.ex.AnActionListener;
import com.intellij.openapi.actionSystem.ex.CustomComponentAction;
import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl;
import com.intellij.openapi.application.AccessToken;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.actions.TextComponentEditorAction;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.fileEditor.impl.EditorHistoryManager;
import com.intellij.openapi.keymap.KeymapManager;
import com.intellij.openapi.keymap.KeymapUtil;
import com.intellij.openapi.keymap.MacKeymapUtil;
import com.intellij.openapi.keymap.impl.ModifierKeyDoubleClickHandler;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.SearchableConfigurable;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.util.ProgressIndicatorBase;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.DumbService;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.popup.ComponentPopupBuilder;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.VirtualFilePathWrapper;
import com.intellij.openapi.wm.*;
import com.intellij.openapi.wm.impl.IdeFrameImpl;
import com.intellij.pom.Navigatable;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.codeStyle.MinusculeMatcher;
import com.intellij.psi.codeStyle.NameUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.ui.*;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.border.CustomLineBorder;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBList;
import com.intellij.ui.components.JBScrollPane;
import com.intellij.ui.components.OnOffButton;
import com.intellij.ui.components.panels.NonOpaquePanel;
import com.intellij.ui.popup.AbstractPopup;
import com.intellij.ui.popup.PopupPositionManager;
import com.intellij.util.*;
import com.intellij.util.text.Matcher;
import com.intellij.util.ui.EmptyIcon;
import com.intellij.util.ui.StatusText;
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.border.EmptyBorder;
import javax.swing.event.DocumentEvent;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* @author Konstantin Bulenkov
*/
@SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized")
public class SearchEverywhereAction extends AnAction implements CustomComponentAction, DumbAware, DataProvider, RightAlignedToolbarAction {
public static final String SE_HISTORY_KEY = "SearchEverywhereHistoryKey";
public static final int SEARCH_FIELD_COLUMNS = 25;
private static final int MAX_CLASSES = 6;
private static final int MAX_FILES = 6;
private static final int MAX_RUN_CONFIGURATION = 6;
private static final int MAX_TOOL_WINDOWS = 4;
private static final int MAX_SYMBOLS = 6;
private static final int MAX_SETTINGS = 5;
private static final int MAX_ACTIONS = 5;
private static final int MAX_RECENT_FILES = 10;
private static final int DEFAULT_MORE_STEP_COUNT = 15;
public static final int MAX_SEARCH_EVERYWHERE_HISTORY = 50;
private static final int POPUP_MAX_WIDTH = 600;
private static final Logger LOG = Logger.getInstance("#" + SearchEverywhereAction.class.getName());
private SearchEverywhereAction.MyListRenderer myRenderer;
MySearchTextField myPopupField;
private volatile GotoClassModel2 myClassModel;
private volatile GotoFileModel myFileModel;
private volatile GotoActionItemProvider myActionProvider;
private volatile GotoSymbolModel2 mySymbolsModel;
private Component myFocusComponent;
private JBPopup myPopup;
private Map<String, String> myConfigurables = new HashMap<String, String>();
private Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, ApplicationManager.getApplication());
private Alarm myUpdateAlarm = new Alarm(ApplicationManager.getApplication());
private JBList myList;
private JCheckBox myNonProjectCheckBox;
private AnActionEvent myActionEvent;
private Component myContextComponent;
private CalcThread myCalcThread;
private static AtomicBoolean ourShiftIsPressed = new AtomicBoolean(false);
private static AtomicBoolean showAll = new AtomicBoolean(false);
private volatile ActionCallback myCurrentWorker = ActionCallback.DONE;
private int myHistoryIndex = 0;
boolean mySkipFocusGain = false;
static {
ModifierKeyDoubleClickHandler.getInstance().registerAction(IdeActions.ACTION_SEARCH_EVERYWHERE, KeyEvent.VK_SHIFT, -1);
IdeEventQueue.getInstance().addPostprocessor(new IdeEventQueue.EventDispatcher() {
@Override
public boolean dispatch(AWTEvent event) {
if (event instanceof KeyEvent) {
final int keyCode = ((KeyEvent)event).getKeyCode();
if (keyCode == KeyEvent.VK_SHIFT) {
ourShiftIsPressed.set(event.getID() == KeyEvent.KEY_PRESSED);
}
}
return false;
}
}, null);
}
private volatile JBPopup myBalloon;
private int myPopupActualWidth;
private Component myFocusOwner;
private ChooseByNamePopup myFileChooseByName;
private ChooseByNamePopup myClassChooseByName;
private ChooseByNamePopup mySymbolsChooseByName;
private Editor myEditor;
private PsiFile myFile;
private HistoryItem myHistoryItem;
@Override
public JComponent createCustomComponent(Presentation presentation) {
JPanel panel = new JPanel(new BorderLayout()) {
@Override
protected void paintComponent(Graphics g) {
if (myBalloon != null && !myBalloon.isDisposed() && myActionEvent != null && myActionEvent.getInputEvent() instanceof MouseEvent) {
final Gradient gradient = getGradientColors();
((Graphics2D)g).setPaint(new GradientPaint(0, 0, gradient.getStartColor(), 0, getHeight(), gradient.getEndColor()));
g.fillRect(0,0,getWidth(), getHeight());
} else {
super.paintComponent(g);
}
}
};
panel.setOpaque(false);
final JLabel label = new JBLabel(AllIcons.Actions.FindPlain) {
{
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK);
}
};
panel.add(label, BorderLayout.CENTER);
initTooltip(label);
label.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
if (myBalloon != null) {
myBalloon.cancel();
}
myFocusOwner = IdeFocusManager.findInstance().getFocusOwner();
label.setToolTipText(null);
IdeTooltipManager.getInstance().hideCurrentNow(false);
label.setIcon(AllIcons.Actions.FindWhite);
actionPerformed(null, e);
}
@Override
public void mouseEntered(MouseEvent e) {
if (myBalloon == null || myBalloon.isDisposed()) {
label.setIcon(AllIcons.Actions.Find);
}
}
@Override
public void mouseExited(MouseEvent e) {
if (myBalloon == null || myBalloon.isDisposed()) {
label.setIcon(AllIcons.Actions.FindPlain);
}
}
});
return panel;
}
private static Gradient getGradientColors() {
return new Gradient(
new JBColor(new Color(101, 147, 242), new Color(64, 80, 94)),
new JBColor(new Color(46, 111, 205), new Color(53, 65, 87)));
}
public SearchEverywhereAction() {
updateComponents();
//noinspection SSBasedInspection
SwingUtilities.invokeLater(new Runnable() {
public void run() {
onFocusLost();
}
});
}
private void updateComponents() {
myRenderer = new MyListRenderer();
myList = new JBList() {
@Override
public Dimension getPreferredSize() {
final Dimension size = super.getPreferredSize();
return new Dimension(Math.min(size.width - 2, POPUP_MAX_WIDTH), size.height);
}
};
myList.setCellRenderer(myRenderer);
myList.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
e.consume();
final int i = myList.locationToIndex(e.getPoint());
if (i != -1) {
mySkipFocusGain = true;
getField().requestFocus();
//noinspection SSBasedInspection
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
myList.setSelectedIndex(i);
doNavigate(i);
}
});
}
}
});
myNonProjectCheckBox = new JCheckBox();
myNonProjectCheckBox.setOpaque(false);
myNonProjectCheckBox.setAlignmentX(1.0f);
myNonProjectCheckBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (showAll.get() != myNonProjectCheckBox.isSelected()) {
showAll.set(!showAll.get());
final JTextField editor = UIUtil.findComponentOfType(myBalloon.getContent(), JTextField.class);
if (editor != null) {
final String pattern = editor.getText();
myAlarm.cancelAllRequests();
myAlarm.addRequest(new Runnable() {
@Override
public void run() {
if (editor.hasFocus()) {
rebuildList(pattern);
}
}
}, 30);
}
}
}
});
}
private static void initTooltip(JLabel label) {
final String shortcutText;
shortcutText = getShortcut();
label.setToolTipText("<html><body>Search Everywhere<br/>Press <b>"
+ shortcutText
+ "</b> to access<br/> - Classes<br/> - Files<br/> - Tool Windows<br/> - Actions<br/> - Settings</body></html>");
}
@Nullable
@Override
public Object getData(@NonNls String dataId) {
return null;
}
private static String getShortcut() {
String shortcutText;
final Shortcut[] shortcuts = KeymapManager.getInstance().getActiveKeymap().getShortcuts(IdeActions.ACTION_SEARCH_EVERYWHERE);
if (shortcuts.length == 0) {
shortcutText = "Double " + (SystemInfo.isMac ? MacKeymapUtil.SHIFT : "Shift");
} else {
shortcutText = KeymapUtil.getShortcutsText(shortcuts);
}
return shortcutText;
}
private void initSearchField(final MySearchTextField search) {
final JTextField editor = search.getTextEditor();
// onFocusLost();
editor.getDocument().addDocumentListener(new DocumentAdapter() {
@Override
protected void textChanged(DocumentEvent e) {
final String pattern = editor.getText();
if (editor.hasFocus()) {
rebuildList(pattern);
}
}
});
editor.addFocusListener(new FocusAdapter() {
@Override
public void focusGained(FocusEvent e) {
if (mySkipFocusGain) {
mySkipFocusGain = false;
return;
}
search.setText("");
search.getTextEditor().setForeground(UIUtil.getLabelForeground());
//titleIndex = new TitleIndexes();
editor.setColumns(SEARCH_FIELD_COLUMNS);
myFocusComponent = e.getOppositeComponent();
//noinspection SSBasedInspection
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
final JComponent parent = (JComponent)editor.getParent();
parent.revalidate();
parent.repaint();
}
});
//if (myPopup != null && myPopup.isVisible()) {
// myPopup.cancel();
// myPopup = null;
//}
rebuildList("");
}
@Override
public void focusLost(FocusEvent e) {
if ( myPopup instanceof AbstractPopup && myPopup.isVisible()
&& ((myList == e.getOppositeComponent()) || ((AbstractPopup)myPopup).getPopupWindow() == e.getOppositeComponent())) {
return;
}
if (myNonProjectCheckBox == e.getOppositeComponent()) {
mySkipFocusGain = true;
editor.requestFocus();
return;
}
onFocusLost();
}
});
}
private void jumpNextGroup(boolean forward) {
final int index = myList.getSelectedIndex();
final SearchListModel model = getModel();
if (index >= 0) {
final int newIndex = forward ? model.next(index) : model.prev(index);
myList.setSelectedIndex(newIndex);
int more = model.next(newIndex) - 1;
if (more < newIndex) {
more = myList.getItemsCount() - 1;
}
ListScrollingUtil.ensureIndexIsVisible(myList, more, forward ? 1 : -1);
ListScrollingUtil.ensureIndexIsVisible(myList, newIndex, forward ? 1 : -1);
}
}
private SearchListModel getModel() {
return (SearchListModel)myList.getModel();
}
private ActionCallback onFocusLost() {
final ActionCallback result = new ActionCallback();
//noinspection SSBasedInspection
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
if (myCalcThread != null) {
myCalcThread.cancel();
//myCalcThread = null;
}
myAlarm.cancelAllRequests();
if (myBalloon != null && !myBalloon.isDisposed() && myPopup != null && !myPopup.isDisposed()) {
myBalloon.cancel();
myPopup.cancel();
}
//noinspection SSBasedInspection
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
ActionToolbarImpl.updateAllToolbarsImmediately();
}
});
} finally {
result.setDone();
}
}
});
return result;
}
private SearchTextField getField() {
return myPopupField;
}
private void doNavigate(final int index) {
final Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(getField().getTextEditor()));
final Executor executor = ourShiftIsPressed.get()
? DefaultRunExecutor.getRunExecutorInstance()
: ExecutorRegistry.getInstance().getExecutorById(ToolWindowId.DEBUG);
assert project != null;
final SearchListModel model = getModel();
if (isMoreItem(index)) {
final String pattern = myPopupField.getText();
WidgetID wid = null;
if (index == model.moreIndex.classes) wid = WidgetID.CLASSES;
else if (index == model.moreIndex.files) wid = WidgetID.FILES;
else if (index == model.moreIndex.settings) wid = WidgetID.SETTINGS;
else if (index == model.moreIndex.actions) wid = WidgetID.ACTIONS;
else if (index == model.moreIndex.symbols) wid = WidgetID.SYMBOLS;
else if (index == model.moreIndex.runConfigurations) wid = WidgetID.RUN_CONFIGURATIONS;
if (wid != null) {
final WidgetID widgetID = wid;
myCurrentWorker.doWhenProcessed(new Runnable() {
@Override
public void run() {
myCalcThread = new CalcThread(project, pattern, true);
myPopupActualWidth = 0;
myCurrentWorker = myCalcThread.insert(index, widgetID);
}
});
return;
}
}
final String pattern = getField().getText();
final Object value = myList.getSelectedValue();
saveHistory(project, pattern, value);
IdeFocusManager focusManager = IdeFocusManager.findInstanceByComponent(getField().getTextEditor());
if (myPopup != null && myPopup.isVisible()) {
myPopup.cancel();
}
if (value instanceof BooleanOptionDescription) {
final BooleanOptionDescription option = (BooleanOptionDescription)value;
option.setOptionState(!option.isOptionEnabled());
myList.revalidate();
myList.repaint();
return;
}
Runnable onDone = null;
AccessToken token = ApplicationManager.getApplication().acquireReadActionLock();
try {
if (value instanceof PsiElement) {
onDone = new Runnable() {
public void run() {
NavigationUtil.activateFileWithPsiElement((PsiElement)value, true);
}
};
return;
}
else if (isVirtualFile(value)) {
onDone = new Runnable() {
public void run() {
OpenSourceUtil.navigate(true, new OpenFileDescriptor(project, (VirtualFile)value));
}
};
return;
}
else if (isActionValue(value) || isSetting(value) || isRunConfiguration(value)) {
focusManager.requestDefaultFocus(true);
final Component comp = myContextComponent;
final AnActionEvent event = myActionEvent;
IdeFocusManager.getInstance(project).doWhenFocusSettlesDown(new Runnable() {
@Override
public void run() {
Component c = comp;
if (c == null) {
c = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
}
if (isRunConfiguration(value)) {
((ChooseRunConfigurationPopup.ItemWrapper)value).perform(project, executor, DataManager.getInstance().getDataContext(c));
} else {
GotoActionAction.openOptionOrPerformAction(value, pattern, project, c, event);
if (isToolWindowAction(value)) return;
}
}
});
return;
}
else if (value instanceof Navigatable) {
onDone = new Runnable() {
@Override
public void run() {
OpenSourceUtil.navigate(true, (Navigatable)value);
}
};
return;
}
}
finally {
token.finish();
final ActionCallback callback = onFocusLost();
if (onDone != null) {
callback.doWhenDone(onDone);
}
}
focusManager.requestDefaultFocus(true);
}
private boolean isMoreItem(int index) {
final SearchListModel model = getModel();
return index == model.moreIndex.classes ||
index == model.moreIndex.files ||
index == model.moreIndex.settings ||
index == model.moreIndex.actions ||
index == model.moreIndex.symbols ||
index == model.moreIndex.runConfigurations;
}
private void rebuildList(final String pattern) {
assert EventQueue.isDispatchThread() : "Must be EDT";
if (myCalcThread != null && !myCurrentWorker.isProcessed()) {
myCurrentWorker = myCalcThread.cancel();
}
if (myCalcThread != null && !myCalcThread.isCanceled()) {
myCalcThread.cancel();
}
final Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(getField().getTextEditor()));
assert project != null;
myRenderer.myProject = project;
myCurrentWorker.doWhenProcessed(new Runnable() {
@Override
public void run() {
myCalcThread = new CalcThread(project, pattern, false);
myPopupActualWidth = 0;
myCurrentWorker = myCalcThread.start();
}
});
}
@Override
public void actionPerformed(AnActionEvent e) {
actionPerformed(e, null);
}
public void actionPerformed(AnActionEvent e, MouseEvent me) {
if (myBalloon != null && myBalloon.isVisible()) {
showAll.set(!showAll.get());
myNonProjectCheckBox.setSelected(showAll.get());
// myPopupField.getTextEditor().setBackground(showAll.get() ? new JBColor(new Color(0xffffe4), new Color(0x494539)) : UIUtil.getTextFieldBackground());
rebuildList(myPopupField.getText());
return;
}
myCurrentWorker = ActionCallback.DONE;
if (e != null) {
myEditor = e.getData(CommonDataKeys.EDITOR);
myFile = e.getData(CommonDataKeys.PSI_FILE);
}
if (e == null && myFocusOwner != null) {
e = new AnActionEvent(me, DataManager.getInstance().getDataContext(myFocusOwner), ActionPlaces.UNKNOWN, getTemplatePresentation(), ActionManager.getInstance(), 0);
}
if (e == null) return;
updateComponents();
myContextComponent = PlatformDataKeys.CONTEXT_COMPONENT.getData(e.getDataContext());
Window wnd = myContextComponent != null ? SwingUtilities.windowForComponent(myContextComponent)
: KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow();
if (wnd == null && myContextComponent instanceof Window) {
wnd = (Window)myContextComponent;
}
if (wnd == null || wnd.getParent() != null) return;
myActionEvent = e;
if (myPopupField != null) {
Disposer.dispose(myPopupField);
}
myPopupField = new MySearchTextField();
myPopupField.getTextEditor().addKeyListener(new KeyAdapter() {
@Override
public void keyTyped(KeyEvent e) {
myHistoryIndex = 0;
myHistoryItem = null;
}
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SHIFT) {
myList.repaint();
}
}
@Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SHIFT) {
myList.repaint();
}
}
});
initSearchField(myPopupField);
myPopupField.setOpaque(false);
final JTextField editor = myPopupField.getTextEditor();
editor.setColumns(SEARCH_FIELD_COLUMNS);
final JPanel panel = new JPanel(new BorderLayout()) {
@Override
protected void paintComponent(Graphics g) {
final Gradient gradient = getGradientColors();
((Graphics2D)g).setPaint(new GradientPaint(0, 0, gradient.getStartColor(), 0, getHeight(), gradient.getEndColor()));
g.fillRect(0, 0, getWidth(), getHeight());
}
@Override
public Dimension getPreferredSize() {
return new Dimension(410, super.getPreferredSize().height);
}
};
final JLabel title = new JLabel(" Search Everywhere: ");
final JPanel topPanel = new NonOpaquePanel(new BorderLayout());
title.setForeground(new JBColor(Gray._240, Gray._200));
if (SystemInfo.isMac) {
title.setFont(title.getFont().deriveFont(Font.BOLD, title.getFont().getSize() - 1f));
} else {
title.setFont(title.getFont().deriveFont(Font.BOLD));
}
topPanel.add(title, BorderLayout.WEST);
myNonProjectCheckBox.setForeground(new JBColor(Gray._240, Gray._200));
myNonProjectCheckBox.setText("Include non-project items (" + getShortcut() + ")");
if (!NonProjectScopeDisablerEP.isSearchInNonProjectDisabled()) {
topPanel.add(myNonProjectCheckBox, BorderLayout.EAST);
}
panel.add(myPopupField, BorderLayout.CENTER);
panel.add(topPanel, BorderLayout.NORTH);
panel.setBorder(IdeBorderFactory.createEmptyBorder(3, 5, 4, 5));
DataManager.registerDataProvider(panel, this);
final ComponentPopupBuilder builder = JBPopupFactory.getInstance().createComponentPopupBuilder(panel, editor);
myBalloon = builder
.setCancelOnClickOutside(true)
.setModalContext(false)
.setRequestFocus(true)
.createPopup();
myBalloon.getContent().setBorder(new EmptyBorder(0,0,0,0));
final Window window = WindowManager.getInstance().suggestParentWindow(e.getProject());
//noinspection ConstantConditions
e.getProject().getMessageBus().connect(myBalloon).subscribe(DumbService.DUMB_MODE, new DumbService.DumbModeListener() {
@Override
public void enteredDumbMode() {
}
@Override
public void exitDumbMode() {
rebuildList(myPopupField.getText());
}
});
Component parent = UIUtil.findUltimateParent(window);
registerDataProvider(panel, e.getProject());
final RelativePoint showPoint;
if (me != null) {
final Component label = me.getComponent();
final Component button = label.getParent();
assert button != null;
showPoint = new RelativePoint(button, new Point(button.getWidth() - panel.getPreferredSize().width, button.getHeight()));
} else {
if (parent != null) {
int height = UISettings.getInstance().SHOW_MAIN_TOOLBAR ? 135 : 115;
if (parent instanceof IdeFrameImpl && ((IdeFrameImpl)parent).isInFullScreen()) {
height -= 20;
}
showPoint = new RelativePoint(parent, new Point((parent.getSize().width - panel.getPreferredSize().width)/ 2, height));
} else {
showPoint = JBPopupFactory.getInstance().guessBestPopupLocation(e.getDataContext());
}
}
myList.setFont(UIUtil.getListFont());
myBalloon.show(showPoint);
initSearchActions(myBalloon, myPopupField);
IdeFocusManager focusManager = IdeFocusManager.getInstance(e.getProject());
focusManager.requestFocus(editor, true);
FeatureUsageTracker.getInstance().triggerFeatureUsed(IdeActions.ACTION_SEARCH_EVERYWHERE);
}
private static void saveHistory(Project project, String text, Object value) {
if (project == null || project.isDisposed() || !project.isInitialized()) {
return;
}
HistoryType type = null;
String fqn = null;
if (isActionValue(value)) {
type = HistoryType.ACTION;
AnAction action = (AnAction)(value instanceof GotoActionModel.ActionWrapper ? ((GotoActionModel.ActionWrapper)value).getAction() : value);
fqn = ActionManager.getInstance().getId(action);
} else if (value instanceof VirtualFile) {
type = HistoryType.FILE;
fqn = ((VirtualFile)value).getUrl();
} else if (value instanceof ChooseRunConfigurationPopup.ItemWrapper) {
type = HistoryType.RUN_CONFIGURATION;
fqn = ((ChooseRunConfigurationPopup.ItemWrapper)value).getText();
} else if (value instanceof PsiElement) {
final PsiElement psiElement = (PsiElement)value;
final Language language = psiElement.getLanguage();
final String name = LanguagePsiElementExternalizer.INSTANCE.forLanguage(language).getQualifiedName(psiElement);
if (name != null) {
type = HistoryType.PSI;
fqn = language.getID() + "://" + name;
}
}
final PropertiesComponent storage = PropertiesComponent.getInstance(project);
final String[] values = storage.getValues(SE_HISTORY_KEY);
List<HistoryItem> history = new ArrayList<HistoryItem>();
if (values != null) {
for (String s : values) {
final String[] split = s.split("\t");
if (split.length != 3 || text.equals(split[0])) {
continue;
}
if (!StringUtil.isEmpty(split[0])) {
history.add(new HistoryItem(split[0], split[1], split[2]));
}
}
}
history.add(0, new HistoryItem(text, type == null ? null : type.name(), fqn));
if (history.size() > MAX_SEARCH_EVERYWHERE_HISTORY) {
history = history.subList(0, MAX_SEARCH_EVERYWHERE_HISTORY);
}
final String[] newValues = new String[history.size()];
for (int i = 0; i < newValues.length; i++) {
newValues[i] = history.get(i).toString();
}
storage.setValues(SE_HISTORY_KEY, newValues);
}
public Executor getExecutor() {
return ourShiftIsPressed.get() ? DefaultRunExecutor.getRunExecutorInstance()
: ExecutorRegistry.getInstance().getExecutorById(ToolWindowId.DEBUG);
}
private void registerDataProvider(JPanel panel, final Project project) {
DataManager.registerDataProvider(panel, new DataProvider() {
@Nullable
@Override
public Object getData(@NonNls String dataId) {
final Object value = myList.getSelectedValue();
if (CommonDataKeys.PSI_ELEMENT.is(dataId) && value instanceof PsiElement) {
return value;
} else if (CommonDataKeys.VIRTUAL_FILE.is(dataId) && value instanceof VirtualFile) {
return value;
} else if (CommonDataKeys.NAVIGATABLE.is(dataId)) {
if (value instanceof Navigatable) return value;
if (value instanceof ChooseRunConfigurationPopup.ItemWrapper) {
final Object config = ((ChooseRunConfigurationPopup.ItemWrapper)value).getValue();
if (config instanceof RunnerAndConfigurationSettings) {
return new Navigatable() {
@Override
public void navigate(boolean requestFocus) {
RunDialog.editConfiguration(project, (RunnerAndConfigurationSettings)config, "Edit Configuration", getExecutor());
}
@Override
public boolean canNavigate() {
return true;
}
@Override
public boolean canNavigateToSource() {
return true;
}
};
}
}
}
return null;
}
});
}
private void initSearchActions(JBPopup balloon, MySearchTextField searchTextField) {
final JTextField editor = searchTextField.getTextEditor();
new DumbAwareAction(){
@Override
public void actionPerformed(AnActionEvent e) {
jumpNextGroup(true);
}
}.registerCustomShortcutSet(CustomShortcutSet.fromString("TAB"), editor, balloon);
new DumbAwareAction(){
@Override
public void actionPerformed(AnActionEvent e) {
jumpNextGroup(false);
}
}.registerCustomShortcutSet(CustomShortcutSet.fromString("shift TAB"), editor, balloon);
new DumbAwareAction(){
@Override
public void actionPerformed(AnActionEvent e) {
if (myBalloon != null && myBalloon.isVisible()) {
myBalloon.cancel();
}
if (myPopup != null && myPopup.isVisible()) {
myPopup.cancel();
}
}
}.registerCustomShortcutSet(CustomShortcutSet.fromString("ESCAPE"), editor, balloon);
new DumbAwareAction(){
@Override
public void actionPerformed(AnActionEvent e) {
final int index = myList.getSelectedIndex();
if (index != -1) {
doNavigate(index);
}
}
}.registerCustomShortcutSet(CustomShortcutSet.fromString("ENTER", "shift ENTER"), editor, balloon);
new DumbAwareAction(){
@Override
public void actionPerformed(AnActionEvent e) {
final PropertiesComponent storage = PropertiesComponent.getInstance(e.getProject());
final String[] values = storage.getValues(SE_HISTORY_KEY);
if (values != null) {
if (values.length > myHistoryIndex) {
final List<String> data = StringUtil.split(values[myHistoryIndex], "\t");
myHistoryItem = new HistoryItem(data.get(0), data.get(1), data.get(2));
myHistoryIndex++;
editor.setText(myHistoryItem.pattern);
editor.setCaretPosition(myHistoryItem.pattern.length());
editor.moveCaretPosition(0);
}
}
}
@Override
public void update(AnActionEvent e) {
e.getPresentation().setEnabled(editor.getCaretPosition() == 0);
}
}.registerCustomShortcutSet(CustomShortcutSet.fromString("LEFT"), editor, balloon);
}
private static class MySearchTextField extends SearchTextField implements DataProvider, Disposable {
public MySearchTextField() {
super(false);
getTextEditor().setOpaque(false);
getTextEditor().setUI((DarculaTextFieldUI)DarculaTextFieldUI.createUI(getTextEditor()));
getTextEditor().setBorder(new DarculaTextBorder());
getTextEditor().putClientProperty("JTextField.Search.noBorderRing", Boolean.TRUE);
if (UIUtil.isUnderDarcula()) {
getTextEditor().setBackground(Gray._45);
getTextEditor().setForeground(Gray._240);
}
}
@Override
protected boolean isSearchControlUISupported() {
return true;
}
@Override
protected boolean hasIconsOutsideOfTextField() {
return false;
}
@Override
protected void showPopup() {
}
@Nullable
@Override
public Object getData(@NonNls String dataId) {
if (PlatformDataKeys.PREDEFINED_TEXT.is(dataId)) {
return getTextEditor().getText();
}
return null;
}
@Override
public void dispose() {
}
}
private class MyListRenderer extends ColoredListCellRenderer {
ColoredListCellRenderer myLocation = new ColoredListCellRenderer() {
@Override
protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
setPaintFocusBorder(false);
append(myLocationString, SimpleTextAttributes.GRAYED_ATTRIBUTES);
setIcon(myLocationIcon);
}
};
GotoFileCellRenderer myFileRenderer = new GotoFileCellRenderer(400);
private String myLocationString;
private DefaultPsiElementCellRenderer myPsiRenderer = new DefaultPsiElementCellRenderer() {
{setFocusBorderEnabled(false);}
};
private Icon myLocationIcon;
private Project myProject;
private JPanel myMainPanel = new JPanel(new BorderLayout());
private JLabel myTitle = new JLabel();
@Override
public void clear() {
super.clear();
myLocation.clear();
myLocationString = null;
myLocationIcon = null;
}
public void setLocationString(String locationString) {
myLocationString = locationString;
}
@Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
Component cmp;
PsiFile file = null;
myLocationString = null;
String pattern = "*" + myPopupField.getText();
Matcher matcher = NameUtil.buildMatcher(pattern, 0, true, true, pattern.toLowerCase().equals(pattern));
if (isMoreItem(index)) {
cmp = More.get(isSelected);
} else if (value instanceof VirtualFile
&& myProject != null
&& (((VirtualFile)value).isDirectory()
|| (file = PsiManager.getInstance(myProject).findFile((VirtualFile)value)) != null)) {
myFileRenderer.setPatternMatcher(matcher);
cmp = myFileRenderer.getListCellRendererComponent(list, file == null ? value : file, index, isSelected, cellHasFocus);
} else if (value instanceof PsiElement) {
myPsiRenderer.setPatternMatcher(matcher);
cmp = myPsiRenderer.getListCellRendererComponent(list, value, index, isSelected, isSelected);
} else {
cmp = super.getListCellRendererComponent(list, value, index, isSelected, isSelected);
final JPanel p = new JPanel(new BorderLayout());
p.setBackground(UIUtil.getListBackground(isSelected));
p.add(cmp, BorderLayout.CENTER);
cmp = p;
}
if (myLocationString != null || value instanceof BooleanOptionDescription) {
final JPanel panel = new JPanel(new BorderLayout());
panel.setBackground(UIUtil.getListBackground(isSelected));
panel.add(cmp, BorderLayout.CENTER);
final Component rightComponent;
if (value instanceof BooleanOptionDescription) {
final OnOffButton button = new OnOffButton();
button.setSelected(((BooleanOptionDescription)value).isOptionEnabled());
rightComponent = button;
}
else {
rightComponent = myLocation.getListCellRendererComponent(list, value, index, isSelected, isSelected);
}
panel.add(rightComponent, BorderLayout.EAST);
cmp = panel;
}
Color bg = cmp.getBackground();
if (bg == null) {
cmp.setBackground(UIUtil.getListBackground(isSelected));
bg = cmp.getBackground();
}
myMainPanel.setBorder(new CustomLineBorder(bg, 0, 0, 2, 0));
String title = getModel().titleIndex.getTitle(index);
myMainPanel.removeAll();
if (title != null) {
myTitle.setText(title);
myMainPanel.add(createTitle(" " + title), BorderLayout.NORTH);
}
myMainPanel.add(cmp, BorderLayout.CENTER);
final int width = myMainPanel.getPreferredSize().width;
if (width > myPopupActualWidth) {
myPopupActualWidth = width;
//schedulePopupUpdate();
}
return myMainPanel;
}
@Override
protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
setPaintFocusBorder(false);
setIcon(EmptyIcon.ICON_16);
AccessToken token = ApplicationManager.getApplication().acquireReadActionLock();
try {
if (value instanceof PsiElement) {
String name = myClassModel.getElementName(value);
assert name != null;
append(name);
} else if (value instanceof ChooseRunConfigurationPopup.ItemWrapper) {
final ChooseRunConfigurationPopup.ItemWrapper wrapper = (ChooseRunConfigurationPopup.ItemWrapper)value;
append(wrapper.getText());
setIcon(wrapper.getIcon());
setLocationString(ourShiftIsPressed.get() ? "Run" : "Debug");
myLocationIcon = ourShiftIsPressed.get() ? AllIcons.Toolwindows.ToolWindowRun : AllIcons.Toolwindows.ToolWindowDebugger;
} else if (isVirtualFile(value)) {
final VirtualFile file = (VirtualFile)value;
if (file instanceof VirtualFilePathWrapper) {
append(((VirtualFilePathWrapper)file).getPresentablePath());
} else {
append(file.getName());
}
setIcon(IconUtil.getIcon(file, Iconable.ICON_FLAG_READ_STATUS, myProject));
}
else if (isActionValue(value)) {
final GotoActionModel.ActionWrapper actionWithParentGroup = value instanceof GotoActionModel.ActionWrapper ? (GotoActionModel.ActionWrapper)value : null;
final AnAction anAction = actionWithParentGroup == null ? (AnAction)value : actionWithParentGroup.getAction();
final Presentation templatePresentation = anAction.getTemplatePresentation();
Icon icon = templatePresentation.getIcon();
if (anAction instanceof ActivateToolWindowAction) {
final String id = ((ActivateToolWindowAction)anAction).getToolWindowId();
ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(id);
if (toolWindow != null) {
icon = toolWindow.getIcon();
}
}
append(templatePresentation.getText());
if (actionWithParentGroup != null) {
final String groupName = actionWithParentGroup.getGroupName();
if (!StringUtil.isEmpty(groupName)) {
setLocationString(groupName);
}
}
final String groupName = actionWithParentGroup == null ? null : actionWithParentGroup.getGroupName();
if (!StringUtil.isEmpty(groupName)) {
setLocationString(groupName);
}
if (icon != null && icon.getIconWidth() <= 16 && icon.getIconHeight() <= 16) {
setIcon(IconUtil.toSize(icon, 16, 16));
}
}
else if (isSetting(value)) {
String text = getSettingText((OptionDescription)value);
append(text);
final String id = ((OptionDescription)value).getConfigurableId();
final String name = myConfigurables.get(id);
if (name != null) {
setLocationString(name);
}
}
else {
ItemPresentation presentation = null;
if (value instanceof ItemPresentation) {
presentation = (ItemPresentation)value;
}
else if (value instanceof NavigationItem) {
presentation = ((NavigationItem)value).getPresentation();
}
if (presentation != null) {
final String text = presentation.getPresentableText();
append(text == null ? value.toString() : text);
final String location = presentation.getLocationString();
if (!StringUtil.isEmpty(location)) {
setLocationString(location);
}
Icon icon = presentation.getIcon(false);
if (icon != null) setIcon(icon);
}
}
}
finally {
token.finish();
}
}
public void recalculateWidth() {
ListModel model = myList.getModel();
myTitle.setIcon(EmptyIcon.ICON_16);
myTitle.setFont(getTitleFont());
int index = 0;
while (index < model.getSize()) {
String title = getModel().titleIndex.getTitle(index);
if (title != null) {
myTitle.setText(title);
}
index++;
}
myTitle.setForeground(Gray._122);
myTitle.setAlignmentY(BOTTOM_ALIGNMENT);
}
}
private static String getSettingText(OptionDescription value) {
String hit = value.getHit();
if (hit == null) {
hit = value.getOption();
}
hit = StringUtil.unescapeXml(hit);
if (hit.length() > 60) {
hit = hit.substring(0, 60) + "...";
}
hit = hit.replace(" ", " "); //avoid extra spaces from mnemonics and xml conversion
String text = hit.trim();
if (text.endsWith(":")) {
text = text.substring(0, text.length() - 1);
}
return text;
}
private static boolean isActionValue(Object o) {
return o instanceof GotoActionModel.ActionWrapper || o instanceof AnAction;
}
private static boolean isSetting(Object o) {
return o instanceof OptionDescription;
}
private static boolean isRunConfiguration(Object o) {
return o instanceof ChooseRunConfigurationPopup.ItemWrapper;
}
private static boolean isVirtualFile(Object o) {
return o instanceof VirtualFile;
}
private static Font getTitleFont() {
return UIUtil.getLabelFont().deriveFont(UIUtil.getFontSize(UIUtil.FontSize.SMALL));
}
enum WidgetID {CLASSES, FILES, ACTIONS, SETTINGS, SYMBOLS, RUN_CONFIGURATIONS}
@SuppressWarnings("SSBasedInspection")
private class CalcThread implements Runnable {
private final Project project;
private final String pattern;
private final ProgressIndicator myProgressIndicator = new ProgressIndicatorBase();
private final ActionCallback myDone = new ActionCallback();
private final SearchListModel myListModel;
private final ArrayList<VirtualFile> myAlreadyAddedFiles = new ArrayList<VirtualFile>();
private final ArrayList<AnAction> myAlreadyAddedActions = new ArrayList<AnAction>();
public CalcThread(Project project, String pattern, boolean reuseModel) {
this.project = project;
this.pattern = pattern;
myListModel = reuseModel ? (SearchListModel)myList.getModel() : new SearchListModel();
}
@Override
public void run() {
try {
check();
//noinspection SSBasedInspection
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// this line must be called on EDT to avoid context switch at clear().append("text") Don't touch. Ask [kb]
myList.getEmptyText().setText("Searching...");
//noinspection unchecked
myList.setModel(myListModel);
}
});
if (pattern.trim().length() == 0) {
buildModelFromRecentFiles();
updatePopup();
return;
}
checkModelsUpToDate(); check();
buildTopHit(pattern); check();
buildRecentFiles(pattern); check();
updatePopup(); check();
buildToolWindows(pattern); check();
updatePopup(); check();
runReadAction(new Runnable() {
public void run() {
buildRunConfigurations(pattern);
}
}, true);
runReadAction(new Runnable() {
public void run() {
buildClasses(pattern);
}
}, true);
runReadAction(new Runnable() {
public void run() {
buildFiles(pattern);
}
}, false);
buildActionsAndSettings(pattern);
updatePopup();
runReadAction(new Runnable() {
public void run() {
buildSymbols(pattern);
}
}, true);
}
catch (ProcessCanceledException ignore) {
myDone.setRejected();
}
catch (Exception e) {
LOG.error(e);
myDone.setRejected();
}
finally {
if (!isCanceled()) {
myList.getEmptyText().setText(StatusText.DEFAULT_EMPTY_TEXT);
updatePopup();
}
if (!myDone.isProcessed()) {
myDone.setDone();
}
}
}
private void runReadAction(Runnable action, boolean checkDumb) {
if (!checkDumb || !DumbService.getInstance(project).isDumb()) {
ApplicationManager.getApplication().runReadAction(action);
updatePopup();
}
}
protected void check() {
myProgressIndicator.checkCanceled();
if (myDone.isRejected()) throw new ProcessCanceledException();
if (myBalloon == null || myBalloon.isDisposed()) throw new ProcessCanceledException();
}
private synchronized void buildToolWindows(String pattern) {
final List<ActivateToolWindowAction> actions = new ArrayList<ActivateToolWindowAction>();
for (ActivateToolWindowAction action : ToolWindowsGroup.getToolWindowActions(project)) {
String text = action.getTemplatePresentation().getText();
if (text != null && StringUtil.startsWithIgnoreCase(text, pattern)) {
actions.add(action);
if (actions.size() == MAX_TOOL_WINDOWS) {
break;
}
}
}
check();
if (actions.isEmpty()) {
return;
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
myListModel.titleIndex.toolWindows = myListModel.size();
for (Object toolWindow : actions) {
myListModel.addElement(toolWindow);
}
}
});
}
private SearchResult getActionsOrSettings(final String pattern, final int max, final boolean actions) {
final SearchResult result = new SearchResult();
final MinusculeMatcher matcher = new MinusculeMatcher("*" +pattern, NameUtil.MatchingCaseSensitivity.NONE);
if (myActionProvider == null) {
myActionProvider = createActionProvider();
}
myActionProvider.filterElements(pattern, true, new Processor<GotoActionModel.MatchedValue>() {
@Override
public boolean process(GotoActionModel.MatchedValue matched) {
check();
Object object = matched.value;
if (myListModel.contains(object)) return true;
if (!actions && isSetting(object)) {
if (matcher.matches(getSettingText((OptionDescription)object))) {
result.add(object);
}
} else if (actions && !isToolWindowAction(object) && isActionValue(object)) {
result.add(object);
}
return result.size() <= max;
}
});
return result;
}
private synchronized void buildActionsAndSettings(String pattern) {
final SearchResult actions = getActionsOrSettings(pattern, MAX_ACTIONS, true);
final SearchResult settings = getActionsOrSettings(pattern, MAX_SETTINGS, false);
check();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (isCanceled()) return;
if (actions.size() > 0) {
myListModel.titleIndex.actions = myListModel.size();
for (Object action : actions) {
myListModel.addElement(action);
}
}
myListModel.moreIndex.actions = actions.size() >= MAX_ACTIONS ? myListModel.size() - 1 : -1;
if (settings.size() > 0) {
myListModel.titleIndex.settings = myListModel.size();
for (Object setting : settings) {
myListModel.addElement(setting);
}
}
myListModel.moreIndex.settings = settings.size() >= MAX_SETTINGS ? myListModel.size() - 1 : -1;
}
});
}
private synchronized void buildFiles(final String pattern) {
final SearchResult files = getFiles(pattern, MAX_FILES, myFileChooseByName);
check();
if (files.size() > 0) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (isCanceled()) return;
myListModel.titleIndex.files = myListModel.size();
for (Object file : files) {
myListModel.addElement(file);
}
myListModel.moreIndex.files = files.needMore ? myListModel.size() - 1 : -1;
}
});
}
}
private synchronized void buildSymbols(final String pattern) {
final SearchResult symbols = getSymbols(pattern, MAX_SYMBOLS, mySymbolsChooseByName);
check();
if (symbols.size() > 0) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (isCanceled()) return;
myListModel.titleIndex.symbols = myListModel.size();
for (Object file : symbols) {
myListModel.addElement(file);
}
myListModel.moreIndex.symbols = symbols.needMore ? myListModel.size() - 1 : -1;
}
});
}
}
@Nullable
private ChooseRunConfigurationPopup.ItemWrapper getRunConfigurationByName(String name) {
final ChooseRunConfigurationPopup.ItemWrapper[] wrappers =
ChooseRunConfigurationPopup.createSettingsList(project, new ExecutorProvider() {
@Override
public Executor getExecutor() {
return ExecutorRegistry.getInstance().getExecutorById(ToolWindowId.DEBUG);
}
}, false);
for (ChooseRunConfigurationPopup.ItemWrapper wrapper : wrappers) {
if (wrapper.getText().equals(name)) {
return wrapper;
}
}
return null;
}
private synchronized void buildRunConfigurations(String pattern) {
final SearchResult runConfigurations = getConfigurations(pattern, MAX_RUN_CONFIGURATION);
if (runConfigurations.size() > 0) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (isCanceled()) return;
myListModel.titleIndex.runConfigurations = myListModel.size();
for (Object runConfiguration : runConfigurations) {
myListModel.addElement(runConfiguration);
}
myListModel.moreIndex.runConfigurations = runConfigurations.needMore ? myListModel.getSize() - 1 : -1;
}
});
}
}
private SearchResult getConfigurations(String pattern, int max) {
SearchResult configurations = new SearchResult();
MinusculeMatcher matcher = new MinusculeMatcher(pattern, NameUtil.MatchingCaseSensitivity.NONE);
final ChooseRunConfigurationPopup.ItemWrapper[] wrappers =
ChooseRunConfigurationPopup.createSettingsList(project, new ExecutorProvider() {
@Override
public Executor getExecutor() {
return ExecutorRegistry.getInstance().getExecutorById(ToolWindowId.DEBUG);
}
}, false);
check();
for (ChooseRunConfigurationPopup.ItemWrapper wrapper : wrappers) {
if (matcher.matches(wrapper.getText()) && !myListModel.contains(wrapper)) {
if (configurations.size() == max) {
configurations.needMore = true;
break;
}
configurations.add(wrapper);
}
check();
}
return configurations;
}
private synchronized void buildClasses(final String pattern) {
if (pattern.indexOf('.') != -1) {
//todo[kb] it's not a mistake. If we search for "*.png" or "index.xml" in SearchEverywhere
//todo[kb] we don't want to see Java classes started with Png or Xml. This approach should be reworked someday.
return;
}
check();
final SearchResult classes = getClasses(pattern, showAll.get(), MAX_CLASSES, myClassChooseByName);
check();
if (classes.size() > 0) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (isCanceled()) return;
myListModel.titleIndex.classes = myListModel.size();
for (Object file : classes) {
myListModel.addElement(file);
}
myListModel.moreIndex.classes = -1;
if (classes.needMore) {
myListModel.moreIndex.classes = myListModel.size() - 1;
}
}
});
}
}
private SearchResult getSymbols(String pattern, final int max, ChooseByNamePopup chooseByNamePopup) {
final SearchResult symbols = new SearchResult();
final GlobalSearchScope scope = GlobalSearchScope.projectScope(project);
chooseByNamePopup.getProvider().filterElements(chooseByNamePopup, pattern, false,
myProgressIndicator, new Processor<Object>() {
@Override
public boolean process(Object o) {
if (o instanceof PsiElement) {
final PsiElement element = (PsiElement)o;
final PsiFile file = element.getContainingFile();
if (!myListModel.contains(o) &&
//some elements are non-physical like DB columns
(file == null || (file.getVirtualFile() != null && scope.accept(file.getVirtualFile())))) {
symbols.add(o);
}
}
symbols.needMore = symbols.size() == max;
return !symbols.needMore;
}
});
return symbols;
}
private SearchResult getClasses(String pattern, boolean includeLibs, final int max, ChooseByNamePopup chooseByNamePopup) {
final SearchResult classes = new SearchResult();
if (chooseByNamePopup == null) {
return classes;
}
chooseByNamePopup.getProvider().filterElements(chooseByNamePopup, pattern, includeLibs,
myProgressIndicator, new Processor<Object>() {
@Override
public boolean process(Object o) {
if (o instanceof PsiElement && !myListModel.contains(o) && !classes.contains(o)) {
if (classes.size() == max) {
classes.needMore = true;
return false;
}
classes.add(o);
}
return true;
}
});
if (!includeLibs && classes.isEmpty()) {
return getClasses(pattern, true, max, chooseByNamePopup);
}
return classes;
}
private SearchResult getFiles(final String pattern, final int max, ChooseByNamePopup chooseByNamePopup) {
final SearchResult files = new SearchResult();
if (chooseByNamePopup == null) {
return files;
}
final GlobalSearchScope scope = GlobalSearchScope.projectScope(project);
chooseByNamePopup.getProvider().filterElements(chooseByNamePopup, pattern, true,
myProgressIndicator, new Processor<Object>() {
@Override
public boolean process(Object o) {
VirtualFile file = null;
if (o instanceof VirtualFile) {
file = (VirtualFile)o;
} else if (o instanceof PsiFile) {
file = ((PsiFile)o).getVirtualFile();
} else if (o instanceof PsiDirectory) {
file = ((PsiDirectory)o).getVirtualFile();
}
if (file != null
&& !(pattern.indexOf(' ') != -1 && file.getName().indexOf(' ') == -1)
&& (showAll.get() || scope.accept(file)
&& !myListModel.contains(file)
&& !myAlreadyAddedFiles.contains(file))
&& !files.contains(file)) {
if (files.size() == max) {
files.needMore = true;
return false;
}
files.add(file);
}
return true;
}
});
return files;
}
private synchronized void buildRecentFiles(String pattern) {
final MinusculeMatcher matcher = new MinusculeMatcher("*" + pattern, NameUtil.MatchingCaseSensitivity.NONE);
final ArrayList<VirtualFile> files = new ArrayList<VirtualFile>();
final List<VirtualFile> selected = Arrays.asList(FileEditorManager.getInstance(project).getSelectedFiles());
for (VirtualFile file : ArrayUtil.reverseArray(EditorHistoryManager.getInstance(project).getFiles())) {
if (StringUtil.isEmptyOrSpaces(pattern) || matcher.matches(file.getName())) {
if (!files.contains(file) && !selected.contains(file)) {
files.add(file);
}
}
if (files.size() > MAX_RECENT_FILES) break;
}
if (files.size() > 0) {
myAlreadyAddedFiles.addAll(files);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (isCanceled()) return;
myListModel.titleIndex.recentFiles = myListModel.size();
for (Object file : files) {
myListModel.addElement(file);
}
}
});
}
}
private boolean isCanceled() {
return myProgressIndicator.isCanceled() || myDone.isRejected();
}
private synchronized void buildTopHit(String pattern) {
final List<Object> elements = new ArrayList<Object>();
final HistoryItem history = myHistoryItem;
if (history != null) {
final HistoryType type = parseHistoryType(history.type);
if (type != null) {
switch (type){
case PSI:
if (!DumbService.isDumb(project)) {
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
final int i = history.fqn.indexOf("://");
if (i != -1) {
final String langId = history.fqn.substring(0, i);
final Language language = Language.findLanguageByID(langId);
final String psiFqn = history.fqn.substring(i + 3);
if (language != null) {
final PsiElement psi =
LanguagePsiElementExternalizer.INSTANCE.forLanguage(language).findByQualifiedName(project, psiFqn);
if (psi != null) {
elements.add(psi);
final PsiFile psiFile = psi.getContainingFile();
if (psiFile != null) {
final VirtualFile file = psiFile.getVirtualFile();
if (file != null) {
myAlreadyAddedFiles.add(file);
}
}
}
}
}
}
});
}
break;
case FILE:
final VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(history.fqn);
if (file != null) {
elements.add(file);
}
break;
case SETTING:
break;
case ACTION:
final AnAction action = ActionManager.getInstance().getAction(history.fqn);
if (action != null) {
elements.add(action);
myAlreadyAddedActions.add(action);
}
break;
case RUN_CONFIGURATION:
if (!DumbService.isDumb(project)) {
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
final ChooseRunConfigurationPopup.ItemWrapper runConfiguration = getRunConfigurationByName(history.fqn);
if (runConfiguration != null) {
elements.add(runConfiguration);
}
}
});
}
break;
}
}
}
final Consumer<Object> consumer = new Consumer<Object>() {
@Override
public void consume(Object o) {
if (isSetting(o) || isVirtualFile(o) || isActionValue(o) || o instanceof PsiElement) {
if (o instanceof AnAction && myAlreadyAddedActions.contains(o)) {
return;
}
elements.add(o);
}
}
};
final ActionManager actionManager = ActionManager.getInstance();
final List<String> actions = AbbreviationManager.getInstance().findActions(pattern);
for (String actionId : actions) {
consumer.consume(actionManager.getAction(actionId));
}
for (SearchTopHitProvider provider : SearchTopHitProvider.EP_NAME.getExtensions()) {
check();
provider.consumeTopHits(pattern, consumer);
}
if (elements.size() > 0) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (isCanceled()) return;
for (Object element : elements.toArray()) {
if (element instanceof AnAction) {
final AnAction action = (AnAction)element;
final AnActionEvent e = new AnActionEvent(myActionEvent.getInputEvent(),
myActionEvent.getDataContext(),
myActionEvent.getPlace(),
action.getTemplatePresentation(),
myActionEvent.getActionManager(),
myActionEvent.getModifiers());
ActionUtil.performDumbAwareUpdate(action, e, false);
final Presentation presentation = e.getPresentation();
if (!presentation.isEnabled() || !presentation.isVisible() || StringUtil.isEmpty(presentation.getText())) {
elements.remove(element);
}
if (isCanceled()) return;
}
}
if (isCanceled() || elements.isEmpty()) return;
myListModel.titleIndex.topHit = myListModel.size();
for (Object element : elements) {
myListModel.addElement(element);
}
}
});
}
}
private synchronized void checkModelsUpToDate() {
if (myClassModel == null) {
myClassModel = new GotoClassModel2(project);
myFileModel = new GotoFileModel(project);
mySymbolsModel = new GotoSymbolModel2(project);
myFileChooseByName = ChooseByNamePopup.createPopup(project, myFileModel, (PsiElement)null);
myClassChooseByName = ChooseByNamePopup.createPopup(project, myClassModel, (PsiElement)null);
mySymbolsChooseByName = ChooseByNamePopup.createPopup(project, mySymbolsModel, (PsiElement)null);
project.putUserData(ChooseByNamePopup.CHOOSE_BY_NAME_POPUP_IN_PROJECT_KEY, null);
myActionProvider = createActionProvider();
myConfigurables.clear();
fillConfigurablesIds(null, ShowSettingsUtilImpl.getConfigurables(project, true));
}
}
private void buildModelFromRecentFiles() {
buildRecentFiles("");
}
private GotoActionItemProvider createActionProvider() {
GotoActionModel model = new GotoActionModel(project, myFocusComponent, myEditor, myFile) {
@Override
protected MatchMode actionMatches(String pattern, @NotNull AnAction anAction) {
String text = anAction.getTemplatePresentation().getText();
return text != null && NameUtil.buildMatcher("*" + pattern, NameUtil.MatchingCaseSensitivity.NONE)
.matches(text) ? MatchMode.NAME : MatchMode.NONE;
}
};
return new GotoActionItemProvider(model);
}
@SuppressWarnings("SSBasedInspection")
private void updatePopup() {
check();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
myListModel.update();
myList.revalidate();
myList.repaint();
myRenderer.recalculateWidth();
if (myBalloon == null || myBalloon.isDisposed()) {
return;
}
if (myPopup == null || !myPopup.isVisible()) {
final ActionCallback callback = ListDelegationUtil.installKeyboardDelegation(getField().getTextEditor(), myList);
final ComponentPopupBuilder builder = JBPopupFactory.getInstance()
.createComponentPopupBuilder(new JBScrollPane(myList), null);
myPopup = builder
.setRequestFocus(false)
.setCancelKeyEnabled(false)
.setCancelCallback(new Computable<Boolean>() {
@Override
public Boolean compute() {
return myBalloon == null || myBalloon.isDisposed() || !getField().getTextEditor().hasFocus();
}
})
.createPopup();
myPopup.getContent().setBorder(new EmptyBorder(0, 0, 0, 0));
Disposer.register(myPopup, new Disposable() {
@Override
public void dispose() {
callback.setDone();
resetFields();
myNonProjectCheckBox.setSelected(false);
ActionToolbarImpl.updateAllToolbarsImmediately();
if (myActionEvent != null && myActionEvent.getInputEvent() instanceof MouseEvent) {
final Component component = myActionEvent.getInputEvent().getComponent();
if (component != null) {
final JLabel label = UIUtil.getParentOfType(JLabel.class, component);
if (label != null) {
label.setIcon(AllIcons.Actions.FindPlain);
}
}
}
myActionEvent = null;
}
});
myPopup.show(new RelativePoint(getField().getParent(), new Point(0, getField().getParent().getHeight())));
updatePopupBounds();
ActionManager.getInstance().addAnActionListener(new AnActionListener.Adapter() {
@Override
public void beforeActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) {
if (action instanceof TextComponentEditorAction) {
return;
}
myPopup.cancel();
}
}, myPopup);
}
else {
myList.revalidate();
myList.repaint();
}
ListScrollingUtil.ensureSelectionExists(myList);
if (myList.getModel().getSize() > 0) {
updatePopupBounds();
}
}
});
}
public ActionCallback cancel() {
myProgressIndicator.cancel();
myDone.setRejected();
return myDone;
}
public ActionCallback insert(final int index, final WidgetID id) {
ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
public void run() {
runReadAction(new Runnable() {
@Override
public void run() {
try {
final SearchResult result
= id == WidgetID.CLASSES ? getClasses(pattern, showAll.get(), DEFAULT_MORE_STEP_COUNT, myClassChooseByName)
: id == WidgetID.FILES ? getFiles(pattern, DEFAULT_MORE_STEP_COUNT, myFileChooseByName)
: id == WidgetID.RUN_CONFIGURATIONS ? getConfigurations(pattern, DEFAULT_MORE_STEP_COUNT)
: id == WidgetID.SYMBOLS ? getSymbols(pattern, DEFAULT_MORE_STEP_COUNT, mySymbolsChooseByName)
: id == WidgetID.ACTIONS ? getActionsOrSettings(pattern, DEFAULT_MORE_STEP_COUNT, true)
: id == WidgetID.SETTINGS ? getActionsOrSettings(pattern, DEFAULT_MORE_STEP_COUNT, false)
: new SearchResult();
check();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
int shift = 0;
int i = index+1;
for (Object o : result) {
//noinspection unchecked
myListModel.insertElementAt(o, i);
shift++;
i++;
}
MoreIndex moreIndex = myListModel.moreIndex;
myListModel.titleIndex.shift(index, shift);
moreIndex.shift(index, shift);
if (!result.needMore) {
switch (id) {
case CLASSES: moreIndex.classes = -1; break;
case FILES: moreIndex.files = -1; break;
case ACTIONS: moreIndex.actions = -1; break;
case SETTINGS: moreIndex.settings = -1; break;
case SYMBOLS: moreIndex.symbols = -1; break;
case RUN_CONFIGURATIONS: moreIndex.runConfigurations = -1; break;
}
}
ListScrollingUtil.selectItem(myList, index);
myDone.setDone();
}
catch (Exception e) {
myDone.setRejected();
}
}
});
}
catch (Exception e) {
myDone.setRejected();
}
}
}, true);
}
});
return myDone;
}
public ActionCallback start() {
ApplicationManager.getApplication().executeOnPooledThread(this);
return myDone;
}
}
protected void resetFields() {
if (myBalloon!= null) {
myBalloon.cancel();
myBalloon = null;
}
myCurrentWorker.doWhenProcessed(new Runnable() {
@Override
public void run() {
myFileModel = null;
if (myFileChooseByName != null) {
myFileChooseByName.close(false);
myFileChooseByName = null;
}
if (myClassChooseByName != null) {
myClassChooseByName.close(false);
myClassChooseByName = null;
}
if (mySymbolsChooseByName != null) {
mySymbolsChooseByName.close(false);
mySymbolsChooseByName = null;
}
final Object lock = myCalcThread;
if (lock != null) {
synchronized (lock) {
myClassModel = null;
myActionProvider = null;
mySymbolsModel = null;
myConfigurables.clear();
myFocusComponent = null;
myContextComponent = null;
myFocusOwner = null;
myRenderer.myProject = null;
myPopup = null;
myHistoryIndex = 0;
myPopupActualWidth = 0;
myCurrentWorker = ActionCallback.DONE;
showAll.set(false);
myCalcThread = null;
}
}
}
});
mySkipFocusGain = false;
}
private void updatePopupBounds() {
if (myPopup == null || !myPopup.isVisible()) {
return;
}
final Container parent = getField().getParent();
final Dimension size = myList.getParent().getParent().getPreferredSize();
size.width = myPopupActualWidth - 2;
if (size.width + 2 < parent.getWidth()) {
size.width = parent.getWidth();
}
if (myList.getItemsCount() == 0) {
size.height = 70;
}
Dimension sz = new Dimension(size.width, myList.getPreferredSize().height);
if (sz.width > POPUP_MAX_WIDTH || sz.height > POPUP_MAX_WIDTH) {
final JBScrollPane pane = new JBScrollPane();
final int extraWidth = pane.getVerticalScrollBar().getWidth() + 1;
final int extraHeight = pane.getHorizontalScrollBar().getHeight() + 1;
sz = new Dimension(Math.min(POPUP_MAX_WIDTH, Math.max(getField().getWidth(), sz.width + extraWidth)), Math.min(POPUP_MAX_WIDTH, sz.height + extraHeight));
sz.width += 20;
sz.height+=2;
} else {
sz.width+=2;
sz.height+=2;
}
sz.width = Math.max(sz.width, myPopup.getSize().width);
myPopup.setSize(sz);
if (myActionEvent != null && myActionEvent.getInputEvent() == null) {
final Point p = parent.getLocationOnScreen();
p.y += parent.getHeight();
if (parent.getWidth() < sz.width) {
p.x -= sz.width - parent.getWidth();
}
myPopup.setLocation(p);
} else {
try {
adjustPopup();
}
catch (Exception ignore) {}
}
}
private void adjustPopup() {
// new PopupPositionManager.PositionAdjuster(getField().getParent(), 0).adjust(myPopup, PopupPositionManager.Position.BOTTOM);
final Dimension d = PopupPositionManager.PositionAdjuster.getPopupSize(myPopup);
final JComponent myRelativeTo = myBalloon.getContent();
Point myRelativeOnScreen = myRelativeTo.getLocationOnScreen();
Rectangle screen = ScreenUtil.getScreenRectangle(myRelativeOnScreen);
Rectangle popupRect = null;
Rectangle r = new Rectangle(myRelativeOnScreen.x, myRelativeOnScreen.y + myRelativeTo.getHeight(), d.width, d.height);
if (screen.contains(r)) {
popupRect = r;
}
if (popupRect != null) {
myPopup.setLocation(new Point(r.x, r.y));
}
else {
if (r.y + d.height > screen.y + screen.height) {
r.height = screen.y + screen.height - r.y - 2;
}
if (r.width > screen.width) {
r.width = screen.width - 50;
}
if (r.x + r.width > screen.x + screen.width) {
r.x = screen.x + screen.width - r.width - 2;
}
myPopup.setSize(r.getSize());
myPopup.setLocation(r.getLocation());
}
}
private static boolean isToolWindowAction(Object o) {
return isActionValue(o)
&& o instanceof GotoActionModel.ActionWrapper
&& ((GotoActionModel.ActionWrapper)o).getAction() instanceof ActivateToolWindowAction;
}
private void fillConfigurablesIds(String pathToParent, Configurable[] configurables) {
for (Configurable configurable : configurables) {
if (configurable instanceof SearchableConfigurable) {
final String id = ((SearchableConfigurable)configurable).getId();
String name = configurable.getDisplayName();
if (pathToParent != null) {
name = pathToParent + " -> " + name;
}
myConfigurables.put(id, name);
if (configurable instanceof SearchableConfigurable.Parent) {
fillConfigurablesIds(name, ((SearchableConfigurable.Parent)configurable).getConfigurables());
}
}
}
}
static class MoreIndex {
volatile int classes = -1;
volatile int files = -1;
volatile int actions = -1;
volatile int settings = -1;
volatile int symbols = -1;
volatile int runConfigurations = -1;
public void shift(int index, int shift) {
if (runConfigurations >= index) runConfigurations += shift;
if (classes >= index) classes += shift;
if (files >= index) files += shift;
if (actions >= index) actions += shift;
if (settings >= index) settings += shift;
if (symbols >= index) symbols += shift;
}
}
static class TitleIndex {
volatile int topHit = -1;
volatile int recentFiles = -1;
volatile int runConfigurations = -1;
volatile int classes = -1;
volatile int files = -1;
volatile int actions = -1;
volatile int settings = -1;
volatile int toolWindows = -1;
volatile int symbols = -1;
final String gotoClassTitle;
final String gotoFileTitle;
final String gotoActionTitle;
final String gotoSettingsTitle;
final String gotoRecentFilesTitle;
final String gotoRunConfigurationsTitle;
final String gotoSymbolTitle;
static final String toolWindowsTitle = "Tool Windows";
TitleIndex() {
String gotoClass = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("GotoClass"));
gotoClassTitle = StringUtil.isEmpty(gotoClass) ? "Classes" : "Classes (" + gotoClass + ")";
String gotoFile = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("GotoFile"));
gotoFileTitle = StringUtil.isEmpty(gotoFile) ? "Files" : "Files (" + gotoFile + ")";
String gotoAction = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("GotoAction"));
gotoActionTitle = StringUtil.isEmpty(gotoAction) ? "Actions" : "Actions (" + gotoAction + ")";
String gotoSettings = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("ShowSettings"));
gotoSettingsTitle = StringUtil.isEmpty(gotoAction) ? "Preferences" : "Preferences (" + gotoSettings + ")";
String gotoRecentFiles = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("RecentFiles"));
gotoRecentFilesTitle = StringUtil.isEmpty(gotoRecentFiles) ? "Recent Files" : "Recent Files (" + gotoRecentFiles + ")";
String gotoSymbol = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("GotoSymbol"));
gotoSymbolTitle = StringUtil.isEmpty(gotoSymbol) ? "Symbols" : "Symbols (" + gotoSymbol + ")";
String gotoRunConfiguration = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("ChooseDebugConfiguration"));
if (StringUtil.isEmpty(gotoRunConfiguration)) {
gotoRunConfiguration = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("ChooseRunConfiguration"));
}
gotoRunConfigurationsTitle = StringUtil.isEmpty(gotoRunConfiguration) ? "Run Configurations" : "Run Configurations (" + gotoRunConfiguration + ")";
}
String getTitle(int index) {
if (index == topHit) return index == 0 ? "Top Hit" : "Top Hits";
if (index == recentFiles) return gotoRecentFilesTitle;
if (index == runConfigurations) return gotoRunConfigurationsTitle;
if (index == classes) return gotoClassTitle;
if (index == files) return gotoFileTitle;
if (index == toolWindows) return toolWindowsTitle;
if (index == actions) return gotoActionTitle;
if (index == settings) return gotoSettingsTitle;
if (index == symbols) return gotoSymbolTitle;
return null;
}
public void clear() {
topHit = -1;
runConfigurations = -1;
recentFiles = -1;
classes = -1;
files = -1;
actions = -1;
settings = -1;
toolWindows = -1;
}
public void shift(int index, int shift) {
if (toolWindows != - 1 && toolWindows > index) toolWindows += shift;
if (settings != - 1 && settings > index) settings += shift;
if (actions != - 1 && actions > index) actions += shift;
if (files != - 1 && files > index) files += shift;
if (classes != - 1 && classes > index) classes += shift;
if (runConfigurations != - 1 && runConfigurations > index) runConfigurations += shift;
if (symbols != - 1 && symbols > index) symbols += shift;
}
}
static class SearchResult extends ArrayList<Object> {
boolean needMore;
}
@SuppressWarnings("unchecked")
private static class SearchListModel extends DefaultListModel {
@SuppressWarnings("UseOfObsoleteCollectionType")
Vector myDelegate;
volatile TitleIndex titleIndex = new TitleIndex();
volatile MoreIndex moreIndex = new MoreIndex();
private SearchListModel() {
super();
myDelegate = ReflectionUtil.getField(DefaultListModel.class, this, Vector.class, "delegate");
}
int next(int index) {
int[] all = getAll();
Arrays.sort(all);
for (int next : all) {
if (next > index) return next;
}
return 0;
}
int[] getAll() {
return new int[]{
titleIndex.topHit,
titleIndex.recentFiles,
titleIndex.runConfigurations,
titleIndex.classes,
titleIndex.files,
titleIndex.actions,
titleIndex.settings,
titleIndex.toolWindows,
titleIndex.symbols,
moreIndex.classes,
moreIndex.actions,
moreIndex.files,
moreIndex.settings,
moreIndex.symbols,
moreIndex.runConfigurations
};
}
int prev(int index) {
int[] all = getAll();
Arrays.sort(all);
for (int i = all.length-1; i >= 0; i--) {
if (all[i] != -1 && all[i] < index) return all[i];
}
return all[all.length - 1];
}
@Override
public void addElement(Object obj) {
myDelegate.add(obj);
}
public void update() {
fireContentsChanged(this, 0, getSize() - 1);
}
}
static class More extends JPanel {
static final More instance = new More();
final JLabel label = new JLabel(" ... more ");
private More() {
super(new BorderLayout());
add(label, BorderLayout.CENTER);
}
static More get(boolean isSelected) {
instance.setBackground(UIUtil.getListBackground(isSelected));
instance.label.setForeground(UIUtil.getLabelDisabledForeground());
instance.label.setFont(getTitleFont());
instance.label.setBackground(UIUtil.getListBackground(isSelected));
return instance;
}
}
private static JComponent createTitle(String titleText) {
JLabel titleLabel = new JLabel(titleText);
titleLabel.setFont(getTitleFont());
titleLabel.setForeground(UIUtil.getLabelDisabledForeground());
final Color bg = UIUtil.getListBackground();
SeparatorComponent separatorComponent =
new SeparatorComponent(titleLabel.getPreferredSize().height / 2, new JBColor(Gray._220, Gray._80), null);
JPanel result = new JPanel(new BorderLayout(5, 10));
result.add(titleLabel, BorderLayout.WEST);
result.add(separatorComponent, BorderLayout.CENTER);
result.setBackground(bg);
return result;
}
private enum HistoryType {PSI, FILE, SETTING, ACTION, RUN_CONFIGURATION}
@Nullable
private static HistoryType parseHistoryType(@Nullable String name) {
try {
return HistoryType.valueOf(name);
} catch (Exception e) {
return null;
}
}
private static class HistoryItem {
final String pattern, type, fqn;
private HistoryItem(String pattern, String type, String fqn) {
this.pattern = pattern;
this.type = type;
this.fqn = fqn;
}
public String toString() {
return pattern + "\t" + type + "\t" + fqn;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
HistoryItem item = (HistoryItem)o;
if (!pattern.equals(item.pattern)) return false;
return true;
}
@Override
public int hashCode() {
return pattern.hashCode();
}
}
}