SE settings icon style: hamburger -> gear
[idea/community.git] / platform / lang-impl / src / com / intellij / ide / actions / SearchEverywhereAction.java
1 /*
2  * Copyright 2000-2014 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.intellij.ide.actions;
17
18 import com.intellij.codeInsight.navigation.NavigationUtil;
19 import com.intellij.execution.Executor;
20 import com.intellij.execution.ExecutorRegistry;
21 import com.intellij.execution.RunnerAndConfigurationSettings;
22 import com.intellij.execution.actions.ChooseRunConfigurationPopup;
23 import com.intellij.execution.actions.ExecutorProvider;
24 import com.intellij.execution.executors.DefaultRunExecutor;
25 import com.intellij.execution.impl.RunDialog;
26 import com.intellij.featureStatistics.FeatureUsageTracker;
27 import com.intellij.icons.AllIcons;
28 import com.intellij.ide.DataManager;
29 import com.intellij.ide.IdeEventQueue;
30 import com.intellij.ide.IdeTooltipManager;
31 import com.intellij.ide.SearchTopHitProvider;
32 import com.intellij.ide.structureView.StructureView;
33 import com.intellij.ide.structureView.StructureViewBuilder;
34 import com.intellij.ide.structureView.StructureViewModel;
35 import com.intellij.ide.structureView.StructureViewTreeElement;
36 import com.intellij.ide.ui.OptionsTopHitProvider;
37 import com.intellij.ide.ui.UISettings;
38 import com.intellij.ide.ui.laf.darcula.ui.DarculaTextBorder;
39 import com.intellij.ide.ui.laf.darcula.ui.DarculaTextFieldUI;
40 import com.intellij.ide.ui.search.BooleanOptionDescription;
41 import com.intellij.ide.ui.search.OptionDescription;
42 import com.intellij.ide.util.DefaultPsiElementCellRenderer;
43 import com.intellij.ide.util.PropertiesComponent;
44 import com.intellij.ide.util.gotoByName.*;
45 import com.intellij.ide.util.treeView.smartTree.TreeElement;
46 import com.intellij.lang.Language;
47 import com.intellij.lang.LanguagePsiElementExternalizer;
48 import com.intellij.navigation.ItemPresentation;
49 import com.intellij.navigation.NavigationItem;
50 import com.intellij.openapi.Disposable;
51 import com.intellij.openapi.actionSystem.*;
52 import com.intellij.openapi.actionSystem.ex.ActionUtil;
53 import com.intellij.openapi.actionSystem.ex.AnActionListener;
54 import com.intellij.openapi.actionSystem.ex.CustomComponentAction;
55 import com.intellij.openapi.actionSystem.impl.ActionToolbarImpl;
56 import com.intellij.openapi.application.AccessToken;
57 import com.intellij.openapi.application.ApplicationManager;
58 import com.intellij.openapi.diagnostic.Logger;
59 import com.intellij.openapi.editor.Editor;
60 import com.intellij.openapi.editor.actions.TextComponentEditorAction;
61 import com.intellij.openapi.fileEditor.FileEditor;
62 import com.intellij.openapi.fileEditor.FileEditorManager;
63 import com.intellij.openapi.fileEditor.OpenFileDescriptor;
64 import com.intellij.openapi.fileEditor.impl.EditorHistoryManager;
65 import com.intellij.openapi.keymap.KeymapManager;
66 import com.intellij.openapi.keymap.KeymapUtil;
67 import com.intellij.openapi.keymap.MacKeymapUtil;
68 import com.intellij.openapi.keymap.impl.ModifierKeyDoubleClickHandler;
69 import com.intellij.openapi.options.Configurable;
70 import com.intellij.openapi.options.SearchableConfigurable;
71 import com.intellij.openapi.progress.ProcessCanceledException;
72 import com.intellij.openapi.progress.ProgressIndicator;
73 import com.intellij.openapi.progress.util.ProgressIndicatorBase;
74 import com.intellij.openapi.project.DumbAware;
75 import com.intellij.openapi.project.DumbAwareAction;
76 import com.intellij.openapi.project.DumbService;
77 import com.intellij.openapi.project.Project;
78 import com.intellij.openapi.ui.popup.ComponentPopupBuilder;
79 import com.intellij.openapi.ui.popup.JBPopup;
80 import com.intellij.openapi.ui.popup.JBPopupFactory;
81 import com.intellij.openapi.util.*;
82 import com.intellij.openapi.util.registry.Registry;
83 import com.intellij.openapi.util.text.StringUtil;
84 import com.intellij.openapi.vfs.VirtualFile;
85 import com.intellij.openapi.vfs.VirtualFileManager;
86 import com.intellij.openapi.vfs.VirtualFilePathWrapper;
87 import com.intellij.openapi.wm.*;
88 import com.intellij.openapi.wm.impl.IdeFrameImpl;
89 import com.intellij.pom.Navigatable;
90 import com.intellij.psi.*;
91 import com.intellij.psi.codeStyle.MinusculeMatcher;
92 import com.intellij.psi.codeStyle.NameUtil;
93 import com.intellij.psi.search.GlobalSearchScope;
94 import com.intellij.ui.*;
95 import com.intellij.ui.awt.RelativePoint;
96 import com.intellij.ui.border.CustomLineBorder;
97 import com.intellij.ui.components.JBLabel;
98 import com.intellij.ui.components.JBList;
99 import com.intellij.ui.components.JBScrollPane;
100 import com.intellij.ui.components.OnOffButton;
101 import com.intellij.ui.components.panels.NonOpaquePanel;
102 import com.intellij.ui.popup.AbstractPopup;
103 import com.intellij.ui.popup.PopupPositionManager;
104 import com.intellij.util.*;
105 import com.intellij.util.containers.ContainerUtil;
106 import com.intellij.util.text.Matcher;
107 import com.intellij.util.ui.EmptyIcon;
108 import com.intellij.util.ui.StatusText;
109 import com.intellij.util.ui.UIUtil;
110 import org.jetbrains.annotations.NonNls;
111 import org.jetbrains.annotations.NotNull;
112 import org.jetbrains.annotations.Nullable;
113
114 import javax.swing.*;
115 import javax.swing.border.EmptyBorder;
116 import javax.swing.event.DocumentEvent;
117 import java.awt.*;
118 import java.awt.event.*;
119 import java.util.*;
120 import java.util.List;
121 import java.util.concurrent.atomic.AtomicBoolean;
122
123 /**
124  * @author Konstantin Bulenkov
125  */
126 @SuppressWarnings("FieldAccessedSynchronizedAndUnsynchronized")
127 public class SearchEverywhereAction extends AnAction implements CustomComponentAction, DumbAware, DataProvider, RightAlignedToolbarAction {
128   public static final String SE_HISTORY_KEY = "SearchEverywhereHistoryKey";
129   public static final int SEARCH_FIELD_COLUMNS = 25;
130   private static final int MAX_CLASSES = 6;
131   private static final int MAX_FILES = 6;
132   private static final int MAX_RUN_CONFIGURATION = 6;
133   private static final int MAX_TOOL_WINDOWS = 4;
134   private static final int MAX_SYMBOLS = 6;
135   private static final int MAX_SETTINGS = 5;
136   private static final int MAX_ACTIONS = 5;
137   private static final int MAX_STRUCTURE = 10;
138   private static final int MAX_RECENT_FILES = 10;
139   private static final int DEFAULT_MORE_STEP_COUNT = 15;
140   public static final int MAX_SEARCH_EVERYWHERE_HISTORY = 50;
141   public static final int MAX_TOP_HIT = 15;
142   private static final int POPUP_MAX_WIDTH = 600;
143   private static final Logger LOG = Logger.getInstance("#" + SearchEverywhereAction.class.getName());
144
145   private SearchEverywhereAction.MyListRenderer myRenderer;
146   MySearchTextField myPopupField;
147   private volatile GotoClassModel2 myClassModel;
148   private volatile GotoFileModel myFileModel;
149   private volatile GotoActionItemProvider myActionProvider;
150   private volatile GotoSymbolModel2 mySymbolsModel;
151   private Component myFocusComponent;
152   private JBPopup myPopup;
153   private Map<String, String> myConfigurables = new HashMap<String, String>();
154
155   private Alarm myAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD, ApplicationManager.getApplication());
156   private Alarm myUpdateAlarm = new Alarm(ApplicationManager.getApplication());
157   private JBList myList;
158   private JCheckBox myNonProjectCheckBox;
159   private AnActionEvent myActionEvent;
160   private Component myContextComponent;
161   private CalcThread myCalcThread;
162   private static AtomicBoolean ourShiftIsPressed = new AtomicBoolean(false);
163   private static AtomicBoolean showAll = new AtomicBoolean(false);
164   private volatile ActionCallback myCurrentWorker = ActionCallback.DONE;
165   private int myHistoryIndex = 0;
166   boolean mySkipFocusGain = false;
167
168   static {
169     ModifierKeyDoubleClickHandler.getInstance().registerAction(IdeActions.ACTION_SEARCH_EVERYWHERE, KeyEvent.VK_SHIFT, -1);
170
171     IdeEventQueue.getInstance().addPostprocessor(new IdeEventQueue.EventDispatcher() {
172       @Override
173       public boolean dispatch(AWTEvent event) {
174         if (event instanceof KeyEvent) {
175           final int keyCode = ((KeyEvent)event).getKeyCode();
176           if (keyCode == KeyEvent.VK_SHIFT) {
177             ourShiftIsPressed.set(event.getID() == KeyEvent.KEY_PRESSED);
178           }
179         }
180         return false;
181       }
182     }, null);
183   }
184
185   private volatile JBPopup myBalloon;
186   private int myPopupActualWidth;
187   private Component myFocusOwner;
188   private ChooseByNamePopup myFileChooseByName;
189   private ChooseByNamePopup myClassChooseByName;
190   private ChooseByNamePopup mySymbolsChooseByName;
191   private StructureViewModel myStructureModel;
192
193
194   private Editor myEditor;
195   private FileEditor myFileEditor;
196   private PsiFile myFile;
197   private HistoryItem myHistoryItem;
198
199   @Override
200   public JComponent createCustomComponent(Presentation presentation) {
201     JPanel panel = new JPanel(new BorderLayout()) {
202       @Override
203       protected void paintComponent(Graphics g) {
204         if (myBalloon != null && !myBalloon.isDisposed() && myActionEvent != null && myActionEvent.getInputEvent() instanceof MouseEvent) {
205           final Gradient gradient = getGradientColors();
206           ((Graphics2D)g).setPaint(new GradientPaint(0, 0, gradient.getStartColor(), 0, getHeight(), gradient.getEndColor()));
207           g.fillRect(0,0,getWidth(), getHeight());
208         } else {
209           super.paintComponent(g);
210         }
211       }
212     };
213     panel.setOpaque(false);
214
215     final JLabel label = new JBLabel(AllIcons.Actions.FindPlain) {
216       {
217         enableEvents(AWTEvent.MOUSE_EVENT_MASK);
218         enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK);
219       }
220     };
221     panel.add(label, BorderLayout.CENTER);
222     initTooltip(label);
223     label.addMouseListener(new MouseAdapter() {
224       @Override
225       public void mousePressed(MouseEvent e) {
226         if (myBalloon != null) {
227           myBalloon.cancel();
228         }
229         myFocusOwner = IdeFocusManager.findInstance().getFocusOwner();
230         label.setToolTipText(null);
231         IdeTooltipManager.getInstance().hideCurrentNow(false);
232         label.setIcon(AllIcons.Actions.FindWhite);
233         actionPerformed(null, e);
234       }
235
236       @Override
237       public void mouseEntered(MouseEvent e) {
238         if (myBalloon == null || myBalloon.isDisposed()) {
239           label.setIcon(AllIcons.Actions.Find);
240         }
241       }
242
243       @Override
244       public void mouseExited(MouseEvent e) {
245         if (myBalloon == null || myBalloon.isDisposed()) {
246           label.setIcon(AllIcons.Actions.FindPlain);
247         }
248       }
249     });
250
251     return panel;
252   }
253
254   private static Gradient getGradientColors() {
255     return new Gradient(
256       new JBColor(new Color(101, 147, 242), new Color(64, 80, 94)),
257       new JBColor(new Color(46, 111, 205), new Color(53, 65, 87)));
258   }
259
260   public SearchEverywhereAction() {
261     updateComponents();
262     //noinspection SSBasedInspection
263     SwingUtilities.invokeLater(new Runnable() {
264       public void run() {
265         onFocusLost();
266       }
267     });
268
269   }
270
271   private void updateComponents() {
272     myRenderer = new MyListRenderer();
273     myList = new JBList() {
274       @Override
275       public Dimension getPreferredSize() {
276         final Dimension size = super.getPreferredSize();
277         return new Dimension(Math.min(size.width - 2, POPUP_MAX_WIDTH), size.height);
278       }
279
280       @Override
281       public void clearSelection() {
282         //avoid blinking
283       }
284
285       @Override
286       public Object getSelectedValue() {
287         try {
288           return super.getSelectedValue();
289         } catch (Exception e) {
290           return null;
291         }
292       }
293     };
294     myList.setCellRenderer(myRenderer);
295     myList.addMouseListener(new MouseAdapter() {
296       @Override
297       public void mouseClicked(MouseEvent e) {
298         e.consume();
299         final int i = myList.locationToIndex(e.getPoint());
300         if (i != -1) {
301           mySkipFocusGain = true;
302           getField().requestFocus();
303           //noinspection SSBasedInspection
304           SwingUtilities.invokeLater(new Runnable() {
305             @Override
306             public void run() {
307               myList.setSelectedIndex(i);
308               doNavigate(i);
309             }
310           });
311         }
312       }
313     });
314
315     myNonProjectCheckBox = new JCheckBox();
316     myNonProjectCheckBox.setOpaque(false);
317     myNonProjectCheckBox.setAlignmentX(1.0f);
318     myNonProjectCheckBox.addActionListener(new ActionListener() {
319       @Override
320       public void actionPerformed(ActionEvent e) {
321         if (showAll.get() != myNonProjectCheckBox.isSelected()) {
322           showAll.set(!showAll.get());
323           final JTextField editor = UIUtil.findComponentOfType(myBalloon.getContent(), JTextField.class);
324           if (editor != null) {
325             final String pattern = editor.getText();
326             myAlarm.cancelAllRequests();
327             myAlarm.addRequest(new Runnable() {
328               @Override
329               public void run() {
330                 if (editor.hasFocus()) {
331                   rebuildList(pattern);
332                 }
333               }
334             }, 30);
335           }
336         }
337       }
338     });
339   }
340
341   private static void initTooltip(JLabel label) {
342     final String shortcutText;
343     shortcutText = getShortcut();
344
345     label.setToolTipText("<html><body>Search Everywhere<br/>Press <b>"
346                                  + shortcutText
347                                  + "</b> to access<br/> - Classes<br/> - Files<br/> - Tool Windows<br/> - Actions<br/> - Settings</body></html>");
348
349   }
350
351   @Nullable
352   @Override
353   public Object getData(@NonNls String dataId) {
354     return null;
355   }
356
357   private static String getShortcut() {
358     String shortcutText;
359     final Shortcut[] shortcuts = KeymapManager.getInstance().getActiveKeymap().getShortcuts(IdeActions.ACTION_SEARCH_EVERYWHERE);
360     if (shortcuts.length == 0) {
361       shortcutText = "Double " + (SystemInfo.isMac ? MacKeymapUtil.SHIFT : "Shift");
362     } else {
363       shortcutText = KeymapUtil.getShortcutsText(shortcuts);
364     }
365     return shortcutText;
366   }
367
368   private void initSearchField(final MySearchTextField search) {
369     final JTextField editor = search.getTextEditor();
370 //    onFocusLost();
371     editor.getDocument().addDocumentListener(new DocumentAdapter() {
372       @Override
373       protected void textChanged(DocumentEvent e) {
374         final String pattern = editor.getText();
375         if (editor.hasFocus()) {
376           rebuildList(pattern);
377         }
378       }
379     });
380     editor.addFocusListener(new FocusAdapter() {
381       @Override
382       public void focusGained(FocusEvent e) {
383         if (mySkipFocusGain) {
384           mySkipFocusGain = false;
385           return;
386         }
387         search.setText("");
388         search.getTextEditor().setForeground(UIUtil.getLabelForeground());
389         //titleIndex = new TitleIndexes();
390         editor.setColumns(SEARCH_FIELD_COLUMNS);
391         myFocusComponent = e.getOppositeComponent();
392         //noinspection SSBasedInspection
393         SwingUtilities.invokeLater(new Runnable() {
394           @Override
395           public void run() {
396             final JComponent parent = (JComponent)editor.getParent();
397             parent.revalidate();
398             parent.repaint();
399           }
400         });
401         //if (myPopup != null && myPopup.isVisible()) {
402         //  myPopup.cancel();
403         //  myPopup = null;
404         //}
405         rebuildList("");
406       }
407
408       @Override
409       public void focusLost(FocusEvent e) {
410         if ( myPopup instanceof AbstractPopup && myPopup.isVisible()
411              && ((myList == e.getOppositeComponent()) || ((AbstractPopup)myPopup).getPopupWindow() == e.getOppositeComponent())) {
412           return;
413         }
414         if (myNonProjectCheckBox == e.getOppositeComponent()) {
415           mySkipFocusGain = true;
416           editor.requestFocus();
417           return;
418         }
419         onFocusLost();
420       }
421     });
422   }
423
424   private void jumpNextGroup(boolean forward) {
425     final int index = myList.getSelectedIndex();
426     final SearchListModel model = getModel();
427     if (index >= 0) {
428       final int newIndex = forward ? model.next(index) : model.prev(index);
429       myList.setSelectedIndex(newIndex);
430       int more = model.next(newIndex) - 1;
431       if (more < newIndex) {
432         more = myList.getItemsCount() - 1;
433       }
434       ListScrollingUtil.ensureIndexIsVisible(myList, more, forward ? 1 : -1);
435       ListScrollingUtil.ensureIndexIsVisible(myList, newIndex, forward ? 1 : -1);
436     }
437   }
438
439   private SearchListModel getModel() {
440     return (SearchListModel)myList.getModel();
441   }
442
443   private ActionCallback onFocusLost() {
444     final ActionCallback result = new ActionCallback();
445     //noinspection SSBasedInspection
446     SwingUtilities.invokeLater(new Runnable() {
447       @Override
448       public void run() {
449         try {
450           if (myCalcThread != null) {
451             myCalcThread.cancel();
452             //myCalcThread = null;
453           }
454           myAlarm.cancelAllRequests();
455           if (myBalloon != null && !myBalloon.isDisposed() && myPopup != null && !myPopup.isDisposed()) {
456             myBalloon.cancel();
457             myPopup.cancel();
458           }
459
460           //noinspection SSBasedInspection
461           SwingUtilities.invokeLater(new Runnable() {
462             @Override
463             public void run() {
464               ActionToolbarImpl.updateAllToolbarsImmediately();
465             }
466           });
467         }
468         finally {
469           result.setDone();
470         }
471       }
472     });
473     return result;
474   }
475
476   private SearchTextField getField() {
477     return myPopupField;
478   }
479
480   private void doNavigate(final int index) {
481     final Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(getField().getTextEditor()));
482     final Executor executor = ourShiftIsPressed.get()
483                               ? DefaultRunExecutor.getRunExecutorInstance()
484                               : ExecutorRegistry.getInstance().getExecutorById(ToolWindowId.DEBUG);
485     assert project != null;
486     final SearchListModel model = getModel();
487     if (isMoreItem(index)) {
488       final String pattern = myPopupField.getText();
489       WidgetID wid = null;
490       if (index == model.moreIndex.classes) wid = WidgetID.CLASSES;
491       else if (index == model.moreIndex.files) wid = WidgetID.FILES;
492       else if (index == model.moreIndex.settings) wid = WidgetID.SETTINGS;
493       else if (index == model.moreIndex.actions) wid = WidgetID.ACTIONS;
494       else if (index == model.moreIndex.symbols) wid = WidgetID.SYMBOLS;
495       else if (index == model.moreIndex.runConfigurations) wid = WidgetID.RUN_CONFIGURATIONS;
496       if (wid != null) {
497         final WidgetID widgetID = wid;
498         myCurrentWorker.doWhenProcessed(new Runnable() {
499           @Override
500           public void run() {
501             myCalcThread = new CalcThread(project, pattern, true);
502             myPopupActualWidth = 0;
503             myCurrentWorker = myCalcThread.insert(index, widgetID);
504           }
505         });
506
507         return;
508       }
509     }
510     final String pattern = getField().getText();
511     final Object value = myList.getSelectedValue();
512     saveHistory(project, pattern, value);
513     IdeFocusManager focusManager = IdeFocusManager.findInstanceByComponent(getField().getTextEditor());
514     if (myPopup != null && myPopup.isVisible()) {
515       myPopup.cancel();
516     }
517
518     if (value instanceof BooleanOptionDescription) {
519       final BooleanOptionDescription option = (BooleanOptionDescription)value;
520       option.setOptionState(!option.isOptionEnabled());
521       myList.revalidate();
522       myList.repaint();
523       getField().requestFocus();
524       return;
525     }
526
527     if (value instanceof OptionsTopHitProvider) {
528       //noinspection SSBasedInspection
529       SwingUtilities.invokeLater(new Runnable() {
530         @Override
531         public void run() {
532           getField().setText("#" + ((OptionsTopHitProvider)value).getId() + " ");
533         }
534       });
535       return;
536     }
537     Runnable onDone = null;
538
539     AccessToken token = ApplicationManager.getApplication().acquireReadActionLock();
540     try {
541       if (value instanceof PsiElement) {
542         onDone = new Runnable() {
543           public void run() {
544             NavigationUtil.activateFileWithPsiElement((PsiElement)value, true);
545           }
546         };
547         return;
548       }
549       else if (isVirtualFile(value)) {
550         onDone = new Runnable() {
551           public void run() {
552             OpenSourceUtil.navigate(true, new OpenFileDescriptor(project, (VirtualFile)value));
553           }
554         };
555         return;
556       }
557       else if (isActionValue(value) || isSetting(value) || isRunConfiguration(value)) {
558         focusManager.requestDefaultFocus(true);
559         final Component comp = myContextComponent;
560         final AnActionEvent event = myActionEvent;
561         IdeFocusManager.getInstance(project).doWhenFocusSettlesDown(new Runnable() {
562           @Override
563           public void run() {
564             Component c = comp;
565             if (c == null) {
566               c = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
567             }
568
569             if (isRunConfiguration(value)) {
570               ((ChooseRunConfigurationPopup.ItemWrapper)value).perform(project, executor, DataManager.getInstance().getDataContext(c));
571             } else {
572               GotoActionAction.openOptionOrPerformAction(value, pattern, project, c, event);
573               if (isToolWindowAction(value)) return;
574             }
575           }
576         });
577         return;
578       }
579       else if (value instanceof Navigatable) {
580         onDone = new Runnable() {
581           @Override
582           public void run() {
583             OpenSourceUtil.navigate(true, (Navigatable)value);
584           }
585         };
586         return;
587       }
588     }
589     finally {
590       token.finish();
591       final ActionCallback callback = onFocusLost();
592       if (onDone != null) {
593         callback.doWhenDone(onDone);
594       }
595     }
596     focusManager.requestDefaultFocus(true);
597   }
598
599   private boolean isMoreItem(int index) {
600     final SearchListModel model = getModel();
601     return index == model.moreIndex.classes ||
602            index == model.moreIndex.files ||
603            index == model.moreIndex.settings ||
604            index == model.moreIndex.actions ||
605            index == model.moreIndex.symbols ||
606            index == model.moreIndex.runConfigurations;
607   }
608
609   private void rebuildList(final String pattern) {
610     assert EventQueue.isDispatchThread() : "Must be EDT";
611     if (myCalcThread != null && !myCurrentWorker.isProcessed()) {
612       myCurrentWorker = myCalcThread.cancel();
613     }
614     if (myCalcThread != null && !myCalcThread.isCanceled()) {
615       myCalcThread.cancel();
616     }
617     final Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext(getField().getTextEditor()));
618
619     assert project != null;
620     myRenderer.myProject = project;
621     final Runnable run = new Runnable() {
622       @Override
623       public void run() {
624         myCalcThread = new CalcThread(project, pattern, false);
625         myPopupActualWidth = 0;
626         myCurrentWorker = myCalcThread.start();
627       }
628     };
629     if (myCurrentWorker.isDone()) {
630       myCurrentWorker.doWhenDone(run);
631     } else {
632       myCurrentWorker.doWhenRejected(run);
633     }
634   }
635
636   @Override
637   public void actionPerformed(AnActionEvent e) {
638     actionPerformed(e, null);
639   }
640
641   public void actionPerformed(AnActionEvent e, MouseEvent me) {
642     if (myBalloon != null && myBalloon.isVisible()) {
643       showAll.set(!showAll.get());
644       myNonProjectCheckBox.setSelected(showAll.get());
645 //      myPopupField.getTextEditor().setBackground(showAll.get() ? new JBColor(new Color(0xffffe4), new Color(0x494539)) : UIUtil.getTextFieldBackground());
646       rebuildList(myPopupField.getText());
647       return;
648     }
649     myCurrentWorker = ActionCallback.DONE;
650     if (e != null) {
651       myEditor = e.getData(CommonDataKeys.EDITOR);
652       myFileEditor = e.getData(PlatformDataKeys.FILE_EDITOR);
653       myFile = e.getData(CommonDataKeys.PSI_FILE);
654     }
655     if (e == null && myFocusOwner != null) {
656       e = new AnActionEvent(me, DataManager.getInstance().getDataContext(myFocusOwner), ActionPlaces.UNKNOWN, getTemplatePresentation(), ActionManager.getInstance(), 0);
657     }
658     if (e == null) return;
659     final Project project = e.getProject();
660     if (project == null) return;
661
662     updateComponents();
663     myContextComponent = PlatformDataKeys.CONTEXT_COMPONENT.getData(e.getDataContext());
664     Window wnd = myContextComponent != null ? SwingUtilities.windowForComponent(myContextComponent)
665       : KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusedWindow();
666     if (wnd == null && myContextComponent instanceof Window) {
667       wnd = (Window)myContextComponent;
668     }
669     if (wnd == null || wnd.getParent() != null) return;
670     myActionEvent = e;
671     if (myPopupField != null) {
672       Disposer.dispose(myPopupField);
673     }
674     myPopupField = new MySearchTextField();
675     myPopupField.getTextEditor().addKeyListener(new KeyAdapter() {
676       @Override
677       public void keyTyped(KeyEvent e) {
678         myHistoryIndex = 0;
679         myHistoryItem = null;
680       }
681
682       @Override
683       public void keyPressed(KeyEvent e) {
684         if (e.getKeyCode() == KeyEvent.VK_SHIFT) {
685           myList.repaint();
686         }
687       }
688
689       @Override
690       public void keyReleased(KeyEvent e) {
691         if (e.getKeyCode() == KeyEvent.VK_SHIFT) {
692           myList.repaint();
693         }
694       }
695     });
696     initSearchField(myPopupField);
697     myPopupField.setOpaque(false);
698     final JTextField editor = myPopupField.getTextEditor();
699     editor.setColumns(SEARCH_FIELD_COLUMNS);
700     final JPanel panel = new JPanel(new BorderLayout()) {
701       @Override
702       protected void paintComponent(Graphics g) {
703         final Gradient gradient = getGradientColors();
704         ((Graphics2D)g).setPaint(new GradientPaint(0, 0, gradient.getStartColor(), 0, getHeight(), gradient.getEndColor()));
705         g.fillRect(0, 0, getWidth(), getHeight());
706       }
707     };
708     final JLabel title = new JLabel(" Search Everywhere:       ");
709     final JPanel topPanel = new NonOpaquePanel(new BorderLayout());
710     title.setForeground(new JBColor(Gray._240, Gray._200));
711     if (SystemInfo.isMac) {
712       title.setFont(title.getFont().deriveFont(Font.BOLD, title.getFont().getSize() - 1f));
713     } else {
714       title.setFont(title.getFont().deriveFont(Font.BOLD));
715     }
716     topPanel.add(title, BorderLayout.WEST);
717     final JPanel controls = new JPanel(new BorderLayout());
718     controls.setOpaque(false);
719     final JLabel settings = new JLabel(AllIcons.General.SearchEverywhereGear);
720     new ClickListener(){
721       @Override
722       public boolean onClick(@NotNull MouseEvent event, int clickCount) {
723         showSettings();
724         return true;
725       }
726     }.installOn(settings);
727     controls.add(settings, BorderLayout.EAST);
728     myNonProjectCheckBox.setForeground(new JBColor(Gray._240, Gray._200));
729     myNonProjectCheckBox.setText("Include non-project items (" + getShortcut() + ")  ");
730     if (!NonProjectScopeDisablerEP.isSearchInNonProjectDisabled()) {
731       controls.add(myNonProjectCheckBox, BorderLayout.WEST);
732     }
733     topPanel.add(controls, BorderLayout.EAST);
734     panel.add(myPopupField, BorderLayout.CENTER);
735     panel.add(topPanel, BorderLayout.NORTH);
736     panel.setBorder(IdeBorderFactory.createEmptyBorder(3, 5, 4, 5));
737     DataManager.registerDataProvider(panel, this);
738     final ComponentPopupBuilder builder = JBPopupFactory.getInstance().createComponentPopupBuilder(panel, editor);
739     myBalloon = builder
740       .setCancelOnClickOutside(true)
741       .setModalContext(false)
742       .setRequestFocus(true)
743       .setCancelCallback(new Computable<Boolean>() {
744         @Override
745         public Boolean compute() {
746           return !mySkipFocusGain;
747         }
748       })
749       .createPopup();
750     myBalloon.getContent().setBorder(new EmptyBorder(0,0,0,0));
751     final Window window = WindowManager.getInstance().suggestParentWindow(project);
752
753     project.getMessageBus().connect(myBalloon).subscribe(DumbService.DUMB_MODE, new DumbService.DumbModeListener() {
754       @Override
755       public void enteredDumbMode() {
756       }
757
758       @Override
759       public void exitDumbMode() {
760         rebuildList(myPopupField.getText());
761       }
762     });
763
764     Component parent = UIUtil.findUltimateParent(window);
765     registerDataProvider(panel, project);
766     final RelativePoint showPoint;
767     if (me != null) {
768       final Component label = me.getComponent();
769       final Component button = label.getParent();
770       assert button != null;
771       showPoint = new RelativePoint(button, new Point(button.getWidth() - panel.getPreferredSize().width, button.getHeight()));
772     } else {
773       if (parent != null) {
774         int height = UISettings.getInstance().SHOW_MAIN_TOOLBAR ? 135 : 115;
775         if (parent instanceof IdeFrameImpl && ((IdeFrameImpl)parent).isInFullScreen()) {
776           height -= 20;
777         }
778         showPoint = new RelativePoint(parent, new Point((parent.getSize().width - panel.getPreferredSize().width)/ 2, height));
779       } else {
780         showPoint = JBPopupFactory.getInstance().guessBestPopupLocation(e.getDataContext());
781       }
782     }
783     myList.setFont(UIUtil.getListFont());
784     myBalloon.show(showPoint);
785     initSearchActions(myBalloon, myPopupField);
786     IdeFocusManager focusManager = IdeFocusManager.getInstance(project);
787     focusManager.requestFocus(editor, true);
788     FeatureUsageTracker.getInstance().triggerFeatureUsed(IdeActions.ACTION_SEARCH_EVERYWHERE);
789   }
790
791   private void showSettings() {
792     myPopupField.setText("");
793     final SearchListModel model = new SearchListModel();
794     //model.addElement(new SEOption("Show current file structure elements", "search.everywhere.structure"));
795     model.addElement(new SEOption("Show files", "search.everywhere.files"));
796     model.addElement(new SEOption("Show symbols", "search.everywhere.symbols"));
797     model.addElement(new SEOption("Show tool windows", "search.everywhere.toolwindows"));
798     model.addElement(new SEOption("Show run configurations", "search.everywhere.configurations"));
799     model.addElement(new SEOption("Show actions", "search.everywhere.actions"));
800     model.addElement(new SEOption("Show IDE settings", "search.everywhere.settings"));
801
802     if (myCalcThread != null && !myCurrentWorker.isProcessed()) {
803       myCurrentWorker = myCalcThread.cancel();
804     }
805     if (myCalcThread != null && !myCalcThread.isCanceled()) {
806       myCalcThread.cancel();
807     }
808     myCurrentWorker.doWhenProcessed(new Runnable() {
809       @Override
810       public void run() {
811         myList.setModel(model);
812         updatePopupBounds();
813       }
814     });
815   }
816
817   static class SEOption extends BooleanOptionDescription {
818     private final String myKey;
819
820     public SEOption(String option, String registryKey) {
821       super(option, null);
822       myKey = registryKey;
823     }
824
825     @Override
826     public boolean isOptionEnabled() {
827       return Registry.is(myKey);
828     }
829
830     @Override
831     public void setOptionState(boolean enabled) {
832       Registry.get(myKey).setValue(enabled);
833     }
834   }
835
836   private static void saveHistory(Project project, String text, Object value) {
837     if (project == null || project.isDisposed() || !project.isInitialized()) {
838       return;
839     }
840     HistoryType type = null;
841     String fqn = null;
842     if (isActionValue(value)) {
843       type = HistoryType.ACTION;
844       AnAction action = (AnAction)(value instanceof GotoActionModel.ActionWrapper ? ((GotoActionModel.ActionWrapper)value).getAction() : value);
845       fqn = ActionManager.getInstance().getId(action);
846     } else if (value instanceof VirtualFile) {
847       type = HistoryType.FILE;
848       fqn = ((VirtualFile)value).getUrl();
849     } else if (value instanceof ChooseRunConfigurationPopup.ItemWrapper) {
850       type = HistoryType.RUN_CONFIGURATION;
851       fqn = ((ChooseRunConfigurationPopup.ItemWrapper)value).getText();
852     } else if (value instanceof PsiElement) {
853       final PsiElement psiElement = (PsiElement)value;
854       final Language language = psiElement.getLanguage();
855       final String name = LanguagePsiElementExternalizer.INSTANCE.forLanguage(language).getQualifiedName(psiElement);
856       if (name != null) {
857         type = HistoryType.PSI;
858         fqn = language.getID() + "://" + name;
859       }
860     }
861
862     final PropertiesComponent storage = PropertiesComponent.getInstance(project);
863     final String[] values = storage.getValues(SE_HISTORY_KEY);
864     List<HistoryItem> history = new ArrayList<HistoryItem>();
865     if (values != null) {
866       for (String s : values) {
867         final String[] split = s.split("\t");
868         if (split.length != 3 || text.equals(split[0])) {
869           continue;
870         }
871         if (!StringUtil.isEmpty(split[0])) {
872           history.add(new HistoryItem(split[0], split[1], split[2]));
873         }
874       }
875     }
876     history.add(0, new HistoryItem(text, type == null ? null : type.name(), fqn));
877
878     if (history.size() > MAX_SEARCH_EVERYWHERE_HISTORY) {
879       history = history.subList(0, MAX_SEARCH_EVERYWHERE_HISTORY);
880     }
881     final String[] newValues = new String[history.size()];
882     for (int i = 0; i < newValues.length; i++) {
883       newValues[i] = history.get(i).toString();
884     }
885     storage.setValues(SE_HISTORY_KEY, newValues);
886   }
887
888   public Executor getExecutor() {
889     return ourShiftIsPressed.get() ? DefaultRunExecutor.getRunExecutorInstance()
890                                    : ExecutorRegistry.getInstance().getExecutorById(ToolWindowId.DEBUG);
891   }
892
893   private void registerDataProvider(JPanel panel, final Project project) {
894     DataManager.registerDataProvider(panel, new DataProvider() {
895       @Nullable
896       @Override
897       public Object getData(@NonNls String dataId) {
898         final Object value = myList.getSelectedValue();
899         if (CommonDataKeys.PSI_ELEMENT.is(dataId) && value instanceof PsiElement) {
900           return value;
901         } else if (CommonDataKeys.VIRTUAL_FILE.is(dataId) && value instanceof VirtualFile) {
902           return value;
903         } else if (CommonDataKeys.NAVIGATABLE.is(dataId)) {
904               if (value instanceof Navigatable) return value;
905               if (value instanceof ChooseRunConfigurationPopup.ItemWrapper) {
906                 final Object config = ((ChooseRunConfigurationPopup.ItemWrapper)value).getValue();
907                 if (config instanceof RunnerAndConfigurationSettings) {
908                   return new Navigatable() {
909                     @Override
910                     public void navigate(boolean requestFocus) {
911                       RunDialog.editConfiguration(project, (RunnerAndConfigurationSettings)config, "Edit Configuration", getExecutor());
912                     }
913
914                     @Override
915                     public boolean canNavigate() {
916                       return true;
917                     }
918
919                     @Override
920                     public boolean canNavigateToSource() {
921                       return true;
922                     }
923                   };
924                 }
925               }
926         }
927         return null;
928       }
929     });
930   }
931
932   private void initSearchActions(JBPopup balloon, MySearchTextField searchTextField) {
933     final JTextField editor = searchTextField.getTextEditor();
934     new DumbAwareAction(){
935       @Override
936       public void actionPerformed(AnActionEvent e) {
937         jumpNextGroup(true);
938       }
939     }.registerCustomShortcutSet(CustomShortcutSet.fromString("TAB"), editor, balloon);
940     new DumbAwareAction(){
941       @Override
942       public void actionPerformed(AnActionEvent e) {
943         jumpNextGroup(false);
944       }
945     }.registerCustomShortcutSet(CustomShortcutSet.fromString("shift TAB"), editor, balloon);
946     new DumbAwareAction(){
947       @Override
948       public void actionPerformed(AnActionEvent e) {
949         if (myBalloon != null && myBalloon.isVisible()) {
950           myBalloon.cancel();
951         }
952         if (myPopup != null && myPopup.isVisible()) {
953           myPopup.cancel();
954         }
955       }
956     }.registerCustomShortcutSet(CustomShortcutSet.fromString("ESCAPE"), editor, balloon);
957     new DumbAwareAction(){
958       @Override
959       public void actionPerformed(AnActionEvent e) {
960         final int index = myList.getSelectedIndex();
961         if (index != -1) {
962           doNavigate(index);
963         }
964       }
965     }.registerCustomShortcutSet(CustomShortcutSet.fromString("ENTER", "shift ENTER"), editor, balloon);
966     new DumbAwareAction(){
967       @Override
968       public void actionPerformed(AnActionEvent e) {
969         final PropertiesComponent storage = PropertiesComponent.getInstance(e.getProject());
970         final String[] values = storage.getValues(SE_HISTORY_KEY);
971         if (values != null) {
972           if (values.length > myHistoryIndex) {
973             final List<String> data = StringUtil.split(values[myHistoryIndex], "\t");
974             myHistoryItem = new HistoryItem(data.get(0), data.get(1), data.get(2));
975             myHistoryIndex++;
976             editor.setText(myHistoryItem.pattern);
977             editor.setCaretPosition(myHistoryItem.pattern.length());
978             editor.moveCaretPosition(0);
979           }
980         }
981       }
982
983       @Override
984       public void update(AnActionEvent e) {
985         e.getPresentation().setEnabled(editor.getCaretPosition() == 0);
986       }
987     }.registerCustomShortcutSet(CustomShortcutSet.fromString("LEFT"), editor, balloon);
988   }
989
990   private static class MySearchTextField extends SearchTextField implements DataProvider, Disposable {
991     public MySearchTextField() {
992       super(false);
993       getTextEditor().setOpaque(false);
994       getTextEditor().setUI((DarculaTextFieldUI)DarculaTextFieldUI.createUI(getTextEditor()));
995       getTextEditor().setBorder(new DarculaTextBorder());
996
997       getTextEditor().putClientProperty("JTextField.Search.noBorderRing", Boolean.TRUE);
998       if (UIUtil.isUnderDarcula()) {
999         getTextEditor().setBackground(Gray._45);
1000         getTextEditor().setForeground(Gray._240);
1001       }
1002     }
1003
1004     @Override
1005     protected boolean isSearchControlUISupported() {
1006       return true;
1007     }
1008
1009     @Override
1010     protected boolean hasIconsOutsideOfTextField() {
1011       return false;
1012     }
1013
1014     @Override
1015     protected void showPopup() {
1016     }
1017
1018     @Nullable
1019     @Override
1020     public Object getData(@NonNls String dataId) {
1021       if (PlatformDataKeys.PREDEFINED_TEXT.is(dataId)) {
1022         return getTextEditor().getText();
1023       }
1024       return null;
1025     }
1026
1027     @Override
1028     public void dispose() {
1029     }
1030   }
1031
1032   private class MyListRenderer extends ColoredListCellRenderer {
1033     ColoredListCellRenderer myLocation = new ColoredListCellRenderer() {
1034       @Override
1035       protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
1036         setPaintFocusBorder(false);
1037         append(myLocationString, SimpleTextAttributes.GRAYED_ATTRIBUTES);
1038         setIcon(myLocationIcon);
1039       }
1040     };
1041     SearchEverywherePsiRenderer myFileRenderer = new SearchEverywherePsiRenderer(myList);
1042
1043     private String myLocationString;
1044     private DefaultPsiElementCellRenderer myPsiRenderer = new DefaultPsiElementCellRenderer() {
1045       {setFocusBorderEnabled(false);}
1046     };
1047     private Icon myLocationIcon;
1048     private Project myProject;
1049     private JPanel myMainPanel = new JPanel(new BorderLayout());
1050     private JLabel myTitle = new JLabel();
1051
1052     @Override
1053     public void clear() {
1054       super.clear();
1055       myLocation.clear();
1056       myLocationString = null;
1057       myLocationIcon = null;
1058     }
1059
1060     public void setLocationString(String locationString) {
1061       myLocationString = locationString;
1062     }
1063
1064     @Override
1065     public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
1066       Component cmp;
1067       PsiElement file;
1068       myLocationString = null;
1069       String pattern = "*" + myPopupField.getText();
1070       Matcher matcher = NameUtil.buildMatcher(pattern, 0, true, true, pattern.toLowerCase().equals(pattern));
1071       if (isMoreItem(index)) {
1072         cmp = More.get(isSelected);
1073       } else if (value instanceof VirtualFile
1074                  && myProject != null
1075                  && ((((VirtualFile)value).isDirectory() && (file = PsiManager.getInstance(myProject).findDirectory((VirtualFile)value)) != null )
1076                      || (file = PsiManager.getInstance(myProject).findFile((VirtualFile)value)) != null)) {
1077         myFileRenderer.setPatternMatcher(matcher);
1078         cmp = myFileRenderer.getListCellRendererComponent(list, file, index, isSelected, cellHasFocus);
1079       } else if (value instanceof PsiElement) {
1080         myFileRenderer.setPatternMatcher(matcher);
1081         cmp = myFileRenderer.getListCellRendererComponent(list, value, index, isSelected, isSelected);
1082       } else {
1083         cmp = super.getListCellRendererComponent(list, value, index, isSelected, isSelected);
1084         final JPanel p = new JPanel(new BorderLayout());
1085         p.setBackground(UIUtil.getListBackground(isSelected));
1086         p.add(cmp, BorderLayout.CENTER);
1087         cmp = p;
1088       }
1089       if (myLocationString != null || value instanceof BooleanOptionDescription) {
1090         final JPanel panel = new JPanel(new BorderLayout());
1091         panel.setBackground(UIUtil.getListBackground(isSelected));
1092         panel.add(cmp, BorderLayout.CENTER);
1093         final Component rightComponent;
1094         if (value instanceof BooleanOptionDescription) {
1095           final OnOffButton button = new OnOffButton();
1096           button.setSelected(((BooleanOptionDescription)value).isOptionEnabled());
1097           rightComponent = button;
1098         }
1099         else {
1100           rightComponent = myLocation.getListCellRendererComponent(list, value, index, isSelected, isSelected);
1101         }
1102         panel.add(rightComponent, BorderLayout.EAST);
1103         cmp = panel;
1104       }
1105
1106       Color bg = cmp.getBackground();
1107       if (bg == null) {
1108         cmp.setBackground(UIUtil.getListBackground(isSelected));
1109         bg = cmp.getBackground();
1110       }
1111       myMainPanel.setBorder(new CustomLineBorder(bg, 0, 0, 2, 0));
1112       String title = getModel().titleIndex.getTitle(index);
1113       myMainPanel.removeAll();
1114       if (title != null) {
1115         myTitle.setText(title);
1116         myMainPanel.add(createTitle(" " + title), BorderLayout.NORTH);
1117       }
1118       myMainPanel.add(cmp, BorderLayout.CENTER);
1119       final int width = myMainPanel.getPreferredSize().width;
1120       if (width > myPopupActualWidth) {
1121         myPopupActualWidth = width;
1122         //schedulePopupUpdate();
1123       }
1124       return myMainPanel;
1125     }
1126
1127     @Override
1128     protected void customizeCellRenderer(JList list, Object value, int index, boolean selected, boolean hasFocus) {
1129       setPaintFocusBorder(false);
1130       setIcon(EmptyIcon.ICON_16);
1131       AccessToken token = ApplicationManager.getApplication().acquireReadActionLock();
1132       try {
1133         if (value instanceof PsiElement) {
1134           String name = myClassModel.getElementName(value);
1135           assert name != null;
1136           append(name);
1137         } else if (value instanceof ChooseRunConfigurationPopup.ItemWrapper) {
1138           final ChooseRunConfigurationPopup.ItemWrapper wrapper = (ChooseRunConfigurationPopup.ItemWrapper)value;
1139           append(wrapper.getText());
1140           setIcon(wrapper.getIcon());
1141           setLocationString(ourShiftIsPressed.get() ? "Run" : "Debug");
1142           myLocationIcon = ourShiftIsPressed.get() ? AllIcons.Toolwindows.ToolWindowRun : AllIcons.Toolwindows.ToolWindowDebugger;
1143         } else if (isVirtualFile(value)) {
1144           final VirtualFile file = (VirtualFile)value;
1145           if (file instanceof VirtualFilePathWrapper) {
1146             append(((VirtualFilePathWrapper)file).getPresentablePath());
1147           } else {
1148             append(file.getName());
1149           }
1150           setIcon(IconUtil.getIcon(file, Iconable.ICON_FLAG_READ_STATUS, myProject));
1151         }
1152         else if (isActionValue(value)) {
1153           final GotoActionModel.ActionWrapper actionWithParentGroup = value instanceof GotoActionModel.ActionWrapper ? (GotoActionModel.ActionWrapper)value : null;
1154           final AnAction anAction = actionWithParentGroup == null ? (AnAction)value : actionWithParentGroup.getAction();
1155           final Presentation templatePresentation = anAction.getTemplatePresentation();
1156           Icon icon = templatePresentation.getIcon();
1157           if (anAction instanceof ActivateToolWindowAction) {
1158             final String id = ((ActivateToolWindowAction)anAction).getToolWindowId();
1159             ToolWindow toolWindow = ToolWindowManager.getInstance(myProject).getToolWindow(id);
1160             if (toolWindow != null) {
1161               icon = toolWindow.getIcon();
1162             }
1163           }
1164
1165           append(templatePresentation.getText());
1166           if (actionWithParentGroup != null) {
1167             final String groupName = actionWithParentGroup.getGroupName();
1168             if (!StringUtil.isEmpty(groupName)) {
1169               setLocationString(groupName);
1170             }
1171           }
1172
1173           final String groupName = actionWithParentGroup == null ? null : actionWithParentGroup.getGroupName();
1174           if (!StringUtil.isEmpty(groupName)) {
1175             setLocationString(groupName);
1176           }
1177           if (icon != null && icon.getIconWidth() <= 16 && icon.getIconHeight() <= 16) {
1178             setIcon(IconUtil.toSize(icon, 16, 16));
1179           }
1180         }
1181         else if (isSetting(value)) {
1182           String text = getSettingText((OptionDescription)value);
1183           append(text);
1184           final String id = ((OptionDescription)value).getConfigurableId();
1185           final String name = myConfigurables.get(id);
1186           if (name != null) {
1187             setLocationString(name);
1188           }
1189         }
1190         else if (value instanceof OptionsTopHitProvider) {
1191           append("#" + ((OptionsTopHitProvider)value).getId());
1192         }
1193         else {
1194           ItemPresentation presentation = null;
1195           if (value instanceof ItemPresentation) {
1196             presentation = (ItemPresentation)value;
1197           }
1198           else if (value instanceof NavigationItem) {
1199             presentation = ((NavigationItem)value).getPresentation();
1200           }
1201           if (presentation != null) {
1202             final String text = presentation.getPresentableText();
1203             append(text == null ? value.toString() : text);
1204             final String location = presentation.getLocationString();
1205             if (!StringUtil.isEmpty(location)) {
1206               setLocationString(location);
1207             }
1208             Icon icon = presentation.getIcon(false);
1209             if (icon != null) setIcon(icon);
1210           }
1211         }
1212       }
1213       finally {
1214         token.finish();
1215       }
1216     }
1217
1218     public void recalculateWidth() {
1219       ListModel model = myList.getModel();
1220       myTitle.setIcon(EmptyIcon.ICON_16);
1221       myTitle.setFont(getTitleFont());
1222       int index = 0;
1223       while (index < model.getSize()) {
1224         String title = getModel().titleIndex.getTitle(index);
1225         if (title != null) {
1226           myTitle.setText(title);
1227         }
1228         index++;
1229       }
1230
1231       myTitle.setForeground(Gray._122);
1232       myTitle.setAlignmentY(BOTTOM_ALIGNMENT);
1233     }
1234   }
1235
1236   private static String getSettingText(OptionDescription value) {
1237     String hit = value.getHit();
1238     if (hit == null) {
1239       hit = value.getOption();
1240     }
1241     hit = StringUtil.unescapeXml(hit);
1242     if (hit.length() > 60) {
1243       hit = hit.substring(0, 60) + "...";
1244     }
1245     hit = hit.replace("  ", " "); //avoid extra spaces from mnemonics and xml conversion
1246     String text = hit.trim();
1247     if (text.endsWith(":")) {
1248       text = text.substring(0, text.length() - 1);
1249     }
1250     return text;
1251   }
1252
1253   private static boolean isActionValue(Object o) {
1254     return o instanceof GotoActionModel.ActionWrapper || o instanceof AnAction;
1255   }
1256
1257   private static boolean isSetting(Object o) {
1258     return o instanceof OptionDescription;
1259   }
1260
1261   private static boolean isRunConfiguration(Object o) {
1262     return o instanceof ChooseRunConfigurationPopup.ItemWrapper;
1263   }
1264
1265   private static boolean isVirtualFile(Object o) {
1266     return o instanceof VirtualFile;
1267   }
1268
1269   private static Font getTitleFont() {
1270     return UIUtil.getLabelFont().deriveFont(UIUtil.getFontSize(UIUtil.FontSize.SMALL));
1271   }
1272
1273   enum WidgetID {CLASSES, FILES, ACTIONS, SETTINGS, SYMBOLS, RUN_CONFIGURATIONS}
1274
1275   @SuppressWarnings("SSBasedInspection")
1276   private class CalcThread implements Runnable {
1277     private final Project project;
1278     private final String pattern;
1279     private final ProgressIndicator myProgressIndicator = new ProgressIndicatorBase();
1280     private final ActionCallback myDone = new ActionCallback();
1281     private final SearchListModel myListModel;
1282     private final ArrayList<VirtualFile> myAlreadyAddedFiles = new ArrayList<VirtualFile>();
1283     private final ArrayList<AnAction> myAlreadyAddedActions = new ArrayList<AnAction>();
1284
1285
1286     public CalcThread(Project project, String pattern, boolean reuseModel) {
1287       this.project = project;
1288       this.pattern = pattern;
1289       myListModel = reuseModel ? (SearchListModel)myList.getModel() : new SearchListModel();
1290     }
1291
1292     @Override
1293     public void run() {
1294       try {
1295         check();
1296
1297         //noinspection SSBasedInspection
1298         SwingUtilities.invokeLater(new Runnable() {
1299           @Override
1300           public void run() {
1301             // this line must be called on EDT to avoid context switch at clear().append("text") Don't touch. Ask [kb]
1302             myList.getEmptyText().setText("Searching...");
1303
1304             if (myList.getModel() instanceof SearchListModel) {
1305               //noinspection unchecked
1306               myAlarm.cancelAllRequests();
1307               myAlarm.addRequest(new Runnable() {
1308                 @Override
1309                 public void run() {
1310                   if (!myDone.isRejected()) {
1311                     myList.setModel(myListModel);
1312                     updatePopup();
1313                   }
1314                 }
1315               }, 50);
1316             } else {
1317               myList.setModel(myListModel);
1318             }
1319           }
1320         });
1321
1322         if (pattern.trim().length() == 0) {
1323           buildModelFromRecentFiles();
1324           updatePopup();
1325           return;
1326         }
1327
1328         checkModelsUpToDate();              check();
1329         buildTopHit(pattern);               check();
1330
1331         if (!pattern.startsWith("#")) {
1332           buildRecentFiles(pattern);
1333           check();
1334           runReadAction(new Runnable() {
1335                       public void run() {
1336                         buildStructure(pattern);
1337                       }
1338                     }, true);
1339           updatePopup();
1340           check();
1341           buildToolWindows(pattern);
1342           check();
1343           updatePopup();
1344           check();
1345
1346           runReadAction(new Runnable() {
1347             public void run() {
1348               buildRunConfigurations(pattern);
1349             }
1350           }, true);
1351           runReadAction(new Runnable() {
1352             public void run() {
1353               buildClasses(pattern);
1354             }
1355           }, true);
1356           runReadAction(new Runnable() {
1357             public void run() {
1358               buildFiles(pattern);
1359             }
1360           }, false);
1361           runReadAction(new Runnable() {
1362             public void run() {
1363               buildSymbols(pattern);
1364             }
1365           }, true);
1366
1367           buildActionsAndSettings(pattern);
1368
1369           updatePopup();
1370
1371         }
1372         updatePopup();
1373       }
1374       catch (ProcessCanceledException ignore) {
1375         myDone.setRejected();
1376       }
1377       catch (Exception e) {
1378         LOG.error(e);
1379         myDone.setRejected();
1380       }
1381       finally {
1382         if (!isCanceled()) {
1383           //noinspection SSBasedInspection
1384           SwingUtilities.invokeLater(new Runnable() {
1385             @Override
1386             public void run() {
1387               myList.getEmptyText().setText(StatusText.DEFAULT_EMPTY_TEXT);
1388             }
1389           });
1390           updatePopup();
1391         }
1392         if (!myDone.isProcessed()) {
1393           myDone.setDone();
1394         }
1395       }
1396     }
1397
1398     private void runReadAction(Runnable action, boolean checkDumb) {
1399       if (!checkDumb || !DumbService.getInstance(project).isDumb()) {
1400         ApplicationManager.getApplication().runReadAction(action);
1401         updatePopup();
1402       }
1403     }
1404
1405     protected void check() {
1406       myProgressIndicator.checkCanceled();
1407       if (myDone.isRejected()) throw new ProcessCanceledException();
1408       if (myBalloon == null || myBalloon.isDisposed()) throw new ProcessCanceledException();
1409     }
1410
1411     private synchronized void buildToolWindows(String pattern) {
1412       if (!Registry.is("search.everywhere.toolwindows")) {
1413         return;
1414       }
1415       final List<ActivateToolWindowAction> actions = new ArrayList<ActivateToolWindowAction>();
1416       for (ActivateToolWindowAction action : ToolWindowsGroup.getToolWindowActions(project)) {
1417         String text = action.getTemplatePresentation().getText();
1418         if (text != null && StringUtil.startsWithIgnoreCase(text, pattern)) {
1419           actions.add(action);
1420
1421           if (actions.size() == MAX_TOOL_WINDOWS) {
1422             break;
1423           }
1424         }
1425       }
1426
1427       check();
1428
1429       if (actions.isEmpty()) {
1430         return;
1431       }
1432
1433       SwingUtilities.invokeLater(new Runnable() {
1434         @Override
1435         public void run() {
1436           myListModel.titleIndex.toolWindows = myListModel.size();
1437           for (Object toolWindow : actions) {
1438             myListModel.addElement(toolWindow);
1439           }
1440         }
1441       });
1442     }
1443
1444     private SearchResult getActionsOrSettings(final String pattern, final int max, final boolean actions) {
1445       final SearchResult result = new SearchResult();
1446       if ((actions && !Registry.is("search.everywhere.actions")) || (!actions && !Registry.is("search.everywhere.settings"))) {
1447         return result;
1448       }
1449       final MinusculeMatcher matcher = new MinusculeMatcher("*" +pattern, NameUtil.MatchingCaseSensitivity.NONE);
1450       if (myActionProvider == null) {
1451         myActionProvider = createActionProvider();
1452       }
1453
1454       myActionProvider.filterElements(pattern, true, new Processor<GotoActionModel.MatchedValue>() {
1455         @Override
1456         public boolean process(GotoActionModel.MatchedValue matched) {
1457           check();
1458           Object object = matched.value;
1459           if (myListModel.contains(object)) return true;
1460
1461           if (!actions && isSetting(object)) {
1462             if (matcher.matches(getSettingText((OptionDescription)object))) {
1463               result.add(object);
1464             }
1465           } else if (actions && !isToolWindowAction(object) && isActionValue(object)) {
1466             result.add(object);
1467           }
1468           return result.size() <= max;
1469         }
1470       });
1471
1472       return result;
1473     }
1474
1475     private synchronized void buildActionsAndSettings(String pattern) {
1476       final SearchResult actions = getActionsOrSettings(pattern, MAX_ACTIONS, true);
1477       final SearchResult settings = getActionsOrSettings(pattern, MAX_SETTINGS, false);
1478
1479       check();
1480
1481       SwingUtilities.invokeLater(new Runnable() {
1482         @Override
1483         public void run() {
1484           if (isCanceled()) return;
1485           if (actions.size() > 0) {
1486             myListModel.titleIndex.actions = myListModel.size();
1487             for (Object action : actions) {
1488               myListModel.addElement(action);
1489             }
1490           }
1491           myListModel.moreIndex.actions = actions.size() >= MAX_ACTIONS ? myListModel.size() - 1 : -1;
1492           if (settings.size() > 0) {
1493             myListModel.titleIndex.settings = myListModel.size();
1494             for (Object setting : settings) {
1495               myListModel.addElement(setting);
1496             }
1497           }
1498           myListModel.moreIndex.settings = settings.size() >= MAX_SETTINGS ? myListModel.size() - 1 : -1;
1499         }
1500       });
1501     }
1502
1503     private synchronized void buildFiles(final String pattern) {
1504       final SearchResult files = getFiles(pattern, MAX_FILES, myFileChooseByName);
1505
1506       check();
1507
1508       if (files.size() > 0) {
1509         SwingUtilities.invokeLater(new Runnable() {
1510           @Override
1511           public void run() {
1512             if (isCanceled()) return;
1513             myListModel.titleIndex.files = myListModel.size();
1514             for (Object file : files) {
1515               myListModel.addElement(file);
1516             }
1517             myListModel.moreIndex.files = files.needMore ? myListModel.size() - 1 : -1;
1518           }
1519         });
1520       }
1521     }
1522
1523     private synchronized void buildStructure(final String pattern) {
1524       if (!Registry.is("search.everywhere.structure") || myStructureModel == null) return;
1525       final List<StructureViewTreeElement> elements = new ArrayList<StructureViewTreeElement>();
1526       final MinusculeMatcher matcher = new MinusculeMatcher("*" + pattern, NameUtil.MatchingCaseSensitivity.NONE);
1527       fillStructure(myStructureModel.getRoot(), elements, matcher);
1528       if (elements.size() > 0) {
1529         SwingUtilities.invokeLater(new Runnable() {
1530           @Override
1531           public void run() {
1532             if (isCanceled()) return;
1533             myListModel.titleIndex.structure = myListModel.size();
1534             for (Object element : elements) {
1535               myListModel.addElement(element);
1536             }
1537             myListModel.moreIndex.files = -1;
1538           }
1539         });
1540       }
1541     }
1542
1543     private void fillStructure(StructureViewTreeElement element, List<StructureViewTreeElement> elements, Matcher matcher) {
1544       final TreeElement[] children = element.getChildren();
1545       check();
1546       for (TreeElement child : children) {
1547         check();
1548         if (child instanceof StructureViewTreeElement) {
1549           final String text = child.getPresentation().getPresentableText();
1550           if (text != null && matcher.matches(text)) {
1551             elements.add((StructureViewTreeElement)child);
1552           }
1553           fillStructure((StructureViewTreeElement)child, elements, matcher);
1554         }
1555       }
1556     }
1557
1558
1559     private synchronized void buildSymbols(final String pattern) {
1560       final SearchResult symbols = getSymbols(pattern, MAX_SYMBOLS, mySymbolsChooseByName);
1561       check();
1562
1563       if (symbols.size() > 0) {
1564         SwingUtilities.invokeLater(new Runnable() {
1565           @Override
1566           public void run() {
1567             if (isCanceled()) return;
1568             myListModel.titleIndex.symbols = myListModel.size();
1569             for (Object file : symbols) {
1570               myListModel.addElement(file);
1571             }
1572             myListModel.moreIndex.symbols = symbols.needMore ? myListModel.size() - 1 : -1;
1573           }
1574         });
1575       }
1576     }
1577
1578     @Nullable
1579     private ChooseRunConfigurationPopup.ItemWrapper getRunConfigurationByName(String name) {
1580       final ChooseRunConfigurationPopup.ItemWrapper[] wrappers =
1581         ChooseRunConfigurationPopup.createSettingsList(project, new ExecutorProvider() {
1582           @Override
1583           public Executor getExecutor() {
1584             return ExecutorRegistry.getInstance().getExecutorById(ToolWindowId.DEBUG);
1585           }
1586         }, false);
1587
1588       for (ChooseRunConfigurationPopup.ItemWrapper wrapper : wrappers) {
1589         if (wrapper.getText().equals(name)) {
1590           return wrapper;
1591         }
1592       }
1593       return null;
1594     }
1595
1596     private synchronized void buildRunConfigurations(String pattern) {
1597       final SearchResult runConfigurations = getConfigurations(pattern, MAX_RUN_CONFIGURATION);
1598
1599       if (runConfigurations.size() > 0) {
1600         SwingUtilities.invokeLater(new Runnable() {
1601           @Override
1602           public void run() {
1603             if (isCanceled()) return;
1604             myListModel.titleIndex.runConfigurations = myListModel.size();
1605             for (Object runConfiguration : runConfigurations) {
1606               myListModel.addElement(runConfiguration);
1607             }
1608             myListModel.moreIndex.runConfigurations = runConfigurations.needMore ? myListModel.getSize() - 1 : -1;
1609           }
1610         });
1611       }
1612     }
1613
1614     private SearchResult getConfigurations(String pattern, int max) {
1615       SearchResult configurations = new SearchResult();
1616       if (!Registry.is("search.everywhere.configurations")) {
1617         return configurations;
1618       }
1619       MinusculeMatcher matcher = new MinusculeMatcher(pattern, NameUtil.MatchingCaseSensitivity.NONE);
1620       final ChooseRunConfigurationPopup.ItemWrapper[] wrappers =
1621         ChooseRunConfigurationPopup.createSettingsList(project, new ExecutorProvider() {
1622           @Override
1623           public Executor getExecutor() {
1624             return ExecutorRegistry.getInstance().getExecutorById(ToolWindowId.DEBUG);
1625           }
1626         }, false);
1627       check();
1628       for (ChooseRunConfigurationPopup.ItemWrapper wrapper : wrappers) {
1629         if (matcher.matches(wrapper.getText()) && !myListModel.contains(wrapper)) {
1630           if (configurations.size() == max) {
1631             configurations.needMore = true;
1632             break;
1633           }
1634           configurations.add(wrapper);
1635         }
1636         check();
1637       }
1638
1639       return configurations;
1640     }
1641
1642
1643     private synchronized void buildClasses(final String pattern) {
1644       final SearchResult classes = getClasses(pattern, showAll.get(), MAX_CLASSES, myClassChooseByName);
1645       check();
1646
1647       if (classes.size() > 0) {
1648         SwingUtilities.invokeLater(new Runnable() {
1649           @Override
1650           public void run() {
1651             if (isCanceled()) return;
1652             myListModel.titleIndex.classes = myListModel.size();
1653             for (Object file : classes) {
1654               myListModel.addElement(file);
1655             }
1656             myListModel.moreIndex.classes = -1;
1657             if (classes.needMore) {
1658               myListModel.moreIndex.classes = myListModel.size() - 1;
1659             }
1660           }
1661         });
1662       }
1663     }
1664
1665     private SearchResult getSymbols(String pattern, final int max, ChooseByNamePopup chooseByNamePopup) {
1666       final SearchResult symbols = new SearchResult();
1667       if (!Registry.is("search.everywhere.symbols")) {
1668         return symbols;
1669       }
1670       final GlobalSearchScope scope = GlobalSearchScope.projectScope(project);
1671       if (chooseByNamePopup == null) return symbols;
1672       final ChooseByNameItemProvider provider = chooseByNamePopup.getProvider();
1673       provider.filterElements(chooseByNamePopup, pattern, false,
1674                               myProgressIndicator, new Processor<Object>() {
1675           @Override
1676           public boolean process(Object o) {
1677             if (o instanceof PsiElement && !(((PsiElement)o).getParent() instanceof PsiFile)) {
1678               final PsiElement element = (PsiElement)o;
1679               final PsiFile file = element.getContainingFile();
1680               if (!myListModel.contains(o) &&
1681                   //some elements are non-physical like DB columns
1682                   (file == null || (file.getVirtualFile() != null && scope.accept(file.getVirtualFile())))) {
1683                 symbols.add(o);
1684               }
1685             }
1686             symbols.needMore = symbols.size() == max;
1687             return !symbols.needMore;
1688           }
1689         });
1690       return symbols;
1691     }
1692
1693     private SearchResult getClasses(String pattern, boolean includeLibs, final int max, ChooseByNamePopup chooseByNamePopup) {
1694       final SearchResult classes = new SearchResult();
1695       if (chooseByNamePopup == null) {
1696         return classes;
1697       }
1698       chooseByNamePopup.getProvider().filterElements(chooseByNamePopup, pattern, includeLibs,
1699                                                       myProgressIndicator, new Processor<Object>() {
1700           @Override
1701           public boolean process(Object o) {
1702             if (o instanceof PsiElement && !myListModel.contains(o) && !classes.contains(o)) {
1703               if (classes.size() == max) {
1704                 classes.needMore = true;
1705                 return false;
1706               }
1707               classes.add(o);
1708               if (o instanceof PsiNamedElement) {
1709                 final String name = ((PsiNamedElement)o).getName();
1710                 final PsiFile file = ((PsiNamedElement)o).getContainingFile();
1711                 if (file != null) {
1712                   final VirtualFile virtualFile = file.getVirtualFile();
1713                   if (StringUtil.equals(name, virtualFile.getNameWithoutExtension())) {
1714                     myAlreadyAddedFiles.add(virtualFile);
1715                   }
1716                 }
1717               }
1718             }
1719             return true;
1720           }
1721         });
1722       if (!includeLibs && classes.isEmpty()) {
1723         return getClasses(pattern, true, max, chooseByNamePopup);
1724       }
1725       return classes;
1726     }
1727
1728     private SearchResult getFiles(final String pattern, final int max, ChooseByNamePopup chooseByNamePopup) {
1729       final SearchResult files = new SearchResult();
1730       if (chooseByNamePopup == null || !Registry.is("search.everywhere.files")) {
1731         return files;
1732       }
1733       final GlobalSearchScope scope = GlobalSearchScope.projectScope(project);
1734       chooseByNamePopup.getProvider().filterElements(chooseByNamePopup, pattern, true,
1735                                                       myProgressIndicator, new Processor<Object>() {
1736           @Override
1737           public boolean process(Object o) {
1738             VirtualFile file = null;
1739             if (o instanceof VirtualFile) {
1740               file = (VirtualFile)o;
1741             } else if (o instanceof PsiFile) {
1742               file = ((PsiFile)o).getVirtualFile();
1743             } else if (o instanceof PsiDirectory) {
1744               file = ((PsiDirectory)o).getVirtualFile();
1745             }
1746             if (file != null
1747                 && !(pattern.indexOf(' ') != -1 && file.getName().indexOf(' ') == -1)
1748                 && (showAll.get() || scope.accept(file)
1749                 && !myListModel.contains(file)
1750                 && !myAlreadyAddedFiles.contains(file))
1751                 && !files.contains(file)) {
1752               if (files.size() == max) {
1753                 files.needMore = true;
1754                 return false;
1755               }
1756               files.add(file);
1757             }
1758             return true;
1759           }
1760         });
1761       return files;
1762     }
1763
1764     private synchronized void buildRecentFiles(String pattern) {
1765       final MinusculeMatcher matcher = new MinusculeMatcher("*" + pattern, NameUtil.MatchingCaseSensitivity.NONE);
1766       final ArrayList<VirtualFile> files = new ArrayList<VirtualFile>();
1767       final List<VirtualFile> selected = Arrays.asList(FileEditorManager.getInstance(project).getSelectedFiles());
1768       for (VirtualFile file : ArrayUtil.reverseArray(EditorHistoryManager.getInstance(project).getFiles())) {
1769         if (StringUtil.isEmptyOrSpaces(pattern) || matcher.matches(file.getName())) {
1770           if (!files.contains(file) && !selected.contains(file)) {
1771             files.add(file);
1772           }
1773         }
1774         if (files.size() > MAX_RECENT_FILES) break;
1775       }
1776
1777       if (files.size() > 0) {
1778         myAlreadyAddedFiles.addAll(files);
1779
1780         SwingUtilities.invokeLater(new Runnable() {
1781           @Override
1782           public void run() {
1783             if (isCanceled()) return;
1784             myListModel.titleIndex.recentFiles = myListModel.size();
1785             for (Object file : files) {
1786               myListModel.addElement(file);
1787             }
1788           }
1789         });
1790       }
1791     }
1792
1793     private boolean isCanceled() {
1794       return myProgressIndicator.isCanceled() || myDone.isRejected();
1795     }
1796
1797     private synchronized void buildTopHit(String pattern) {
1798       final List<Object> elements = new ArrayList<Object>();
1799       final HistoryItem history = myHistoryItem;
1800       if (history != null) {
1801         final HistoryType type = parseHistoryType(history.type);
1802         if (type != null) {
1803           switch (type){
1804             case PSI:
1805               if (!DumbService.isDumb(project)) {
1806                 ApplicationManager.getApplication().runReadAction(new Runnable() {
1807                   public void run() {
1808
1809                     final int i = history.fqn.indexOf("://");
1810                     if (i != -1) {
1811                       final String langId = history.fqn.substring(0, i);
1812                       final Language language = Language.findLanguageByID(langId);
1813                       final String psiFqn = history.fqn.substring(i + 3);
1814                       if (language != null) {
1815                         final PsiElement psi =
1816                           LanguagePsiElementExternalizer.INSTANCE.forLanguage(language).findByQualifiedName(project, psiFqn);
1817                         if (psi != null) {
1818                           elements.add(psi);
1819                           final PsiFile psiFile = psi.getContainingFile();
1820                           if (psiFile != null) {
1821                             final VirtualFile file = psiFile.getVirtualFile();
1822                             if (file != null) {
1823                               myAlreadyAddedFiles.add(file);
1824                             }
1825                           }
1826                         }
1827                       }
1828                     }
1829                   }
1830                 });
1831               }
1832               break;
1833             case FILE:
1834               final VirtualFile file = VirtualFileManager.getInstance().findFileByUrl(history.fqn);
1835               if (file != null) {
1836                 elements.add(file);
1837               }
1838               break;
1839             case SETTING:
1840               break;
1841             case ACTION:
1842               final AnAction action = ActionManager.getInstance().getAction(history.fqn);
1843               if (action != null) {
1844                 elements.add(action);
1845                 myAlreadyAddedActions.add(action);
1846               }
1847               break;
1848             case RUN_CONFIGURATION:
1849               if (!DumbService.isDumb(project)) {
1850                 ApplicationManager.getApplication().runReadAction(new Runnable() {
1851                   public void run() {
1852                     final ChooseRunConfigurationPopup.ItemWrapper runConfiguration = getRunConfigurationByName(history.fqn);
1853                     if (runConfiguration != null) {
1854                       elements.add(runConfiguration);
1855                     }
1856                   }
1857                 });
1858               }
1859               break;
1860           }
1861         }
1862       }
1863       final Consumer<Object> consumer = new Consumer<Object>() {
1864         @Override
1865         public void consume(Object o) {
1866           if (isSetting(o) || isVirtualFile(o) || isActionValue(o) || o instanceof PsiElement || o instanceof OptionsTopHitProvider) {
1867             if (o instanceof AnAction && myAlreadyAddedActions.contains(o)) {
1868               return;
1869             }
1870             elements.add(o);
1871           }
1872         }
1873       };
1874
1875       if (pattern.equals("#")) {
1876         final HashSet<String> ids = new HashSet<String>();
1877         for (SearchTopHitProvider provider : SearchTopHitProvider.EP_NAME.getExtensions()) {
1878           check();
1879           if (provider instanceof OptionsTopHitProvider) {
1880             if (!ids.contains(((OptionsTopHitProvider)provider).getId())) {
1881               consumer.consume(provider);
1882               ids.add(((OptionsTopHitProvider)provider).getId());
1883             }
1884           }
1885         }
1886       } else {
1887         final ActionManager actionManager = ActionManager.getInstance();
1888         final List<String> actions = AbbreviationManager.getInstance().findActions(pattern);
1889         for (String actionId : actions) {
1890           consumer.consume(actionManager.getAction(actionId));
1891         }
1892
1893         for (SearchTopHitProvider provider : SearchTopHitProvider.EP_NAME.getExtensions()) {
1894           check();
1895           if (provider instanceof OptionsTopHitProvider && !((OptionsTopHitProvider)provider).isEnabled(project)) {
1896             continue;
1897           }
1898           provider.consumeTopHits(pattern, consumer, project);
1899         }
1900       }
1901       if (elements.size() > 0) {
1902         SwingUtilities.invokeLater(new Runnable() {
1903           @Override
1904           public void run() {
1905             if (isCanceled()) return;
1906
1907
1908             for (Object element : new ArrayList(elements)) {
1909               if (element instanceof AnAction) {
1910                 final AnAction action = (AnAction)element;
1911                 final AnActionEvent e = new AnActionEvent(myActionEvent.getInputEvent(),
1912                                                           myActionEvent.getDataContext(),
1913                                                           myActionEvent.getPlace(),
1914                                                           action.getTemplatePresentation(),
1915                                                           myActionEvent.getActionManager(),
1916                                                           myActionEvent.getModifiers());
1917                 ActionUtil.performDumbAwareUpdate(action, e, false);
1918                 final Presentation presentation = e.getPresentation();
1919                 if (!presentation.isEnabled() || !presentation.isVisible() || StringUtil.isEmpty(presentation.getText())) {
1920                   elements.remove(element);
1921                 }
1922                 if (isCanceled()) return;
1923               }
1924             }
1925             if (isCanceled() || elements.isEmpty()) return;
1926             myListModel.titleIndex.topHit = myListModel.size();
1927             for (Object element : ContainerUtil.getFirstItems(elements, MAX_TOP_HIT)) {
1928               myListModel.addElement(element);
1929             }
1930           }
1931         });
1932       }
1933     }
1934
1935     private synchronized void checkModelsUpToDate() {
1936       if (myClassModel == null) {
1937         myClassModel = new GotoClassModel2(project);
1938         myFileModel = new GotoFileModel(project);
1939         mySymbolsModel = new GotoSymbolModel2(project);
1940         myFileChooseByName = ChooseByNamePopup.createPopup(project, myFileModel, (PsiElement)null);
1941         myClassChooseByName = ChooseByNamePopup.createPopup(project, myClassModel, (PsiElement)null);
1942         mySymbolsChooseByName = ChooseByNamePopup.createPopup(project, mySymbolsModel, (PsiElement)null);
1943         project.putUserData(ChooseByNamePopup.CHOOSE_BY_NAME_POPUP_IN_PROJECT_KEY, null);
1944         myActionProvider = createActionProvider();
1945         myConfigurables.clear();
1946         fillConfigurablesIds(null, ShowSettingsUtilImpl.getConfigurables(project, true));
1947       }
1948       if (myStructureModel == null && myFileEditor != null && Registry.is("search.everywhere.structure")) {
1949         runReadAction(new Runnable() {
1950           public void run() {
1951             StructureViewBuilder structureViewBuilder = myFileEditor.getStructureViewBuilder();
1952             if (structureViewBuilder == null) return;
1953             StructureView structureView = structureViewBuilder.createStructureView(myFileEditor, project);
1954             myStructureModel = structureView.getTreeModel();
1955           }
1956         }, true);
1957       }
1958     }
1959
1960     private void buildModelFromRecentFiles() {
1961       buildRecentFiles("");
1962     }
1963
1964     private GotoActionItemProvider createActionProvider() {
1965       GotoActionModel model = new GotoActionModel(project, myFocusComponent, myEditor, myFile) {
1966         @Override
1967         protected MatchMode actionMatches(String pattern, @NotNull AnAction anAction) {
1968           String text = anAction.getTemplatePresentation().getText();
1969           return text != null && NameUtil.buildMatcher("*" + pattern, NameUtil.MatchingCaseSensitivity.NONE)
1970                    .matches(text) ? MatchMode.NAME : MatchMode.NONE;
1971         }
1972       };
1973       return new GotoActionItemProvider(model);
1974     }
1975
1976     @SuppressWarnings("SSBasedInspection")
1977     private void updatePopup() {
1978       check();
1979       SwingUtilities.invokeLater(new Runnable() {
1980         @Override
1981         public void run() {
1982           myListModel.update();
1983           myList.revalidate();
1984           myList.repaint();
1985
1986           myRenderer.recalculateWidth();
1987           if (myBalloon == null || myBalloon.isDisposed()) {
1988             return;
1989           }
1990           if (myPopup == null || !myPopup.isVisible()) {
1991             final ActionCallback callback = ListDelegationUtil.installKeyboardDelegation(getField().getTextEditor(), myList);
1992             final ComponentPopupBuilder builder = JBPopupFactory.getInstance()
1993               .createComponentPopupBuilder(new JBScrollPane(myList), null);
1994             myPopup = builder
1995               .setRequestFocus(false)
1996               .setCancelKeyEnabled(false)
1997               .setCancelCallback(new Computable<Boolean>() {
1998                 @Override
1999                 public Boolean compute() {
2000                   return myBalloon == null || myBalloon.isDisposed() || (!getField().getTextEditor().hasFocus() && !mySkipFocusGain);
2001                 }
2002               })
2003               .createPopup();
2004             myPopup.getContent().setBorder(new EmptyBorder(0, 0, 0, 0));
2005             Disposer.register(myPopup, new Disposable() {
2006               @Override
2007               public void dispose() {
2008                 callback.setDone();
2009                 resetFields();
2010                 myNonProjectCheckBox.setSelected(false);
2011                 ActionToolbarImpl.updateAllToolbarsImmediately();
2012                 if (myActionEvent != null && myActionEvent.getInputEvent() instanceof MouseEvent) {
2013                   final Component component = myActionEvent.getInputEvent().getComponent();
2014                   if (component != null) {
2015                     final JLabel label = UIUtil.getParentOfType(JLabel.class, component);
2016                     if (label != null) {
2017                       label.setIcon(AllIcons.Actions.FindPlain);
2018                     }
2019                   }
2020                 }
2021                 myActionEvent = null;
2022               }
2023             });
2024             myPopup.show(new RelativePoint(getField().getParent(), new Point(0, getField().getParent().getHeight())));
2025             updatePopupBounds();
2026
2027             ActionManager.getInstance().addAnActionListener(new AnActionListener.Adapter() {
2028               @Override
2029               public void beforeActionPerformed(AnAction action, DataContext dataContext, AnActionEvent event) {
2030                 if (action instanceof TextComponentEditorAction) {
2031                   return;
2032                 }
2033                 myPopup.cancel();
2034               }
2035             }, myPopup);
2036           }
2037           else {
2038             myList.revalidate();
2039             myList.repaint();
2040           }
2041           ListScrollingUtil.ensureSelectionExists(myList);
2042           if (myList.getModel().getSize() > 0) {
2043             updatePopupBounds();
2044           }
2045         }
2046       });
2047     }
2048
2049     public ActionCallback cancel() {
2050       myProgressIndicator.cancel();
2051       myDone.setRejected();
2052       return myDone;
2053     }
2054
2055     public ActionCallback insert(final int index, final WidgetID id) {
2056        ApplicationManager.getApplication().executeOnPooledThread(new Runnable() {
2057         public void run() {
2058           runReadAction(new Runnable() {
2059             @Override
2060             public void run() {
2061               try {
2062                 final SearchResult result
2063                   = id == WidgetID.CLASSES ? getClasses(pattern, showAll.get(), DEFAULT_MORE_STEP_COUNT, myClassChooseByName)
2064                   : id == WidgetID.FILES ? getFiles(pattern, DEFAULT_MORE_STEP_COUNT, myFileChooseByName)
2065                   : id == WidgetID.RUN_CONFIGURATIONS ? getConfigurations(pattern, DEFAULT_MORE_STEP_COUNT)
2066                   : id == WidgetID.SYMBOLS ? getSymbols(pattern, DEFAULT_MORE_STEP_COUNT, mySymbolsChooseByName)
2067                   : id == WidgetID.ACTIONS ? getActionsOrSettings(pattern, DEFAULT_MORE_STEP_COUNT, true)
2068                   : id == WidgetID.SETTINGS ? getActionsOrSettings(pattern, DEFAULT_MORE_STEP_COUNT, false)
2069                   : new SearchResult();
2070
2071                 check();
2072                 SwingUtilities.invokeLater(new Runnable() {
2073                   @Override
2074                   public void run() {
2075                     try {
2076                       int shift = 0;
2077                       int i = index+1;
2078                       for (Object o : result) {
2079                         //noinspection unchecked
2080                         myListModel.insertElementAt(o, i);
2081                         shift++;
2082                         i++;
2083                       }
2084                       MoreIndex moreIndex = myListModel.moreIndex;
2085                       myListModel.titleIndex.shift(index, shift);
2086                       moreIndex.shift(index, shift);
2087
2088                       if (!result.needMore) {
2089                         switch (id) {
2090                           case CLASSES: moreIndex.classes = -1; break;
2091                           case FILES: moreIndex.files = -1; break;
2092                           case ACTIONS: moreIndex.actions = -1; break;
2093                           case SETTINGS: moreIndex.settings = -1; break;
2094                           case SYMBOLS: moreIndex.symbols = -1; break;
2095                           case RUN_CONFIGURATIONS: moreIndex.runConfigurations = -1; break;
2096                         }
2097                       }
2098                       ListScrollingUtil.selectItem(myList, index);
2099                       myDone.setDone();
2100                     }
2101                     catch (Exception e) {
2102                       myDone.setRejected();
2103                     }
2104                   }
2105                 });
2106               }
2107               catch (Exception e) {
2108                 myDone.setRejected();
2109               }
2110             }
2111           }, true);
2112         }
2113       });
2114       return myDone;
2115     }
2116
2117     public ActionCallback start() {
2118       ApplicationManager.getApplication().executeOnPooledThread(this);
2119       return myDone;
2120     }
2121   }
2122
2123   protected void resetFields() {
2124     if (myBalloon!= null) {
2125       myBalloon.cancel();
2126       myBalloon = null;
2127     }
2128     myCurrentWorker.doWhenProcessed(new Runnable() {
2129       @Override
2130       public void run() {
2131         myFileModel = null;
2132         if (myFileChooseByName != null) {
2133           myFileChooseByName.close(false);
2134           myFileChooseByName = null;
2135         }
2136         if (myClassChooseByName != null) {
2137           myClassChooseByName.close(false);
2138           myClassChooseByName = null;
2139         }
2140         if (mySymbolsChooseByName != null) {
2141           mySymbolsChooseByName.close(false);
2142           mySymbolsChooseByName = null;
2143         }
2144         final Object lock = myCalcThread;
2145         if (lock != null) {
2146           synchronized (lock) {
2147             myClassModel = null;
2148             myActionProvider = null;
2149             mySymbolsModel = null;
2150             myConfigurables.clear();
2151             myFocusComponent = null;
2152             myContextComponent = null;
2153             myFocusOwner = null;
2154             myRenderer.myProject = null;
2155             myPopup = null;
2156             myHistoryIndex = 0;
2157             myPopupActualWidth = 0;
2158             myCurrentWorker = ActionCallback.DONE;
2159             showAll.set(false);
2160             myCalcThread = null;
2161             myEditor = null;
2162             myFileEditor = null;
2163             myStructureModel = null;
2164           }
2165         }
2166       }
2167     });
2168     mySkipFocusGain = false;
2169   }
2170
2171   private void updatePopupBounds() {
2172     if (myPopup == null || !myPopup.isVisible()) {
2173       return;
2174     }
2175     final Container parent = getField().getParent();
2176     final Dimension size = myList.getParent().getParent().getPreferredSize();
2177     size.width = myPopupActualWidth - 2;
2178     if (size.width + 2 < parent.getWidth()) {
2179       size.width = parent.getWidth();
2180     }
2181     if (myList.getItemsCount() == 0) {
2182       size.height = 70;
2183     }
2184     Dimension sz = new Dimension(size.width, myList.getPreferredSize().height);
2185     if (sz.width > POPUP_MAX_WIDTH || sz.height > POPUP_MAX_WIDTH) {
2186       final JBScrollPane pane = new JBScrollPane();
2187       final int extraWidth = pane.getVerticalScrollBar().getWidth() + 1;
2188       final int extraHeight = pane.getHorizontalScrollBar().getHeight() + 1;
2189       sz = new Dimension(Math.min(POPUP_MAX_WIDTH, Math.max(getField().getWidth(), sz.width + extraWidth)), Math.min(POPUP_MAX_WIDTH, sz.height + extraHeight));
2190       sz.width += 20;
2191       sz.height+=2;
2192     } else {
2193       sz.width+=2;
2194       sz.height+=2;
2195     }
2196     sz.width = Math.max(sz.width, myPopup.getSize().width);
2197     myPopup.setSize(sz);
2198     if (myActionEvent != null && myActionEvent.getInputEvent() == null) {
2199       final Point p = parent.getLocationOnScreen();
2200       p.y += parent.getHeight();
2201       if (parent.getWidth() < sz.width) {
2202         p.x -= sz.width - parent.getWidth();
2203       }
2204       myPopup.setLocation(p);
2205     } else {
2206       try {
2207         adjustPopup();
2208       }
2209       catch (Exception ignore) {}
2210     }
2211   }
2212
2213   private void adjustPopup() {
2214 //    new PopupPositionManager.PositionAdjuster(getField().getParent(), 0).adjust(myPopup, PopupPositionManager.Position.BOTTOM);
2215     final Dimension d = PopupPositionManager.PositionAdjuster.getPopupSize(myPopup);
2216     final JComponent myRelativeTo = myBalloon.getContent();
2217     Point myRelativeOnScreen = myRelativeTo.getLocationOnScreen();
2218     Rectangle screen = ScreenUtil.getScreenRectangle(myRelativeOnScreen);
2219     Rectangle popupRect = null;
2220     Rectangle r = new Rectangle(myRelativeOnScreen.x, myRelativeOnScreen.y + myRelativeTo.getHeight(), d.width, d.height);
2221
2222       if (screen.contains(r)) {
2223         popupRect = r;
2224       }
2225
2226     if (popupRect != null) {
2227       myPopup.setLocation(new Point(r.x, r.y));
2228     }
2229     else {
2230       if (r.y + d.height > screen.y + screen.height) {
2231         r.height =  screen.y + screen.height - r.y - 2;
2232       }
2233       if (r.width > screen.width) {
2234         r.width = screen.width - 50;
2235       }
2236       if (r.x + r.width > screen.x + screen.width) {
2237         r.x = screen.x + screen.width - r.width - 2;
2238       }
2239
2240       myPopup.setSize(r.getSize());
2241       myPopup.setLocation(r.getLocation());
2242     }
2243
2244   }
2245
2246   private static boolean isToolWindowAction(Object o) {
2247     return isActionValue(o)
2248            && o instanceof GotoActionModel.ActionWrapper
2249            && ((GotoActionModel.ActionWrapper)o).getAction() instanceof ActivateToolWindowAction;
2250   }
2251
2252   private void fillConfigurablesIds(String pathToParent, Configurable[] configurables) {
2253     for (Configurable configurable : configurables) {
2254       if (configurable instanceof SearchableConfigurable) {
2255         final String id = ((SearchableConfigurable)configurable).getId();
2256         String name = configurable.getDisplayName();
2257         if (pathToParent != null) {
2258           name = pathToParent + " -> " + name;
2259         }
2260         myConfigurables.put(id, name);
2261         if (configurable instanceof SearchableConfigurable.Parent) {
2262           fillConfigurablesIds(name, ((SearchableConfigurable.Parent)configurable).getConfigurables());
2263         }
2264       }
2265     }
2266   }
2267
2268   static class MoreIndex {
2269     volatile int classes = -1;
2270     volatile int files = -1;
2271     volatile int actions = -1;
2272     volatile int settings = -1;
2273     volatile int symbols = -1;
2274     volatile int runConfigurations = -1;
2275     volatile int structure = -1;
2276
2277     public void shift(int index, int shift) {
2278       if (runConfigurations >= index) runConfigurations += shift;
2279       if (classes >= index) classes += shift;
2280       if (files >= index) files += shift;
2281       if (symbols >= index) symbols += shift;
2282       if (actions >= index) actions += shift;
2283       if (settings >= index) settings += shift;
2284       if (structure >= index) structure += shift;
2285     }
2286   }
2287
2288   static class TitleIndex {
2289     volatile int topHit = -1;
2290     volatile int recentFiles = -1;
2291     volatile int runConfigurations = -1;
2292     volatile int classes = -1;
2293     volatile int structure = -1;
2294     volatile int files = -1;
2295     volatile int actions = -1;
2296     volatile int settings = -1;
2297     volatile int toolWindows = -1;
2298     volatile int symbols = -1;
2299
2300     final String gotoClassTitle;
2301     final String gotoFileTitle;
2302     final String gotoActionTitle;
2303     final String gotoSettingsTitle;
2304     final String gotoRecentFilesTitle;
2305     final String gotoRunConfigurationsTitle;
2306     final String gotoSymbolTitle;
2307     final String gotoStructureTitle;
2308     static final String toolWindowsTitle = "Tool Windows";
2309
2310     TitleIndex() {
2311       String gotoClass = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("GotoClass"));
2312       gotoClassTitle = StringUtil.isEmpty(gotoClass) ? "Classes" : "Classes (" + gotoClass + ")";
2313       String gotoFile = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("GotoFile"));
2314       gotoFileTitle = StringUtil.isEmpty(gotoFile) ? "Files" : "Files (" + gotoFile + ")";
2315       String gotoAction = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("GotoAction"));
2316       gotoActionTitle = StringUtil.isEmpty(gotoAction) ? "Actions" : "Actions (" + gotoAction + ")";
2317       String gotoSettings = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("ShowSettings"));
2318       gotoSettingsTitle = StringUtil.isEmpty(gotoAction) ? "Preferences" : "Preferences (" + gotoSettings + ")";
2319       String gotoRecentFiles = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("RecentFiles"));
2320       gotoRecentFilesTitle = StringUtil.isEmpty(gotoRecentFiles) ? "Recent Files" : "Recent Files (" + gotoRecentFiles + ")";
2321       String gotoSymbol = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("GotoSymbol"));
2322       gotoSymbolTitle = StringUtil.isEmpty(gotoSymbol) ? "Symbols" : "Symbols (" + gotoSymbol + ")";
2323       String gotoRunConfiguration = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("ChooseDebugConfiguration"));
2324       if (StringUtil.isEmpty(gotoRunConfiguration)) {
2325         gotoRunConfiguration = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("ChooseRunConfiguration"));
2326       }
2327       gotoRunConfigurationsTitle = StringUtil.isEmpty(gotoRunConfiguration) ? "Run Configurations" : "Run Configurations (" + gotoRunConfiguration + ")";
2328       String gotoStructure = KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction("FileStructurePopup"));
2329       gotoStructureTitle = StringUtil.isEmpty(gotoStructure) ? "File Structure" : "File Structure (" + gotoStructure + ")";
2330     }
2331
2332     String getTitle(int index) {
2333       if (index == topHit) return index == 0 ? "Top Hit" : "Top Hits";
2334       if (index == recentFiles) return gotoRecentFilesTitle;
2335       if (index == structure) return gotoStructureTitle;
2336       if (index == runConfigurations) return gotoRunConfigurationsTitle;
2337       if (index == classes) return gotoClassTitle;
2338       if (index == files) return gotoFileTitle;
2339       if (index == toolWindows) return toolWindowsTitle;
2340       if (index == actions) return gotoActionTitle;
2341       if (index == settings) return gotoSettingsTitle;
2342       if (index == symbols) return gotoSymbolTitle;
2343       return null;
2344     }
2345
2346     public void clear() {
2347       topHit = -1;
2348       runConfigurations = -1;
2349       recentFiles = -1;
2350       classes = -1;
2351       files = -1;
2352       structure = -1;
2353       actions = -1;
2354       settings = -1;
2355       toolWindows = -1;
2356     }
2357
2358     public void shift(int index, int shift) {
2359       if (toolWindows != - 1 && toolWindows > index) toolWindows += shift;
2360       if (settings != - 1 && settings > index) settings += shift;
2361       if (actions != - 1 && actions > index) actions += shift;
2362       if (files != - 1 && files > index) files += shift;
2363       if (structure != - 1 && structure > index) structure += shift;
2364       if (classes != - 1 && classes > index) classes += shift;
2365       if (runConfigurations != - 1 && runConfigurations > index) runConfigurations += shift;
2366       if (symbols != - 1 && symbols > index) symbols += shift;
2367     }
2368   }
2369
2370   static class SearchResult extends ArrayList<Object> {
2371     boolean needMore;
2372   }
2373
2374   @SuppressWarnings("unchecked")
2375   private static class SearchListModel extends DefaultListModel {
2376     @SuppressWarnings("UseOfObsoleteCollectionType")
2377     Vector myDelegate;
2378
2379     volatile TitleIndex titleIndex = new TitleIndex();
2380     volatile MoreIndex moreIndex = new MoreIndex();
2381
2382     private SearchListModel() {
2383       super();
2384       myDelegate = ReflectionUtil.getField(DefaultListModel.class, this, Vector.class, "delegate");
2385     }
2386
2387     int next(int index) {
2388       int[] all = getAll();
2389       Arrays.sort(all);
2390       for (int next : all) {
2391         if (next > index) return next;
2392       }
2393       return 0;
2394     }
2395
2396     int[] getAll() {
2397       return new int[]{
2398         titleIndex.topHit,
2399         titleIndex.recentFiles,
2400         titleIndex.structure,
2401         titleIndex.runConfigurations,
2402         titleIndex.classes,
2403         titleIndex.files,
2404         titleIndex.actions,
2405         titleIndex.settings,
2406         titleIndex.toolWindows,
2407         titleIndex.symbols,
2408         moreIndex.classes,
2409         moreIndex.actions,
2410         moreIndex.files,
2411         moreIndex.settings,
2412         moreIndex.symbols,
2413         moreIndex.runConfigurations,
2414         moreIndex.structure
2415       };
2416     }
2417
2418     int prev(int index) {
2419       int[] all = getAll();
2420       Arrays.sort(all);
2421       for (int i = all.length-1; i >= 0; i--) {
2422         if (all[i] != -1 && all[i] < index) return all[i];
2423       }
2424       return all[all.length - 1];
2425     }
2426
2427     @Override
2428     public void addElement(Object obj) {
2429       myDelegate.add(obj);
2430     }
2431
2432     public void update() {
2433       fireContentsChanged(this, 0, getSize() - 1);
2434     }
2435   }
2436
2437   static class More extends JPanel {
2438     static final More instance = new More();
2439     final JLabel label = new JLabel("    ... more   ");
2440
2441     private More() {
2442       super(new BorderLayout());
2443       add(label, BorderLayout.CENTER);
2444     }
2445
2446     static More get(boolean isSelected) {
2447       instance.setBackground(UIUtil.getListBackground(isSelected));
2448       instance.label.setForeground(UIUtil.getLabelDisabledForeground());
2449       instance.label.setFont(getTitleFont());
2450       instance.label.setBackground(UIUtil.getListBackground(isSelected));
2451       return instance;
2452     }
2453   }
2454
2455   private static JComponent createTitle(String titleText) {
2456     JLabel titleLabel = new JLabel(titleText);
2457     titleLabel.setFont(getTitleFont());
2458     titleLabel.setForeground(UIUtil.getLabelDisabledForeground());
2459     final Color bg = UIUtil.getListBackground();
2460     SeparatorComponent separatorComponent =
2461       new SeparatorComponent(titleLabel.getPreferredSize().height / 2, new JBColor(Gray._220, Gray._80), null);
2462
2463     JPanel result = new JPanel(new BorderLayout(5, 10));
2464     result.add(titleLabel, BorderLayout.WEST);
2465     result.add(separatorComponent, BorderLayout.CENTER);
2466     result.setBackground(bg);
2467
2468     return result;
2469   }
2470
2471   private enum HistoryType {PSI, FILE, SETTING, ACTION, RUN_CONFIGURATION}
2472
2473   @Nullable
2474   private static HistoryType parseHistoryType(@Nullable String name) {
2475     try {
2476       return HistoryType.valueOf(name);
2477     } catch (Exception e) {
2478       return null;
2479     }
2480   }
2481
2482   private static class HistoryItem {
2483     final String pattern, type, fqn;
2484
2485     private HistoryItem(String pattern, String type, String fqn) {
2486       this.pattern = pattern;
2487       this.type = type;
2488       this.fqn = fqn;
2489     }
2490
2491     public String toString() {
2492       return pattern + "\t" + type + "\t" + fqn;
2493     }
2494
2495     @Override
2496     public boolean equals(Object o) {
2497       if (this == o) return true;
2498       if (o == null || getClass() != o.getClass()) return false;
2499
2500       HistoryItem item = (HistoryItem)o;
2501
2502       if (!pattern.equals(item.pattern)) return false;
2503
2504       return true;
2505     }
2506
2507     @Override
2508     public int hashCode() {
2509       return pattern.hashCode();
2510     }
2511   }
2512 }
2513