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.JList; 013import javax.swing.JMenuItem; 014import javax.swing.ListCellRenderer; 015 016import org.openstreetmap.josm.Main; 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(Main.parent, 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 } 039 040 /** 041 * Returns the unique instance of {@code MenuItemSearchDialog}. 042 * 043 * @return the unique instance of {@code MenuItemSearchDialog}. 044 */ 045 public static synchronized MenuItemSearchDialog getInstance() { 046 return INSTANCE; 047 } 048 049 @Override 050 public ExtendedDialog showDialog() { 051 selector.init(); 052 super.showDialog(); 053 selector.clearSelection(); 054 return this; 055 } 056 057 @Override 058 protected void buttonAction(int buttonIndex, ActionEvent evt) { 059 super.buttonAction(buttonIndex, evt); 060 if (buttonIndex == 0 && selector.getSelectedItem() != null && selector.getSelectedItem().isEnabled()) { 061 selector.getSelectedItem().getAction().actionPerformed(evt); 062 } 063 } 064 065 private static class MenuItemSelector extends SearchTextResultListPanel<JMenuItem> { 066 067 private final MainMenu menu; 068 069 MenuItemSelector(MainMenu menu) { 070 super(); 071 this.menu = menu; 072 lsResult.setCellRenderer(new CellRenderer()); 073 } 074 075 public JMenuItem getSelectedItem() { 076 final JMenuItem selected = lsResult.getSelectedValue(); 077 if (selected != null) { 078 return selected; 079 } else if (!lsResultModel.isEmpty()) { 080 return lsResultModel.getElementAt(0); 081 } else { 082 return null; 083 } 084 } 085 086 @Override 087 protected void filterItems() { 088 lsResultModel.setItems(menu.findMenuItems(edSearchText.getText(), true)); 089 } 090 } 091 092 private static class CellRenderer implements ListCellRenderer<JMenuItem> { 093 094 @Override 095 public Component getListCellRendererComponent(JList<? extends JMenuItem> list, JMenuItem value, int index, 096 boolean isSelected, boolean cellHasFocus) { 097 final JMenuItem item = new JMenuItem(value.getText()); 098 item.setAction(value.getAction()); 099 Optional.ofNullable(value.getAction()) 100 .filter(JosmAction.class::isInstance) 101 .map(JosmAction.class::cast) 102 .map(JosmAction::getShortcut) 103 .map(Shortcut::getKeyStroke) 104 .ifPresent(item::setAccelerator); 105 item.setArmed(isSelected); 106 if (isSelected) { 107 item.setBackground(list.getSelectionBackground()); 108 item.setForeground(list.getSelectionForeground()); 109 } else { 110 item.setBackground(list.getBackground()); 111 item.setForeground(list.getForeground()); 112 } 113 return item; 114 } 115 } 116 117 /** 118 * The action that opens the menu item search dialog. 119 */ 120 public static class Action extends JosmAction { 121 122 // CHECKSTYLE.OFF: LineLength 123 /** Action shortcut (ctrl / space by default), made public in order to be used from {@code GettingStarted} page. */ 124 public static final Shortcut SHORTCUT = Shortcut.registerShortcut("help:search-items", "Search menu items", KeyEvent.VK_SPACE, Shortcut.CTRL); 125 // CHECKSTYLE.ON: LineLength 126 127 /** 128 * Constructs a new {@code Action}. 129 */ 130 public Action() { 131 super(tr("Search menu items"), "dialogs/search", null, 132 SHORTCUT, 133 true, "dialogs/search-items", false); 134 } 135 136 @Override 137 public void actionPerformed(ActionEvent e) { 138 MenuItemSearchDialog.getInstance().showDialog(); 139 } 140 } 141}