001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.dialogs;
003
004import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
005import static org.openstreetmap.josm.tools.I18n.tr;
006
007import java.awt.Component;
008import java.awt.Dimension;
009import java.awt.event.ActionEvent;
010import java.awt.event.KeyEvent;
011import java.util.Optional;
012
013import javax.swing.JList;
014import javax.swing.JMenuItem;
015import javax.swing.ListCellRenderer;
016
017import org.openstreetmap.josm.actions.JosmAction;
018import org.openstreetmap.josm.gui.ExtendedDialog;
019import org.openstreetmap.josm.gui.MainApplication;
020import org.openstreetmap.josm.gui.MainMenu;
021import org.openstreetmap.josm.gui.widgets.SearchTextResultListPanel;
022import org.openstreetmap.josm.tools.Shortcut;
023
024/**
025 * A dialog that allows you to search for a menu item. The user can input part of the menu item name.
026 */
027public final class MenuItemSearchDialog extends ExtendedDialog {
028
029    private final MenuItemSelector selector;
030    private static final MenuItemSearchDialog INSTANCE = new MenuItemSearchDialog(MainApplication.getMenu());
031
032    private MenuItemSearchDialog(MainMenu menu) {
033        super(MainApplication.getMainFrame(), tr("Search menu items"), tr("Select"), tr("Cancel"));
034        this.selector = new MenuItemSelector(menu);
035        this.selector.setDblClickListener(e -> buttonAction(0, null));
036        setContent(selector, false);
037        setPreferredSize(new Dimension(600, 300));
038        setButtonIcons("ok", "cancel");
039        configureContextsensitiveHelp(ht("Action/SearchMenuItems"), true);
040    }
041
042    /**
043     * Returns the unique instance of {@code MenuItemSearchDialog}.
044     *
045     * @return the unique instance of {@code MenuItemSearchDialog}.
046     */
047    public static synchronized MenuItemSearchDialog getInstance() {
048        return INSTANCE;
049    }
050
051    @Override
052    public ExtendedDialog showDialog() {
053        selector.init();
054        super.showDialog();
055        selector.clearSelection();
056        return this;
057    }
058
059    @Override
060    protected void buttonAction(int buttonIndex, ActionEvent evt) {
061        super.buttonAction(buttonIndex, evt);
062        if (buttonIndex == 0 && selector.getSelectedItem() != null && selector.getSelectedItem().isEnabled()) {
063            selector.getSelectedItem().getAction().actionPerformed(evt);
064        }
065    }
066
067    private static class MenuItemSelector extends SearchTextResultListPanel<JMenuItem> {
068
069        private final MainMenu menu;
070
071        MenuItemSelector(MainMenu menu) {
072            super();
073            this.menu = menu;
074            lsResult.setCellRenderer(new CellRenderer());
075        }
076
077        public JMenuItem getSelectedItem() {
078            final JMenuItem selected = lsResult.getSelectedValue();
079            if (selected != null) {
080                return selected;
081            } else if (!lsResultModel.isEmpty()) {
082                return lsResultModel.getElementAt(0);
083            } else {
084                return null;
085            }
086        }
087
088        @Override
089        protected void filterItems() {
090            menu.imageryMenu.refreshImageryMenu();
091            lsResultModel.setItems(menu.findMenuItems(edSearchText.getText(), true));
092        }
093    }
094
095    private static class CellRenderer implements ListCellRenderer<JMenuItem> {
096
097        @Override
098        public Component getListCellRendererComponent(JList<? extends JMenuItem> list, JMenuItem value, int index,
099                                                      boolean isSelected, boolean cellHasFocus) {
100            final JMenuItem item = new JMenuItem(value.getText());
101            item.setAction(value.getAction());
102            Optional.ofNullable(value.getAction())
103                    .filter(JosmAction.class::isInstance)
104                    .map(JosmAction.class::cast)
105                    .map(JosmAction::getShortcut)
106                    .map(Shortcut::getKeyStroke)
107                    .ifPresent(item::setAccelerator);
108            item.setArmed(isSelected);
109            if (isSelected) {
110                item.setBackground(list.getSelectionBackground());
111                item.setForeground(list.getSelectionForeground());
112            } else {
113                item.setBackground(list.getBackground());
114                item.setForeground(list.getForeground());
115            }
116            return item;
117        }
118    }
119
120    /**
121     * The action that opens the menu item search dialog.
122     */
123    public static class Action extends JosmAction {
124
125        // CHECKSTYLE.OFF: LineLength
126        /** Action shortcut (ctrl / space by default), made public in order to be used from {@code GettingStarted} page. */
127        public static final Shortcut SHORTCUT = Shortcut.registerShortcut("help:search-items", tr("Help: {0}", tr("Search menu items")), KeyEvent.VK_SPACE, Shortcut.CTRL);
128        // CHECKSTYLE.ON: LineLength
129
130        /**
131         * Constructs a new {@code Action}.
132         */
133        public Action() {
134            super(tr("Search menu items"), "dialogs/search", null,
135                    SHORTCUT,
136                    true, "dialogs/search-items", false);
137            setHelpId(ht("Action/SearchMenuItems"));
138        }
139
140        @Override
141        public void actionPerformed(ActionEvent e) {
142            MenuItemSearchDialog.getInstance().showDialog();
143        }
144    }
145}