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}