Index: trunk/src/org/openstreetmap/josm/gui/MainApplication.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 9346)
+++ trunk/src/org/openstreetmap/josm/gui/MainApplication.java	(revision 9347)
@@ -473,5 +473,4 @@
                 splash.dispose();
                 mainFrame.setVisible(true);
-                main.gettingStarted.requestFocusInWindow();
             }
         });
Index: trunk/src/org/openstreetmap/josm/gui/MainMenu.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/MainMenu.java	(revision 9346)
+++ trunk/src/org/openstreetmap/josm/gui/MainMenu.java	(revision 9347)
@@ -7,10 +7,6 @@
 
 import java.awt.Component;
-import java.awt.DefaultFocusTraversalPolicy;
-import java.awt.Dimension;
 import java.awt.GraphicsEnvironment;
-import java.awt.event.ActionEvent;
 import java.awt.event.KeyEvent;
-import java.awt.event.KeyListener;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -19,6 +15,4 @@
 import java.util.Map;
 
-import javax.swing.Action;
-import javax.swing.Box;
 import javax.swing.JCheckBoxMenuItem;
 import javax.swing.JMenu;
@@ -27,10 +21,5 @@
 import javax.swing.JPopupMenu;
 import javax.swing.JSeparator;
-import javax.swing.JTextField;
 import javax.swing.KeyStroke;
-import javax.swing.MenuElement;
-import javax.swing.MenuSelectionManager;
-import javax.swing.event.DocumentEvent;
-import javax.swing.event.DocumentListener;
 import javax.swing.event.MenuEvent;
 import javax.swing.event.MenuListener;
@@ -125,4 +114,5 @@
 import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent;
 import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
+import org.openstreetmap.josm.gui.dialogs.MenuItemSearchDialog;
 import org.openstreetmap.josm.gui.io.RecentlyOpenedFilesMenu;
 import org.openstreetmap.josm.gui.layer.Layer;
@@ -132,5 +122,4 @@
 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetSearchAction;
 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetSearchPrimitiveDialog;
-import org.openstreetmap.josm.gui.widgets.DisableShortcutsOnFocusGainedTextField;
 import org.openstreetmap.josm.tools.Shortcut;
 
@@ -404,9 +393,4 @@
     public FullscreenToggleAction fullscreenToggleAction;
 
-    /**
-     * Popup menu to display menu items search result.
-     */
-    private final JPopupMenu searchResultsMenu = new JPopupMenu();
-
     /** this menu listener hides unnecessary JSeparators in a menu list but does not remove them.
      * If at a later time the separators are required, they will be made visible again. Intended
@@ -441,5 +425,5 @@
 
     /**
-     * @return the default position of tnew top-level menus
+     * @return the default position of new top-level menus
      * @since 6088
      */
@@ -818,4 +802,6 @@
         });
 
+        add(helpMenu, new MenuItemSearchDialog.Action());
+        helpMenu.addSeparator();
         add(helpMenu, statusreport);
         add(helpMenu, reportbug);
@@ -824,16 +810,4 @@
         add(helpMenu, help);
         add(helpMenu, about);
-        add(Box.createHorizontalGlue());
-        final DisableShortcutsOnFocusGainedTextField searchField = createSearchField();
-        add(searchField);
-
-        // Do not let search field take the focus automatically
-        setFocusTraversalPolicyProvider(true);
-        setFocusTraversalPolicy(new DefaultFocusTraversalPolicy() {
-            @Override
-            protected boolean accept(Component aComponent) {
-                return super.accept(aComponent) && !searchField.equals(aComponent);
-            }
-        });
 
         windowMenu.addMenuListener(menuSeparatorHandler);
@@ -854,49 +828,17 @@
 
     /**
-     * Create search field.
-     * @return the search field
-     */
-    private DisableShortcutsOnFocusGainedTextField createSearchField() {
-        DisableShortcutsOnFocusGainedTextField searchField = new DisableShortcutsOnFocusGainedTextField() {
-            @Override
-            public Dimension getPreferredSize() {
-                // JMenuBar uses a BoxLayout and it doesn't seem possible to specify a size factor,
-                // so compute the preferred size dynamically
-                return new Dimension(Math.min(200, Math.max(25, getMaximumAvailableWidth())),
-                        helpMenu.getPreferredSize().height);
-            }
-        };
-        Shortcut searchFieldShortcut = Shortcut.registerShortcut("menu:search-field", tr("Search menu items"), KeyEvent.VK_R, Shortcut.MNEMONIC);
-        searchFieldShortcut.setFocusAccelerator(searchField);
-        searchField.setEditable(true);
-        searchField.setMaximumSize(new Dimension(200, helpMenu.getPreferredSize().height));
-        searchField.setHint(tr("Search menu items"));
-        searchField.setToolTipText(Main.platform.makeTooltip(tr("Search menu items"), searchFieldShortcut));
-        searchField.addKeyListener(new SearchFieldKeyListener());
-        searchField.getDocument().addDocumentListener(new SearchFieldTextListener(this, searchField));
-        return searchField;
-    }
-
-    /**
      * Search main menu for items with {@code textToFind} in title.
      * @param textToFind The text to find
+     * @param skipPresets whether to skip the {@link #presetsMenu} in the search
      * @return not null list of found menu items.
      */
-    private List<JMenuItem> findMenuItems(String textToFind) {
-        // Explicitely use default locale in this case, because we're looking for translated strings
+    public List<JMenuItem> findMenuItems(String textToFind, boolean skipPresets) {
+        // Explicitly use default locale in this case, because we're looking for translated strings
         textToFind = textToFind.toLowerCase(Locale.getDefault());
         List<JMenuItem> result = new ArrayList<>();
-
-        // Iterate over main menus
-        for (MenuElement menuElement : getSubElements()) {
-            if (!(menuElement instanceof JMenu)) continue;
-
-            JMenu mainMenuItem = (JMenu) menuElement;
-            if (mainMenuItem.getAction() != null && mainMenuItem.getText().toLowerCase(Locale.getDefault()).contains(textToFind)) {
-                result.add(mainMenuItem);
+        for (int i = 0; i < getMenuCount(); i++) {
+            if (getMenu(i) != null && (!skipPresets || presetsMenu != getMenu(i))) {
+                findMenuItems(getMenu(i), textToFind, result);
             }
-
-            //Search recursively
-            findMenuItems(mainMenuItem, textToFind, result);
         }
         return result;
@@ -908,5 +850,5 @@
      * @param menu menu in which search will be performed
      * @param textToFind The text to find
-     * @param result resulting list ofmenu items
+     * @param result resulting list of menu items
      */
     private static void findMenuItems(final JMenu menu, final String textToFind, final List<JMenuItem> result) {
@@ -915,5 +857,5 @@
             if (menuItem == null) continue;
 
-            // Explicitely use default locale in this case, because we're looking for translated strings
+            // Explicitly use default locale in this case, because we're looking for translated strings
             if (menuItem.getAction() != null && menuItem.getText().toLowerCase(Locale.getDefault()).contains(textToFind)) {
                 result.add(menuItem);
@@ -977,109 +919,3 @@
     }
 
-    /**
-     * This listener is designed to handle ENTER key pressed in menu search field.
-     * When user presses Enter key then selected item of "searchResultsMenu" is triggered.
-     */
-    private static class SearchFieldKeyListener implements KeyListener {
-
-        @Override
-        public void keyPressed(KeyEvent e) {
-            if (e.getKeyCode() == KeyEvent.VK_ENTER) {
-                // On ENTER selected menu item must be triggered
-                MenuElement[] selection = MenuSelectionManager.defaultManager().getSelectedPath();
-                if (selection.length > 1) {
-                    MenuElement selectedElement = selection[selection.length-1];
-                    if (selectedElement instanceof JMenuItem) {
-                        JMenuItem selectedItem = (JMenuItem) selectedElement;
-                        Action menuAction = selectedItem.getAction();
-                        menuAction.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, null));
-                        if (Main.isDebugEnabled()) {
-                            Main.debug(getClass().getName()+" consuming event "+e);
-                        }
-                        e.consume();
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void keyTyped(KeyEvent e) {
-            // Not used
-        }
-
-        @Override
-        public void keyReleased(KeyEvent e) {
-            // Not used
-        }
-    }
-
-    private class SearchFieldTextListener implements DocumentListener {
-        private final JTextField searchField;
-        private final MainMenu mainMenu;
-        private String currentSearchText;
-
-        SearchFieldTextListener(MainMenu mainMenu, JTextField searchField) {
-            this.mainMenu = mainMenu;
-            this.searchField = searchField;
-        }
-
-        @Override
-        public void insertUpdate(DocumentEvent e) {
-            doSearch(searchField.getText());
-        }
-
-        @Override
-        public void removeUpdate(DocumentEvent e) {
-            doSearch(searchField.getText());
-        }
-
-        @Override
-        public void changedUpdate(DocumentEvent e) {
-            doSearch(searchField.getText());
-        }
-
-        //TODO: perform some delay (maybe 200 ms) before actual searching.
-        void doSearch(String searchTerm) {
-            // Explicitely use default locale in this case, because we're looking for translated strings
-            searchTerm = searchTerm.trim().toLowerCase(Locale.getDefault());
-
-            if (searchTerm.equals(currentSearchText)) {
-                return;
-            }
-            currentSearchText = searchTerm;
-            if (searchTerm.isEmpty()) {
-                // No text to search
-                hideMenu();
-                return;
-            }
-
-            List<JMenuItem> searchResult = mainMenu.findMenuItems(currentSearchText);
-            if (searchResult.isEmpty()) {
-                // Nothing found
-                hideMenu();
-                return;
-            }
-
-            if (searchResult.size() > 20) {
-                // Too many items found...
-                searchResult = searchResult.subList(0, 20);
-            }
-
-            // Update Popup menu
-            searchResultsMenu.removeAll();
-            for (JMenuItem foundItem : searchResult) {
-                searchResultsMenu.add(foundItem.getText()).setAction(foundItem.getAction());
-            }
-            // Put menu right under search field
-            searchResultsMenu.pack();
-            searchResultsMenu.show(mainMenu, searchField.getX(), searchField.getY() + searchField.getHeight());
-
-            // This is tricky. User still is able to edit search text. While Up and Down keys are handled by Popup Menu.
-            searchField.requestFocusInWindow();
-        }
-
-        private void hideMenu() {
-            searchResultsMenu.setVisible(false);
-        }
-    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/dialogs/MenuItemSearchDialog.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/dialogs/MenuItemSearchDialog.java	(revision 9347)
+++ trunk/src/org/openstreetmap/josm/gui/dialogs/MenuItemSearchDialog.java	(revision 9347)
@@ -0,0 +1,127 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.dialogs;
+
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.actions.JosmAction;
+import org.openstreetmap.josm.gui.ExtendedDialog;
+import org.openstreetmap.josm.gui.MainMenu;
+import org.openstreetmap.josm.gui.widgets.SearchTextResultListPanel;
+import org.openstreetmap.josm.tools.Shortcut;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyEvent;
+
+import static org.openstreetmap.josm.tools.I18n.tr;
+
+public class MenuItemSearchDialog extends ExtendedDialog {
+
+    private final Selector selector;
+    private static final MenuItemSearchDialog INSTANCE = new MenuItemSearchDialog(Main.main.menu);
+
+    private MenuItemSearchDialog(MainMenu menu) {
+        super(Main.parent, tr("Search menu items"), new String[]{tr("Select"), tr("Cancel")});
+        this.selector = new Selector(menu);
+        this.selector.setDblClickListener(new ActionListener() {
+            @Override
+            public void actionPerformed(ActionEvent e) {
+                buttonAction(0, null);
+            }
+        });
+        setContent(selector);
+        setPreferredSize(new Dimension(600, 300));
+    }
+
+    /**
+     * Returns the unique instance of {@code MenuItemSearchDialog}.
+     *
+     * @return the unique instance of {@code MenuItemSearchDialog}.
+     */
+    public static synchronized MenuItemSearchDialog getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public ExtendedDialog showDialog() {
+        selector.init();
+        super.showDialog();
+        selector.clearSelection();
+        return this;
+    }
+
+    @Override
+    protected void buttonAction(int buttonIndex, ActionEvent evt) {
+        super.buttonAction(buttonIndex, evt);
+        if (buttonIndex == 0 && selector.getSelectedItem() != null && selector.getSelectedItem().isEnabled()) {
+            selector.getSelectedItem().getAction().actionPerformed(evt);
+        }
+    }
+
+    private static class Selector extends SearchTextResultListPanel<JMenuItem> {
+
+        private final MainMenu menu;
+
+        public Selector(MainMenu menu) {
+            super();
+            this.menu = menu;
+            lsResult.setCellRenderer(new CellRenderer());
+            lsResult.setSelectionModel(new DefaultListSelectionModel() {
+
+            });
+        }
+
+        public JMenuItem getSelectedItem() {
+            final JMenuItem selected = lsResult.getSelectedValue();
+            if (selected != null) {
+                return selected;
+            } else if (!lsResultModel.isEmpty()) {
+                return lsResultModel.getElementAt(0);
+            } else {
+                return null;
+            }
+        }
+
+        @Override
+        protected void filterItems() {
+            lsResultModel.setItems(menu.findMenuItems(edSearchText.getText(), true));
+        }
+    }
+
+    private static class CellRenderer implements ListCellRenderer<JMenuItem> {
+
+        private final DefaultListCellRenderer def = new DefaultListCellRenderer();
+
+        @Override
+        public Component getListCellRendererComponent(JList<? extends JMenuItem> list, JMenuItem value, int index, boolean isSelected, boolean cellHasFocus) {
+            final JLabel label = (JLabel) def.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+            label.setText(value.getText());
+            label.setIcon(value.getIcon());
+            label.setEnabled(value.isEnabled());
+            final JMenuItem item = new JMenuItem(value.getText());
+            item.setAction(value.getAction());
+            if (isSelected) {
+                item.setBackground(list.getSelectionBackground());
+                item.setForeground(list.getSelectionForeground());
+            } else {
+                item.setBackground(list.getBackground());
+                item.setForeground(list.getForeground());
+            }
+            return item;
+        }
+    }
+
+    public static class Action extends JosmAction {
+
+        public Action() {
+            super(tr("Search menu items"), "dialogs/search", null,
+                    Shortcut.registerShortcut("help:search-items", "Search menu items", KeyEvent.VK_SPACE, Shortcut.CTRL), false);
+        }
+
+        @Override
+        public void actionPerformed(ActionEvent e) {
+            MenuItemSearchDialog.getInstance().showDialog();
+        }
+    }
+}
Index: trunk/src/org/openstreetmap/josm/gui/tagging/presets/TaggingPresetSelector.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/tagging/presets/TaggingPresetSelector.java	(revision 9346)
+++ trunk/src/org/openstreetmap/josm/gui/tagging/presets/TaggingPresetSelector.java	(revision 9347)
@@ -8,11 +8,6 @@
 import java.awt.Dimension;
 import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
 import java.awt.event.ItemEvent;
 import java.awt.event.ItemListener;
-import java.awt.event.KeyAdapter;
-import java.awt.event.KeyEvent;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -27,5 +22,4 @@
 
 import javax.swing.AbstractAction;
-import javax.swing.AbstractListModel;
 import javax.swing.Action;
 import javax.swing.BoxLayout;
@@ -37,8 +31,5 @@
 import javax.swing.JPanel;
 import javax.swing.JPopupMenu;
-import javax.swing.JScrollPane;
 import javax.swing.ListCellRenderer;
-import javax.swing.event.DocumentEvent;
-import javax.swing.event.DocumentListener;
 import javax.swing.event.ListSelectionEvent;
 import javax.swing.event.ListSelectionListener;
@@ -54,6 +45,6 @@
 import org.openstreetmap.josm.gui.tagging.presets.items.Roles;
 import org.openstreetmap.josm.gui.tagging.presets.items.Roles.Role;
-import org.openstreetmap.josm.gui.widgets.JosmTextField;
 import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
+import org.openstreetmap.josm.gui.widgets.SearchTextResultListPanel;
 import org.openstreetmap.josm.tools.Predicate;
 import org.openstreetmap.josm.tools.Utils;
@@ -63,5 +54,5 @@
  * @since 6068
  */
-public class TaggingPresetSelector extends JPanel implements SelectionChangedListener {
+public class TaggingPresetSelector extends SearchTextResultListPanel<TaggingPreset> implements SelectionChangedListener {
 
     private static final int CLASSIFICATION_IN_FAVORITES = 300;
@@ -73,6 +64,4 @@
     private static final BooleanProperty ONLY_APPLICABLE  = new BooleanProperty("taggingpreset.dialog.only-applicable-to-selection", true);
 
-    private final JosmTextField edSearchText;
-    private final JList<TaggingPreset> lsResult;
     private final JCheckBox ckOnlyApplicable;
     private final JCheckBox ckSearchInTags;
@@ -80,10 +69,4 @@
     private boolean typesInSelectionDirty = true;
     private final transient PresetClassifications classifications = new PresetClassifications();
-    private final ResultListModel lsResultModel = new ResultListModel();
-
-    private final transient List<ListSelectionListener> listSelectionListeners = new ArrayList<>();
-
-    private transient ActionListener dblClickListener;
-    private transient ActionListener clickListener;
 
     private static class ResultListCellRenderer implements ListCellRenderer<TaggingPreset> {
@@ -96,28 +79,4 @@
             result.setIcon((Icon) tp.getValue(Action.SMALL_ICON));
             return result;
-        }
-    }
-
-    private static class ResultListModel extends AbstractListModel<TaggingPreset> {
-
-        private transient List<PresetClassification> presets = new ArrayList<>();
-
-        public synchronized void setPresets(List<PresetClassification> presets) {
-            this.presets = presets;
-            fireContentsChanged(this, 0, Integer.MAX_VALUE);
-        }
-
-        @Override
-        public synchronized TaggingPreset getElementAt(int index) {
-            return presets.get(index).preset;
-        }
-
-        @Override
-        public synchronized int getSize() {
-            return presets.size();
-        }
-
-        public synchronized boolean isEmpty() {
-            return presets.isEmpty();
         }
     }
@@ -219,66 +178,7 @@
      */
     public TaggingPresetSelector(boolean displayOnlyApplicable, boolean displaySearchInTags) {
-        super(new BorderLayout());
+        super();
+        lsResult.setCellRenderer(new ResultListCellRenderer());
         classifications.loadPresets(TaggingPresets.getTaggingPresets());
-
-        edSearchText = new JosmTextField();
-        edSearchText.getDocument().addDocumentListener(new DocumentListener() {
-            @Override
-            public void removeUpdate(DocumentEvent e) {
-                filterPresets();
-            }
-
-            @Override
-            public void insertUpdate(DocumentEvent e) {
-                filterPresets();
-            }
-
-            @Override
-            public void changedUpdate(DocumentEvent e) {
-                filterPresets();
-            }
-        });
-        edSearchText.addKeyListener(new KeyAdapter() {
-            @Override
-            public void keyPressed(KeyEvent e) {
-                switch (e.getKeyCode()) {
-                case KeyEvent.VK_DOWN:
-                    selectPreset(lsResult.getSelectedIndex() + 1);
-                    break;
-                case KeyEvent.VK_UP:
-                    selectPreset(lsResult.getSelectedIndex() - 1);
-                    break;
-                case KeyEvent.VK_PAGE_DOWN:
-                    selectPreset(lsResult.getSelectedIndex() + 10);
-                    break;
-                case KeyEvent.VK_PAGE_UP:
-                    selectPreset(lsResult.getSelectedIndex() - 10);
-                    break;
-                case KeyEvent.VK_HOME:
-                    selectPreset(0);
-                    break;
-                case KeyEvent.VK_END:
-                    selectPreset(lsResultModel.getSize());
-                    break;
-                }
-            }
-        });
-        add(edSearchText, BorderLayout.NORTH);
-
-        lsResult = new JList<>(lsResultModel);
-        lsResult.setCellRenderer(new ResultListCellRenderer());
-        lsResult.addMouseListener(new MouseAdapter() {
-            @Override
-            public void mouseClicked(MouseEvent e) {
-                if (e.getClickCount() > 1) {
-                    if (dblClickListener != null)
-                        dblClickListener.actionPerformed(null);
-                } else {
-                    if (clickListener != null)
-                        clickListener.actionPerformed(null);
-                }
-            }
-        });
-        add(new JScrollPane(lsResult), BorderLayout.CENTER);
 
         JPanel pnChecks = new JPanel();
@@ -292,5 +192,5 @@
                 @Override
                 public void itemStateChanged(ItemEvent e) {
-                    filterPresets();
+                    filterItems();
                 }
             });
@@ -306,5 +206,5 @@
                 @Override
                 public void itemStateChanged(ItemEvent e) {
-                    filterPresets();
+                    filterItems();
                 }
             });
@@ -317,5 +217,5 @@
 
         setPreferredSize(new Dimension(400, 300));
-        filterPresets();
+        filterItems();
         JPopupMenu popupMenu = new JPopupMenu();
         popupMenu.add(new AbstractAction(tr("Add toolbar button")) {
@@ -331,19 +231,9 @@
     }
 
-    private synchronized void selectPreset(int newIndex) {
-        if (newIndex < 0) {
-            newIndex = 0;
-        }
-        if (newIndex > lsResultModel.getSize() - 1) {
-            newIndex = lsResultModel.getSize() - 1;
-        }
-        lsResult.setSelectedIndex(newIndex);
-        lsResult.ensureIndexIsVisible(newIndex);
-    }
-
     /**
      * Search expression can be in form: "group1/group2/name" where names can contain multiple words
      */
-    private synchronized void filterPresets() {
+    @Override
+    protected synchronized void filterItems() {
         //TODO Save favorites to file
         String text = edSearchText.getText().toLowerCase(Locale.ENGLISH);
@@ -357,5 +247,10 @@
 
         final TaggingPreset oldPreset = lsResult.getSelectedValue();
-        lsResultModel.setPresets(result);
+        lsResultModel.setItems(Utils.transform(result, new Utils.Function<PresetClassification, TaggingPreset>() {
+            @Override
+            public TaggingPreset apply(PresetClassification x) {
+                return x.preset;
+            }
+        }));
         final TaggingPreset newPreset = lsResult.getSelectedValue();
         if (!Objects.equals(oldPreset, newPreset)) {
@@ -491,4 +386,5 @@
     }
 
+    @Override
     public synchronized void init() {
         if (ckOnlyApplicable != null) {
@@ -496,7 +392,5 @@
             ckOnlyApplicable.setSelected(!getTypesInSelection().isEmpty() && ONLY_APPLICABLE.get());
         }
-        listSelectionListeners.clear();
-        edSearchText.setText("");
-        filterPresets();
+        super.init();
     }
 
@@ -505,8 +399,4 @@
         classifications.loadPresets(presets);
         init();
-    }
-
-    public synchronized void clearSelection() {
-        lsResult.getSelectionModel().clearSelection();
     }
 
@@ -547,35 +437,3 @@
         lsResult.setSelectedValue(p, true);
     }
-
-    public synchronized int getItemCount() {
-        return lsResultModel.getSize();
-    }
-
-    public void setDblClickListener(ActionListener dblClickListener) {
-        this.dblClickListener = dblClickListener;
-    }
-
-    public void setClickListener(ActionListener clickListener) {
-        this.clickListener = clickListener;
-    }
-
-    /**
-     * Adds a selection listener to the presets list.
-     * @param selectListener The list selection listener
-     * @since 7412
-     */
-    public synchronized void addSelectionListener(ListSelectionListener selectListener) {
-        lsResult.getSelectionModel().addListSelectionListener(selectListener);
-        listSelectionListeners.add(selectListener);
-    }
-
-    /**
-     * Removes a selection listener from the presets list.
-     * @param selectListener The list selection listener
-     * @since 7412
-     */
-    public synchronized void removeSelectionListener(ListSelectionListener selectListener) {
-        listSelectionListeners.remove(selectListener);
-        lsResult.getSelectionModel().removeListSelectionListener(selectListener);
-    }
 }
Index: trunk/src/org/openstreetmap/josm/gui/widgets/SearchTextResultListPanel.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/widgets/SearchTextResultListPanel.java	(revision 9347)
+++ trunk/src/org/openstreetmap/josm/gui/widgets/SearchTextResultListPanel.java	(revision 9347)
@@ -0,0 +1,171 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.widgets;
+
+import javax.swing.*;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import javax.swing.event.ListSelectionListener;
+import java.awt.*;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.*;
+import java.util.List;
+
+public abstract class SearchTextResultListPanel<T> extends JPanel {
+
+    protected final JosmTextField edSearchText;
+    protected final JList<T> lsResult;
+    protected final ResultListModel<T> lsResultModel = new ResultListModel<>();
+
+    protected final transient List<ListSelectionListener> listSelectionListeners = new ArrayList<>();
+
+    private transient ActionListener dblClickListener;
+    private transient ActionListener clickListener;
+
+    protected abstract void filterItems();
+
+    public SearchTextResultListPanel() {
+        super(new BorderLayout());
+
+        edSearchText = new JosmTextField();
+        edSearchText.getDocument().addDocumentListener(new DocumentListener() {
+            @Override
+            public void removeUpdate(DocumentEvent e) {
+                filterItems();
+            }
+
+            @Override
+            public void insertUpdate(DocumentEvent e) {
+                filterItems();
+            }
+
+            @Override
+            public void changedUpdate(DocumentEvent e) {
+                filterItems();
+            }
+        });
+        edSearchText.addKeyListener(new KeyAdapter() {
+            @Override
+            public void keyPressed(KeyEvent e) {
+                switch (e.getKeyCode()) {
+                    case KeyEvent.VK_DOWN:
+                        selectItem(lsResult.getSelectedIndex() + 1);
+                        break;
+                    case KeyEvent.VK_UP:
+                        selectItem(lsResult.getSelectedIndex() - 1);
+                        break;
+                    case KeyEvent.VK_PAGE_DOWN:
+                        selectItem(lsResult.getSelectedIndex() + 10);
+                        break;
+                    case KeyEvent.VK_PAGE_UP:
+                        selectItem(lsResult.getSelectedIndex() - 10);
+                        break;
+                    case KeyEvent.VK_HOME:
+                        selectItem(0);
+                        break;
+                    case KeyEvent.VK_END:
+                        selectItem(lsResultModel.getSize());
+                        break;
+                }
+            }
+        });
+        add(edSearchText, BorderLayout.NORTH);
+
+        lsResult = new JList<>(lsResultModel);
+        lsResult.addMouseListener(new MouseAdapter() {
+            @Override
+            public void mouseClicked(MouseEvent e) {
+                if (e.getClickCount() > 1) {
+                    if (dblClickListener != null)
+                        dblClickListener.actionPerformed(null);
+                } else {
+                    if (clickListener != null)
+                        clickListener.actionPerformed(null);
+                }
+            }
+        });
+        add(new JScrollPane(lsResult), BorderLayout.CENTER);
+    }
+
+    protected static class ResultListModel<T> extends AbstractListModel<T> {
+
+        private transient List<T> items = new ArrayList<>();
+
+        public synchronized void setItems(List<T> items) {
+            this.items = items;
+            fireContentsChanged(this, 0, Integer.MAX_VALUE);
+        }
+
+        @Override
+        public synchronized T getElementAt(int index) {
+            return items.get(index);
+        }
+
+        @Override
+        public synchronized int getSize() {
+            return items.size();
+        }
+
+        public synchronized boolean isEmpty() {
+            return items.isEmpty();
+        }
+    }
+
+    public synchronized void init() {
+        listSelectionListeners.clear();
+        edSearchText.setText("");
+        filterItems();
+    }
+
+    private synchronized void selectItem(int newIndex) {
+        if (newIndex < 0) {
+            newIndex = 0;
+        }
+        if (newIndex > lsResultModel.getSize() - 1) {
+            newIndex = lsResultModel.getSize() - 1;
+        }
+        lsResult.setSelectedIndex(newIndex);
+        lsResult.ensureIndexIsVisible(newIndex);
+    }
+
+    public synchronized void clearSelection() {
+        lsResult.clearSelection();
+    }
+
+    public synchronized int getItemCount() {
+        return lsResultModel.getSize();
+    }
+
+    public void setDblClickListener(ActionListener dblClickListener) {
+        this.dblClickListener = dblClickListener;
+    }
+
+    public void setClickListener(ActionListener clickListener) {
+        this.clickListener = clickListener;
+    }
+
+    /**
+     * Adds a selection listener to the presets list.
+     *
+     * @param selectListener The list selection listener
+     * @since 7412
+     */
+    public synchronized void addSelectionListener(ListSelectionListener selectListener) {
+        lsResult.getSelectionModel().addListSelectionListener(selectListener);
+        listSelectionListeners.add(selectListener);
+    }
+
+    /**
+     * Removes a selection listener from the presets list.
+     *
+     * @param selectListener The list selection listener
+     * @since 7412
+     */
+    public synchronized void removeSelectionListener(ListSelectionListener selectListener) {
+        listSelectionListeners.remove(selectListener);
+        lsResult.getSelectionModel().removeListSelectionListener(selectListener);
+    }
+}
