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            lsResultModel.setItems(menu.findMenuItems(edSearchText.getText(), true));
091        }
092    }
093
094    private static class CellRenderer implements ListCellRenderer<JMenuItem> {
095
096        @Override
097        public Component getListCellRendererComponent(JList<? extends JMenuItem> list, JMenuItem value, int index,
098                                                      boolean isSelected, boolean cellHasFocus) {
099            final JMenuItem item = new JMenuItem(value.getText());
100            item.setAction(value.getAction());
101            Optional.ofNullable(value.getAction())
102                    .filter(JosmAction.class::isInstance)
103                    .map(JosmAction.class::cast)
104                    .map(JosmAction::getShortcut)
105                    .map(Shortcut::getKeyStroke)
106                    .ifPresent(item::setAccelerator);
107            item.setArmed(isSelected);
108            if (isSelected) {
109                item.setBackground(list.getSelectionBackground());
110                item.setForeground(list.getSelectionForeground());
111            } else {
112                item.setBackground(list.getBackground());
113                item.setForeground(list.getForeground());
114            }
115            return item;
116        }
117    }
118
119    /**
120     * The action that opens the menu item search dialog.
121     */
122    public static class Action extends JosmAction {
123
124        // CHECKSTYLE.OFF: LineLength
125        /** Action shortcut (ctrl / space by default), made public in order to be used from {@code GettingStarted} page. */
126        public static final Shortcut SHORTCUT = Shortcut.registerShortcut("help:search-items", "Search menu items", KeyEvent.VK_SPACE, Shortcut.CTRL);
127        // CHECKSTYLE.ON: LineLength
128
129        /**
130         * Constructs a new {@code Action}.
131         */
132        public Action() {
133            super(tr("Search menu items"), "dialogs/search", null,
134                    SHORTCUT,
135                    true, "dialogs/search-items", false);
136            setHelpId(ht("Action/SearchMenuItems"));
137        }
138
139        @Override
140        public void actionPerformed(ActionEvent e) {
141            MenuItemSearchDialog.getInstance().showDialog();
142        }
143    }
144}