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}