build searchable options respects configurables filters
[idea/community.git] / platform / platform-impl / src / com / intellij / ide / ui / search / SearchUtil.java
1 /*
2  * Copyright 2000-2009 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
17 package com.intellij.ide.ui.search;
18
19 import com.intellij.openapi.options.Configurable;
20 import com.intellij.openapi.options.ConfigurableGroup;
21 import com.intellij.openapi.options.MasterDetails;
22 import com.intellij.openapi.options.SearchableConfigurable;
23 import com.intellij.openapi.options.ex.GlassPanel;
24 import com.intellij.openapi.options.ex.IdeConfigurablesGroup;
25 import com.intellij.openapi.options.ex.ProjectConfigurablesGroup;
26 import com.intellij.openapi.project.Project;
27 import com.intellij.openapi.ui.popup.JBPopup;
28 import com.intellij.openapi.ui.popup.JBPopupFactory;
29 import com.intellij.openapi.util.text.StringUtil;
30 import com.intellij.ui.*;
31 import com.intellij.util.Alarm;
32 import com.intellij.util.Consumer;
33 import com.intellij.util.ui.UIUtil;
34 import org.jetbrains.annotations.NonNls;
35 import org.jetbrains.annotations.NotNull;
36 import org.jetbrains.annotations.Nullable;
37
38 import javax.swing.*;
39 import javax.swing.border.Border;
40 import javax.swing.border.TitledBorder;
41 import java.awt.*;
42 import java.awt.event.ActionEvent;
43 import java.awt.event.ActionListener;
44 import java.awt.event.KeyAdapter;
45 import java.awt.event.KeyEvent;
46 import java.util.*;
47 import java.util.List;
48 import java.util.regex.Matcher;
49 import java.util.regex.Pattern;
50
51 /**
52  * User: anna
53  * Date: 07-Feb-2006
54  */
55 public class SearchUtil {
56   private static final Pattern HTML_PATTERN = Pattern.compile("<[^<>]*>");
57   private static final Pattern QUOTED = Pattern.compile("\\\"([^\\\"]+)\\\"");
58
59   public static final String HIGHLIGHT_WITH_BORDER = "searchUtil.highlightWithBorder";
60
61   private SearchUtil() {
62   }
63
64   public static void processProjectConfigurables(Project project, HashMap<SearchableConfigurable, TreeSet<OptionDescription>> options) {
65     processConfigurables(new ProjectConfigurablesGroup(project, false).getConfigurables(), options);
66     processConfigurables(new IdeConfigurablesGroup().getConfigurables(), options);
67   }
68
69   private static void processConfigurables(final Configurable[] configurables,
70                                           final HashMap<SearchableConfigurable, TreeSet<OptionDescription>> options) {
71     for (Configurable configurable : configurables) {
72       if (configurable instanceof SearchableConfigurable) {
73         TreeSet<OptionDescription> configurableOptions = new TreeSet<OptionDescription>();
74         options.put((SearchableConfigurable)configurable, configurableOptions);
75         if (configurable instanceof Configurable.Composite) {
76           final Configurable[] children = ((Configurable.Composite)configurable).getConfigurables();
77           processConfigurables(children, options);
78         }
79
80         if (configurable instanceof MasterDetails) {
81           final MasterDetails md = (MasterDetails)configurable;
82           md.initUi();
83           _processComponent(configurable, configurableOptions, md.getMaster());
84           _processComponent(configurable, configurableOptions, md.getDetails().getComponent());
85         }
86         else {
87           _processComponent(configurable, configurableOptions, configurable.createComponent());
88         }
89       }
90     }
91   }
92
93   private static void _processComponent(final Configurable configurable, final TreeSet<OptionDescription> configurableOptions,
94                                         final JComponent component) {
95
96     if (component == null) return;
97
98     processUILabel(configurable.getDisplayName(), configurableOptions, null);
99     processComponent(component, configurableOptions, null);
100   }
101
102   public static void processComponent(final JComponent component, final Set<OptionDescription> configurableOptions, @NonNls String path) {
103     final Border border = component.getBorder();
104     if (border instanceof TitledBorder) {
105       final TitledBorder titledBorder = (TitledBorder)border;
106       final String title = titledBorder.getTitle();
107       if (title != null) {
108         processUILabel(title, configurableOptions, path);
109       }
110     }
111     if (component instanceof JLabel) {
112       final String label = ((JLabel)component).getText();
113       if (label != null) {
114         processUILabel(label, configurableOptions, path);
115       }
116     }
117     else if (component instanceof JCheckBox) {
118       @NonNls final String checkBoxTitle = ((JCheckBox)component).getText();
119       if (checkBoxTitle != null) {
120         processUILabel(checkBoxTitle, configurableOptions, path);
121       }
122     }
123     else if (component instanceof JRadioButton) {
124       @NonNls final String radioButtonTitle = ((JRadioButton)component).getText();
125       if (radioButtonTitle != null) {
126         processUILabel(radioButtonTitle, configurableOptions, path);
127       }
128     }
129     else if (component instanceof JButton) {
130       @NonNls final String buttonTitle = ((JButton)component).getText();
131       if (buttonTitle != null) {
132         processUILabel(buttonTitle, configurableOptions, path);
133       }
134     }
135     if (component instanceof JTabbedPane) {
136       final JTabbedPane tabbedPane = (JTabbedPane)component;
137       final int tabCount = tabbedPane.getTabCount();
138       for (int i = 0; i < tabCount; i++) {
139         final String title = path != null ? path + '.' + tabbedPane.getTitleAt(i) : tabbedPane.getTitleAt(i);
140         processUILabel(title, configurableOptions, title);
141         final Component tabComponent = tabbedPane.getComponentAt(i);
142         if (tabComponent instanceof JComponent) {
143           processComponent((JComponent)tabComponent, configurableOptions, title);
144         }
145       }
146     }
147     else {
148       final Component[] components = component.getComponents();
149       if (components != null) {
150         for (Component child : components) {
151           if (child instanceof JComponent) {
152             processComponent((JComponent)child, configurableOptions, path);
153           }
154         }
155       }
156     }
157   }
158
159   private static void processUILabel(@NonNls final String title, final Set<OptionDescription> configurableOptions, String path) {
160     final Set<String> words = SearchableOptionsRegistrar.getInstance().getProcessedWordsWithoutStemming(title);
161     @NonNls final String regex = "[\\W&&[^\\p{Punct}\\p{Blank}]]";
162     for (String option : words) {
163       configurableOptions.add(new OptionDescription(option, HTML_PATTERN.matcher(title).replaceAll(" ").replaceAll(regex, " "), path));
164     }
165   }
166
167   public static Runnable lightOptions(final SearchableConfigurable configurable,
168                                       final JComponent component,
169                                       final String option,
170                                       final GlassPanel glassPanel) {
171     return new Runnable() {
172       public void run() {
173         if (!SearchUtil.traverseComponentsTree(configurable, glassPanel, component, option, true)) {
174           SearchUtil.traverseComponentsTree(configurable, glassPanel, component, option, false);
175         }
176       }
177     };
178   }
179
180   public static int getSelection(String tabIdx, final JTabbedPane tabbedPane) {
181     SearchableOptionsRegistrar searchableOptionsRegistrar = SearchableOptionsRegistrar.getInstance();
182     for (int i = 0; i < tabbedPane.getTabCount(); i++) {
183       final Set<String> pathWords = searchableOptionsRegistrar.getProcessedWords(tabIdx);
184       final String title = tabbedPane.getTitleAt(i);
185       final Set<String> titleWords = searchableOptionsRegistrar.getProcessedWords(title);
186       pathWords.removeAll(titleWords);
187       if (pathWords.isEmpty()) return i;
188     }
189     return -1;
190   }
191
192   public static int getSelection(String tabIdx, final TabbedPaneWrapper tabbedPane) {
193     SearchableOptionsRegistrar searchableOptionsRegistrar = SearchableOptionsRegistrar.getInstance();
194     for (int i = 0; i < tabbedPane.getTabCount(); i++) {
195       final Set<String> pathWords = searchableOptionsRegistrar.getProcessedWords(tabIdx);
196       final String title = tabbedPane.getTitleAt(i);
197       final Set<String> titleWords = searchableOptionsRegistrar.getProcessedWords(title);
198       pathWords.removeAll(titleWords);
199       if (pathWords.isEmpty()) return i;
200     }
201     return -1;
202   }
203
204   private static boolean traverseComponentsTree(final SearchableConfigurable configurable,
205                                                 GlassPanel glassPanel,
206                                                 JComponent rootComponent,
207                                                 String option,
208                                                 boolean force) {
209
210     rootComponent.putClientProperty(HIGHLIGHT_WITH_BORDER, null);
211
212     if (option == null || option.trim().length() == 0) return false;
213     boolean highlight = false;
214     if (rootComponent instanceof JCheckBox) {
215       final JCheckBox checkBox = ((JCheckBox)rootComponent);
216       if (isComponentHighlighted(checkBox.getText(), option, force, configurable)) {
217         highlight = true;
218         glassPanel.addSpotlight(checkBox);
219       }
220     }
221     else if (rootComponent instanceof JRadioButton) {
222       final JRadioButton radioButton = ((JRadioButton)rootComponent);
223       if (isComponentHighlighted(radioButton.getText(), option, force, configurable)) {
224         highlight = true;
225         glassPanel.addSpotlight(radioButton);
226       }
227     }
228     else if (rootComponent instanceof JLabel) {
229       final JLabel label = ((JLabel)rootComponent);
230       if (isComponentHighlighted(label.getText(), option, force, configurable)) {
231         highlight = true;
232         glassPanel.addSpotlight(label);
233       }
234     }
235     else if (rootComponent instanceof JButton) {
236       final JButton button = ((JButton)rootComponent);
237       if (isComponentHighlighted(button.getText(), option, force, configurable)) {
238         highlight = true;
239         glassPanel.addSpotlight(button);
240       }
241     }
242     else if (rootComponent instanceof JTabbedPane) {
243       final JTabbedPane tabbedPane = (JTabbedPane)rootComponent;
244       final String path = SearchableOptionsRegistrarImpl.getInstance().getInnerPath(configurable, option);
245       if (path != null) {
246         final int index = SearchUtil.getSelection(path, tabbedPane);
247         if (index > -1 && index < tabbedPane.getTabCount()) {
248           tabbedPane.setSelectedIndex(index);
249         }
250       }
251     }
252
253
254     final Component[] components = rootComponent.getComponents();
255     for (Component component : components) {
256       if (component instanceof JComponent) {
257         final boolean innerHighlight = traverseComponentsTree(configurable, glassPanel, (JComponent)component, option, force);
258
259         if (!highlight && !innerHighlight) {
260           final Border border = rootComponent.getBorder();
261           if (border instanceof TitledBorder) {
262             final String title = ((TitledBorder)border).getTitle();
263             if (isComponentHighlighted(title, option, force, configurable)) {
264               highlight = true;
265               glassPanel.addSpotlight(rootComponent);
266               rootComponent.putClientProperty(HIGHLIGHT_WITH_BORDER, Boolean.TRUE);
267             }
268           }
269         }
270
271
272         if (innerHighlight) {
273           highlight = true;
274         }
275       }
276     }
277     return highlight;
278   }
279
280   public static boolean isComponentHighlighted(String text, String option, final boolean force, final SearchableConfigurable configurable) {
281     if (text == null || option == null || option.length() == 0) return false;
282     final SearchableOptionsRegistrar searchableOptionsRegistrar = SearchableOptionsRegistrar.getInstance();
283     final Set<String> words = searchableOptionsRegistrar.getProcessedWords(option);
284     final Set<String> options =
285       configurable != null ? searchableOptionsRegistrar.replaceSynonyms(words, configurable) : words;
286     if (options == null || options.isEmpty()) {
287       return text.toLowerCase().indexOf(option.toLowerCase()) != -1;
288     }
289     final Set<String> tokens = searchableOptionsRegistrar.getProcessedWords(text);
290     if (!force) {
291       options.retainAll(tokens);
292       final boolean highlight = !options.isEmpty();
293       return highlight || text.toLowerCase().indexOf(option.toLowerCase()) != -1;
294     }
295     else {
296       options.removeAll(tokens);
297       return options.isEmpty();
298     }
299   }
300
301   public static Runnable lightOptions(final SearchableConfigurable configurable,
302                                       final JComponent component,
303                                       final String option,
304                                       final GlassPanel glassPanel,
305                                       final boolean forceSelect) {
306     return new Runnable() {
307       public void run() {
308         SearchUtil.traverseComponentsTree(configurable, glassPanel, component, option, forceSelect);
309       }
310     };
311   }
312
313   public static String markup(@NonNls @NotNull String textToMarkup, String filter) {
314     if (filter == null || filter.length() == 0) {
315       return textToMarkup;
316     }
317     final Pattern insideHtmlTagPattern = Pattern.compile("[<[^<>]*>]*<[^<>]*");
318     final SearchableOptionsRegistrar registrar = SearchableOptionsRegistrar.getInstance();
319     final HashSet<String> quoted = new HashSet<String>();
320     filter = processFilter(quoteStrictOccurences(textToMarkup, filter), quoted);
321     final Set<String> options = registrar.getProcessedWords(filter);
322     final Set<String> words = registrar.getProcessedWords(textToMarkup);
323     for (String option : options) {
324       if (words.contains(option)) {
325         textToMarkup = markup(textToMarkup, insideHtmlTagPattern, option);
326       }
327     }
328     for (String stripped : quoted) {
329       textToMarkup = markup(textToMarkup, insideHtmlTagPattern, stripped);
330     }
331     return textToMarkup;
332   }
333
334   private static String quoteStrictOccurences(final String textToMarkup, final String filter) {
335     String cur = "";
336     for (String part : filter.split(" ")) {
337       if (textToMarkup.toLowerCase().indexOf(part) != -1) {
338         cur += "\"" + part + "\" ";
339       } else {
340         cur += part + " ";
341       }
342     }
343     return cur;
344   }
345
346   private static String markup(@NonNls String textToMarkup, final Pattern insideHtmlTagPattern, final String option) {
347     @NonNls String result = "";
348     int beg = 0;
349     int idx;
350     while ((idx = StringUtil.indexOfIgnoreCase(textToMarkup, option, beg)) != -1) {
351       final String prefix = textToMarkup.substring(beg, idx);
352       final String toMark = textToMarkup.substring(idx, idx + option.length());
353       if (insideHtmlTagPattern.matcher(prefix).matches()) {
354         result += prefix + toMark;
355       } else {
356         result += prefix + "<font color='#ffffff' bgColor='#1d5da7'>" + toMark + "</font>";
357       }
358       beg = idx + option.length();
359     }
360     result += textToMarkup.substring(beg);
361     return result;
362   }
363
364   public static void appendFragments(String filter,
365                                      @NonNls String text,
366                                      final int style,
367                                      final Color foreground,
368                                      final Color background,
369                                      final ColoredTreeCellRenderer textRenderer) {
370     if (text == null) return;
371     if (filter == null || filter.length() == 0) {
372       textRenderer.append(text, new SimpleTextAttributes(style, foreground));
373     }
374     else { //markup
375       final HashSet<String> quoted = new HashSet<String>();
376       filter = processFilter(quoteStrictOccurences(text, filter), quoted);
377       final TreeMap<Integer, String> indx = new TreeMap<Integer, String>();
378       for (String stripped : quoted) {
379         int beg = 0;
380         int idx;
381         while ((idx = StringUtil.indexOfIgnoreCase(text, stripped, beg)) != -1) {
382           indx.put(idx, text.substring(idx, idx + stripped.length()));
383           beg = idx + stripped.length();
384         }
385       }
386
387       final List<String> selectedWords = new ArrayList<String>();
388       int pos = 0;
389       for (Integer index : indx.keySet()) {
390         final String stripped = indx.get(index);
391         final int start = index.intValue();
392         if (pos > start) {
393           final String highlighted = selectedWords.get(selectedWords.size() - 1);
394           if (highlighted.length() < stripped.length()){
395             selectedWords.remove(highlighted);
396           } else {
397             continue;
398           }
399         }
400         appendSelectedWords(text, selectedWords, pos, start, filter);
401         selectedWords.add(stripped);
402         pos = start + stripped.length();
403       }
404       appendSelectedWords(text, selectedWords, pos, text.length(), filter);
405
406       int idx = 0;
407       for (String word : selectedWords) {
408         text = text.substring(idx);
409         textRenderer.append(text.substring(0, text.indexOf(word)), new SimpleTextAttributes(background, foreground, null, style));
410         idx = text.indexOf(word) + word.length();
411         textRenderer.append(text.substring(idx - word.length(), idx), new SimpleTextAttributes(UIUtil.getTreeSelectionBackground(),
412                                                                                                UIUtil.getTreeSelectionForeground(), null,
413                                                                                                style));
414       }
415       textRenderer.append(text.substring(idx, text.length()), new SimpleTextAttributes(background, foreground, null, style));
416     }
417   }
418
419   private static void appendSelectedWords(final String text, final List<String> selectedWords, final int pos, int end, final String filter) {
420     if (pos < end) {
421       final Set<String> filters = SearchableOptionsRegistrar.getInstance().getProcessedWords(filter);
422       final String[] words = text.substring(pos, end).split("[\\W&&[^_-]]");
423       for (String word : words) {
424         if (filters.contains(PorterStemmerUtil.stem(word.toLowerCase()))) {
425           selectedWords.add(word);
426         }
427       }
428     }
429   }
430
431   @Nullable
432   private static JBPopup createPopup(final ConfigurableSearchTextField searchField,
433                                      final JBPopup[] activePopup,
434                                      final Alarm showHintAlarm,
435                                      final Consumer<String> selectConfigurable,
436                                      final Project project,
437                                      final int down) {
438
439     final String filter = searchField.getText();
440     if (filter == null || filter.length() == 0) return null;
441     final Map<String, Set<String>> hints = SearchableOptionsRegistrar.getInstance().findPossibleExtension(filter, project);
442     final DefaultListModel model = new DefaultListModel();
443     final JList list = new JList(model);
444     for (String groupName : hints.keySet()) {
445       model.addElement(groupName);
446       final Set<String> descriptions = hints.get(groupName);
447       if (descriptions != null) {
448         for (String hit : descriptions) {
449           if (hit == null) continue;
450           model.addElement(new OptionDescription(null, groupName, hit, null));
451         }
452       }
453     }
454     ListScrollingUtil.installActions(list);
455     list.setCellRenderer(new DefaultListCellRenderer() {
456       public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
457         final Component rendererComponent = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
458         if (value instanceof String) {
459           setText("------ " + value + " ------");
460         }
461         else if (value instanceof OptionDescription) {
462           setText(((OptionDescription)value).getHit());
463         }
464         return rendererComponent;
465       }
466     });
467
468
469     if (model.size() > 0) {
470       final Runnable onChosen = new Runnable() {
471         public void run() {
472           final Object selectedValue = list.getSelectedValue();
473           if (selectedValue instanceof OptionDescription) {
474             final OptionDescription description = ((OptionDescription)selectedValue);
475             searchField.setText(description.getHit());
476             searchField.addCurrentTextToHistory();
477             SwingUtilities.invokeLater(new Runnable() {
478               public void run() {     //do not show look up again
479                 showHintAlarm.cancelAllRequests();
480                 selectConfigurable.consume(description.getConfigurableId());
481               }
482             });
483           }
484         }
485       };
486       final JBPopup popup = JBPopupFactory.getInstance()
487         .createListPopupBuilder(list)
488         .setItemChoosenCallback(onChosen)
489         .setRequestFocus(down != 0)
490         .createPopup();
491       list.addKeyListener(new KeyAdapter() {
492         public void keyPressed(final KeyEvent e) {
493           if (e.getKeyCode() != KeyEvent.VK_ENTER && e.getKeyCode() != KeyEvent.VK_UP && e.getKeyCode() != KeyEvent.VK_DOWN &&
494               e.getKeyCode() != KeyEvent.VK_PAGE_UP && e.getKeyCode() != KeyEvent.VK_PAGE_DOWN) {
495             searchField.requestFocusInWindow();
496             if (cancelPopups(activePopup) && e.getKeyCode() == KeyEvent.VK_ESCAPE) {
497               return;
498             }
499             if (e.getKeyChar() != KeyEvent.CHAR_UNDEFINED){
500               searchField.process(new KeyEvent(searchField, KeyEvent.KEY_TYPED, e.getWhen(), e.getModifiers(), KeyEvent.VK_UNDEFINED, e.getKeyChar()));
501             }
502           }
503         }
504
505       });
506       if (down > 0) {
507         if (list.getSelectedIndex() < list.getModel().getSize() - 1) {
508           list.setSelectedIndex(list.getSelectedIndex() + 1);
509         }
510       }
511       else if (down < 0) {
512         if (list.getSelectedIndex() > 0) {
513           list.setSelectedIndex(list.getSelectedIndex() - 1);
514         }
515       }
516       return popup;
517     }
518     return null;
519   }
520
521   public static void showHintPopup(final ConfigurableSearchTextField searchField,
522                                    final JBPopup[] activePopup,
523                                    final Alarm showHintAlarm,
524                                    final Consumer<String> selectConfigurable,
525                                    final Project project) {
526     for (JBPopup aPopup : activePopup) {
527       if (aPopup != null) {
528         aPopup.cancel();
529       }
530     }
531
532     final JBPopup popup = createPopup(searchField, activePopup, showHintAlarm, selectConfigurable, project, 0); //no selection
533     if (popup != null) {
534       popup.showUnderneathOf(searchField);
535       searchField.requestFocusInWindow();
536     }
537
538     activePopup[0] = popup;
539     activePopup[1] = null;
540   }
541
542
543   public static void registerKeyboardNavigation(final ConfigurableSearchTextField searchField,
544                                                 final JBPopup[] activePopup,
545                                                 final Alarm showHintAlarm,
546                                                 final Consumer<String> selectConfigurable,
547                                                 final Project project) {
548     final Consumer<Integer> shower = new Consumer<Integer>() {
549       public void consume(final Integer direction) {
550         if (activePopup[0] != null) {
551           activePopup[0].cancel();
552         }
553
554         if (activePopup[1] != null && activePopup[1].isVisible()) {
555           return;
556         }
557
558         final JBPopup popup = createPopup(searchField, activePopup, showHintAlarm, selectConfigurable, project, direction.intValue());
559         if (popup != null) {
560           popup.showUnderneathOf(searchField);
561         }
562         activePopup[0] = null;
563         activePopup[1] = popup;
564       }
565     };
566     searchField.registerKeyboardAction(new ActionListener() {
567       public void actionPerformed(ActionEvent e) {
568         shower.consume(1);
569       }
570     }, KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
571     searchField.registerKeyboardAction(new ActionListener() {
572       public void actionPerformed(ActionEvent e) {
573         shower.consume(-1);
574       }
575     }, KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
576
577     searchField.addKeyboardListener(new KeyAdapter() {
578       public void keyPressed(KeyEvent e) {
579         if (e.getKeyCode() == KeyEvent.VK_ESCAPE && searchField.getText().length() > 0) {
580           e.consume();
581           if (cancelPopups(activePopup)) return;
582           searchField.setText("");
583         }
584         else if (e.getKeyCode() == KeyEvent.VK_ENTER) {
585           searchField.addCurrentTextToHistory();
586           cancelPopups(activePopup);
587           if (e.getModifiers() == 0) {
588             e.consume();
589           }
590         }
591       }
592     });
593   }
594
595   private static boolean cancelPopups(final JBPopup[] activePopup) {
596     for (JBPopup popup : activePopup) {
597       if (popup != null && popup.isVisible()) {
598         popup.cancel();
599         return true;
600       }
601     }
602     return false;
603   }
604
605   public static List<Set<String>> findKeys(String filter, Set<String> quoted) {
606     filter = processFilter(filter.toLowerCase(), quoted);
607     final List<Set<String>> keySetList = new ArrayList<Set<String>>();
608     final SearchableOptionsRegistrar optionsRegistrar = SearchableOptionsRegistrar.getInstance();
609     final Set<String> words = optionsRegistrar.getProcessedWords(filter);
610     for (String word : words) {
611       final Set<OptionDescription> descriptions = ((SearchableOptionsRegistrarImpl)optionsRegistrar).getAcceptableDescriptions(word);
612       Set<String> keySet = new HashSet<String>();
613       if (descriptions != null) {
614         for (OptionDescription description : descriptions) {
615           keySet.add(description.getPath());
616         }
617       }
618       keySetList.add(keySet);
619     }
620     return keySetList;
621   }
622
623   public static String processFilter(String filter, Set<String> quoted) {
624     String withoutQuoted = "";
625     int beg = 0;
626     final Matcher matcher = QUOTED.matcher(filter);
627     while (matcher.find()) {
628       final int start = matcher.start(1);
629       withoutQuoted += " " + filter.substring(beg, start);
630       beg = matcher.end(1);
631       final String trimmed = filter.substring(start, beg).trim();
632       if (trimmed.length() > 0) {
633         quoted.add(trimmed);
634       }
635     }
636     return withoutQuoted + " " + filter.substring(beg);
637   }
638
639   //to process event
640   public static class ConfigurableSearchTextField extends SearchTextFieldWithStoredHistory {
641     public ConfigurableSearchTextField() {
642       super("ALL_CONFIGURABLES_PANEL_SEARCH_HISTORY");
643     }
644
645     public void process(final KeyEvent e) {
646       ((TextFieldWithProcessing)getTextEditor()).processKeyEvent(e);
647     }
648   }
649
650   public static List<Configurable> expand(ConfigurableGroup[] groups) {
651     final ArrayList<Configurable> result = new ArrayList<Configurable>();
652     for (ConfigurableGroup eachGroup : groups) {
653       result.addAll(expandGroup(eachGroup));
654     }
655     return result;
656   }
657
658   public static List<Configurable> expandGroup(final ConfigurableGroup group) {
659     final Configurable[] configurables = group.getConfigurables();
660     ArrayList<Configurable> result = new ArrayList<Configurable>();
661     result.addAll(Arrays.asList(configurables));
662     for (Configurable each : configurables) {
663       addChildren(each, result);
664     }
665     return result;
666   }
667
668   private static void addChildren(Configurable configurable, ArrayList<Configurable> list) {
669     if (configurable instanceof Configurable.Composite) {
670       final Configurable[] kids = ((Configurable.Composite)configurable).getConfigurables();
671       for (Configurable eachKid : kids) {
672         list.add(eachKid);
673         addChildren(eachKid, list);
674       }
675     }
676   }
677
678 }