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