Ticket #12224: 12224.patch
| File 12224.patch, 35.9 KB (added by , 10 years ago) |
|---|
-
src/org/openstreetmap/josm/gui/MainApplication.java
commit c12fc123f2111694e58c8a8a36320dbe38b0b15b Author: Simon Legner <Simon.Legner@gmail.com> Date: Wed Dec 23 16:25:33 2015 +0100 see #12224 - Dialog for "Search menu items" diff --git a/src/org/openstreetmap/josm/gui/MainApplication.java b/src/org/openstreetmap/josm/gui/MainApplication.java index 5198e66..64c6842 100644a b public class MainApplication extends Main { 471 471 splash.setVisible(false); 472 472 splash.dispose(); 473 473 mainFrame.setVisible(true); 474 main.gettingStarted.requestFocusInWindow();475 474 } 476 475 }); 477 476 -
src/org/openstreetmap/josm/gui/MainMenu.java
diff --git a/src/org/openstreetmap/josm/gui/MainMenu.java b/src/org/openstreetmap/josm/gui/MainMenu.java index f996248..82ab5b7 100644
a b import static org.openstreetmap.josm.tools.I18n.tr; 6 6 import static org.openstreetmap.josm.tools.I18n.trc; 7 7 8 8 import java.awt.Component; 9 import java.awt.DefaultFocusTraversalPolicy;10 import java.awt.Dimension;11 9 import java.awt.GraphicsEnvironment; 12 import java.awt.event.ActionEvent;13 10 import java.awt.event.KeyEvent; 14 import java.awt.event.KeyListener;15 11 import java.util.ArrayList; 16 12 import java.util.HashMap; 17 13 import java.util.List; 18 14 import java.util.Locale; 19 15 import java.util.Map; 20 16 21 import javax.swing.Action;22 import javax.swing.Box;23 17 import javax.swing.JCheckBoxMenuItem; 24 18 import javax.swing.JMenu; 25 19 import javax.swing.JMenuBar; 26 20 import javax.swing.JMenuItem; 27 21 import javax.swing.JPopupMenu; 28 22 import javax.swing.JSeparator; 29 import javax.swing.JTextField;30 23 import javax.swing.KeyStroke; 31 import javax.swing.MenuElement;32 import javax.swing.MenuSelectionManager;33 import javax.swing.event.DocumentEvent;34 import javax.swing.event.DocumentListener;35 24 import javax.swing.event.MenuEvent; 36 25 import javax.swing.event.MenuListener; 37 26 … … import org.openstreetmap.josm.actions.audio.AudioSlowerAction; 124 113 import org.openstreetmap.josm.actions.search.SearchAction; 125 114 import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent; 126 115 import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener; 116 import org.openstreetmap.josm.gui.dialogs.MenuItemSearchDialog; 127 117 import org.openstreetmap.josm.gui.io.RecentlyOpenedFilesMenu; 128 118 import org.openstreetmap.josm.gui.layer.Layer; 129 119 import org.openstreetmap.josm.gui.mappaint.MapPaintMenu; … … import org.openstreetmap.josm.gui.preferences.imagery.ImageryPreference; 131 121 import org.openstreetmap.josm.gui.preferences.map.TaggingPresetPreference; 132 122 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetSearchAction; 133 123 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetSearchPrimitiveDialog; 134 import org.openstreetmap.josm.gui.widgets.DisableShortcutsOnFocusGainedTextField;135 124 import org.openstreetmap.josm.tools.Shortcut; 136 125 137 126 /** … … public class MainMenu extends JMenuBar { 403 392 public final DialogsToggleAction dialogsToggleAction = new DialogsToggleAction(); 404 393 public FullscreenToggleAction fullscreenToggleAction; 405 394 406 /**407 * Popup menu to display menu items search result.408 */409 private final JPopupMenu searchResultsMenu = new JPopupMenu();410 411 395 /** this menu listener hides unnecessary JSeparators in a menu list but does not remove them. 412 396 * If at a later time the separators are required, they will be made visible again. Intended 413 397 * usage is make menus not look broken if separators are used to group the menu and some of … … public class MainMenu extends JMenuBar { 440 424 }; 441 425 442 426 /** 443 * @return the default position of tnew top-level menus427 * @return the default position of new top-level menus 444 428 * @since 6088 445 429 */ 446 430 public int getDefaultMenuPos() { … … public class MainMenu extends JMenuBar { 809 793 } 810 794 }); 811 795 796 helpMenu.add(new MenuItemSearchDialog.Action()); 797 helpMenu.addSeparator(); 812 798 helpMenu.add(statusreport); 813 799 helpMenu.add(reportbug); 814 800 helpMenu.addSeparator(); … … public class MainMenu extends JMenuBar { 817 803 helpMenu.add(help).setAccelerator(Shortcut.registerShortcut("system:help", tr("Help"), KeyEvent.VK_F1, 818 804 Shortcut.DIRECT).getKeyStroke()); 819 805 add(helpMenu, about); 820 add(Box.createHorizontalGlue());821 final DisableShortcutsOnFocusGainedTextField searchField = createSearchField();822 add(searchField);823 824 // Do not let search field take the focus automatically825 setFocusTraversalPolicyProvider(true);826 setFocusTraversalPolicy(new DefaultFocusTraversalPolicy() {827 @Override828 protected boolean accept(Component aComponent) {829 return super.accept(aComponent) && !searchField.equals(aComponent);830 }831 });832 806 833 807 windowMenu.addMenuListener(menuSeparatorHandler); 834 808 … … public class MainMenu extends JMenuBar { 847 821 } 848 822 849 823 /** 850 * Create search field.851 * @return the search field852 */853 private DisableShortcutsOnFocusGainedTextField createSearchField() {854 DisableShortcutsOnFocusGainedTextField searchField = new DisableShortcutsOnFocusGainedTextField() {855 @Override856 public Dimension getPreferredSize() {857 // JMenuBar uses a BoxLayout and it doesn't seem possible to specify a size factor,858 // so compute the preferred size dynamically859 return new Dimension(Math.min(200, Math.max(25, getMaximumAvailableWidth())),860 helpMenu.getPreferredSize().height);861 }862 };863 Shortcut searchFieldShortcut = Shortcut.registerShortcut("menu:search-field", tr("Search menu items"), KeyEvent.VK_R, Shortcut.MNEMONIC);864 searchFieldShortcut.setFocusAccelerator(searchField);865 searchField.setEditable(true);866 searchField.setMaximumSize(new Dimension(200, helpMenu.getPreferredSize().height));867 searchField.setHint(tr("Search menu items"));868 searchField.setToolTipText(Main.platform.makeTooltip(tr("Search menu items"), searchFieldShortcut));869 searchField.addKeyListener(new SearchFieldKeyListener());870 searchField.getDocument().addDocumentListener(new SearchFieldTextListener(this, searchField));871 return searchField;872 }873 874 /**875 824 * Search main menu for items with {@code textToFind} in title. 876 825 * @param textToFind The text to find 826 * @param skipPresets whether to skip the {@link #presetsMenu} in the search 877 827 * @return not null list of found menu items. 878 828 */ 879 p rivate List<JMenuItem> findMenuItems(String textToFind) {880 // Explicit ely use default locale in this case, because we're looking for translated strings829 public List<JMenuItem> findMenuItems(String textToFind, boolean skipPresets) { 830 // Explicitly use default locale in this case, because we're looking for translated strings 881 831 textToFind = textToFind.toLowerCase(Locale.getDefault()); 882 832 List<JMenuItem> result = new ArrayList<>(); 883 884 // Iterate over main menus 885 for (MenuElement menuElement : getSubElements()) { 886 if (!(menuElement instanceof JMenu)) continue; 887 888 JMenu mainMenuItem = (JMenu) menuElement; 889 if (mainMenuItem.getAction() != null && mainMenuItem.getText().toLowerCase(Locale.getDefault()).contains(textToFind)) { 890 result.add(mainMenuItem); 833 for (int i = 0; i < getMenuCount(); i++) { 834 if (getMenu(i) != null && (!skipPresets || presetsMenu != getMenu(i))) { 835 findMenuItems(getMenu(i), textToFind, result); 891 836 } 892 893 //Search recursively894 findMenuItems(mainMenuItem, textToFind, result);895 837 } 896 838 return result; 897 839 } … … public class MainMenu extends JMenuBar { 901 843 * contains {@code textToFind} it's appended to result. 902 844 * @param menu menu in which search will be performed 903 845 * @param textToFind The text to find 904 * @param result resulting list of menu items846 * @param result resulting list of menu items 905 847 */ 906 848 private static void findMenuItems(final JMenu menu, final String textToFind, final List<JMenuItem> result) { 907 849 for (int i = 0; i < menu.getItemCount(); i++) { 908 850 JMenuItem menuItem = menu.getItem(i); 909 851 if (menuItem == null) continue; 910 852 911 // Explicit ely use default locale in this case, because we're looking for translated strings853 // Explicitly use default locale in this case, because we're looking for translated strings 912 854 if (menuItem.getAction() != null && menuItem.getText().toLowerCase(Locale.getDefault()).contains(textToFind)) { 913 855 result.add(menuItem); 914 856 } … … public class MainMenu extends JMenuBar { 970 912 } 971 913 } 972 914 973 /**974 * This listener is designed to handle ENTER key pressed in menu search field.975 * When user presses Enter key then selected item of "searchResultsMenu" is triggered.976 */977 private static class SearchFieldKeyListener implements KeyListener {978 979 @Override980 public void keyPressed(KeyEvent e) {981 if (e.getKeyCode() == KeyEvent.VK_ENTER) {982 // On ENTER selected menu item must be triggered983 MenuElement[] selection = MenuSelectionManager.defaultManager().getSelectedPath();984 if (selection.length > 1) {985 MenuElement selectedElement = selection[selection.length-1];986 if (selectedElement instanceof JMenuItem) {987 JMenuItem selectedItem = (JMenuItem) selectedElement;988 Action menuAction = selectedItem.getAction();989 menuAction.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, null));990 if (Main.isDebugEnabled()) {991 Main.debug(getClass().getName()+" consuming event "+e);992 }993 e.consume();994 }995 }996 }997 }998 999 @Override1000 public void keyTyped(KeyEvent e) {1001 // Not used1002 }1003 1004 @Override1005 public void keyReleased(KeyEvent e) {1006 // Not used1007 }1008 }1009 1010 private class SearchFieldTextListener implements DocumentListener {1011 private final JTextField searchField;1012 private final MainMenu mainMenu;1013 private String currentSearchText;1014 1015 SearchFieldTextListener(MainMenu mainMenu, JTextField searchField) {1016 this.mainMenu = mainMenu;1017 this.searchField = searchField;1018 }1019 1020 @Override1021 public void insertUpdate(DocumentEvent e) {1022 doSearch(searchField.getText());1023 }1024 1025 @Override1026 public void removeUpdate(DocumentEvent e) {1027 doSearch(searchField.getText());1028 }1029 1030 @Override1031 public void changedUpdate(DocumentEvent e) {1032 doSearch(searchField.getText());1033 }1034 1035 //TODO: perform some delay (maybe 200 ms) before actual searching.1036 void doSearch(String searchTerm) {1037 // Explicitely use default locale in this case, because we're looking for translated strings1038 searchTerm = searchTerm.trim().toLowerCase(Locale.getDefault());1039 1040 if (searchTerm.equals(currentSearchText)) {1041 return;1042 }1043 currentSearchText = searchTerm;1044 if (searchTerm.isEmpty()) {1045 // No text to search1046 hideMenu();1047 return;1048 }1049 1050 List<JMenuItem> searchResult = mainMenu.findMenuItems(currentSearchText);1051 if (searchResult.isEmpty()) {1052 // Nothing found1053 hideMenu();1054 return;1055 }1056 1057 if (searchResult.size() > 20) {1058 // Too many items found...1059 searchResult = searchResult.subList(0, 20);1060 }1061 1062 // Update Popup menu1063 searchResultsMenu.removeAll();1064 for (JMenuItem foundItem : searchResult) {1065 searchResultsMenu.add(foundItem.getText()).setAction(foundItem.getAction());1066 }1067 // Put menu right under search field1068 searchResultsMenu.pack();1069 searchResultsMenu.show(mainMenu, searchField.getX(), searchField.getY() + searchField.getHeight());1070 1071 // This is tricky. User still is able to edit search text. While Up and Down keys are handled by Popup Menu.1072 searchField.requestFocusInWindow();1073 }1074 1075 private void hideMenu() {1076 searchResultsMenu.setVisible(false);1077 }1078 }1079 915 } -
new file src/org/openstreetmap/josm/gui/dialogs/MenuItemSearchDialog.java
diff --git a/src/org/openstreetmap/josm/gui/dialogs/MenuItemSearchDialog.java b/src/org/openstreetmap/josm/gui/dialogs/MenuItemSearchDialog.java new file mode 100644 index 0000000..2ab0a94
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.dialogs; 3 4 import org.openstreetmap.josm.Main; 5 import org.openstreetmap.josm.actions.JosmAction; 6 import org.openstreetmap.josm.gui.ExtendedDialog; 7 import org.openstreetmap.josm.gui.MainMenu; 8 import org.openstreetmap.josm.gui.widgets.SearchTextResultListPanel; 9 import org.openstreetmap.josm.tools.Shortcut; 10 11 import javax.swing.*; 12 import java.awt.*; 13 import java.awt.event.ActionEvent; 14 import java.awt.event.ActionListener; 15 import java.awt.event.KeyEvent; 16 17 import static org.openstreetmap.josm.tools.I18n.tr; 18 19 public class MenuItemSearchDialog extends ExtendedDialog { 20 21 private final Selector selector; 22 private static final MenuItemSearchDialog INSTANCE = new MenuItemSearchDialog(Main.main.menu); 23 24 private MenuItemSearchDialog(MainMenu menu) { 25 super(Main.parent, tr("Search menu items"), new String[]{tr("Select"), tr("Cancel")}); 26 this.selector = new Selector(menu); 27 this.selector.setDblClickListener(new ActionListener() { 28 @Override 29 public void actionPerformed(ActionEvent e) { 30 buttonAction(0, null); 31 } 32 }); 33 setContent(selector); 34 setPreferredSize(new Dimension(600, 300)); 35 } 36 37 /** 38 * Returns the unique instance of {@code MenuItemSearchDialog}. 39 * 40 * @return the unique instance of {@code MenuItemSearchDialog}. 41 */ 42 public static synchronized MenuItemSearchDialog getInstance() { 43 return INSTANCE; 44 } 45 46 @Override 47 public ExtendedDialog showDialog() { 48 selector.init(); 49 super.showDialog(); 50 selector.clearSelection(); 51 return this; 52 } 53 54 @Override 55 protected void buttonAction(int buttonIndex, ActionEvent evt) { 56 super.buttonAction(buttonIndex, evt); 57 if (buttonIndex == 0 && selector.getSelectedItem() != null && selector.getSelectedItem().isEnabled()) { 58 selector.getSelectedItem().getAction().actionPerformed(evt); 59 } 60 } 61 62 private static class Selector extends SearchTextResultListPanel<JMenuItem> { 63 64 private final MainMenu menu; 65 66 public Selector(MainMenu menu) { 67 super(); 68 this.menu = menu; 69 lsResult.setCellRenderer(new CellRenderer()); 70 lsResult.setSelectionModel(new DefaultListSelectionModel() { 71 72 }); 73 } 74 75 public JMenuItem getSelectedItem() { 76 final JMenuItem selected = lsResult.getSelectedValue(); 77 if (selected != null) { 78 return selected; 79 } else if (!lsResultModel.isEmpty()) { 80 return lsResultModel.getElementAt(0); 81 } else { 82 return null; 83 } 84 } 85 86 @Override 87 protected void filterItems() { 88 lsResultModel.setItems(menu.findMenuItems(edSearchText.getText(), true)); 89 } 90 } 91 92 private static class CellRenderer implements ListCellRenderer<JMenuItem> { 93 94 private final DefaultListCellRenderer def = new DefaultListCellRenderer(); 95 96 @Override 97 public Component getListCellRendererComponent(JList<? extends JMenuItem> list, JMenuItem value, int index, boolean isSelected, boolean cellHasFocus) { 98 final JLabel label = (JLabel) def.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 99 label.setText(value.getText()); 100 label.setIcon(value.getIcon()); 101 label.setEnabled(value.isEnabled()); 102 final JMenuItem item = new JMenuItem(value.getText()); 103 item.setAction(value.getAction()); 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 public Action() { 118 super(tr("Search menu items"), "dialogs/search", null, 119 Shortcut.registerShortcut("help:search-items", "Search menu items", KeyEvent.VK_SPACE, Shortcut.CTRL), false); 120 } 121 122 @Override 123 public void actionPerformed(ActionEvent e) { 124 MenuItemSearchDialog.getInstance().showDialog(); 125 } 126 } 127 } -
src/org/openstreetmap/josm/gui/tagging/presets/TaggingPresetSelector.java
diff --git a/src/org/openstreetmap/josm/gui/tagging/presets/TaggingPresetSelector.java b/src/org/openstreetmap/josm/gui/tagging/presets/TaggingPresetSelector.java index 2c5162c..e430097 100644
a b import java.awt.BorderLayout; 7 7 import java.awt.Component; 8 8 import java.awt.Dimension; 9 9 import java.awt.event.ActionEvent; 10 import java.awt.event.ActionListener;11 10 import java.awt.event.ItemEvent; 12 11 import java.awt.event.ItemListener; 13 import java.awt.event.KeyAdapter;14 import java.awt.event.KeyEvent;15 import java.awt.event.MouseAdapter;16 import java.awt.event.MouseEvent;17 12 import java.util.ArrayList; 18 13 import java.util.Collection; 19 14 import java.util.Collections; … … import java.util.Objects; 26 21 import java.util.Set; 27 22 28 23 import javax.swing.AbstractAction; 29 import javax.swing.AbstractListModel;30 24 import javax.swing.Action; 31 25 import javax.swing.BoxLayout; 32 26 import javax.swing.DefaultListCellRenderer; … … import javax.swing.JLabel; 36 30 import javax.swing.JList; 37 31 import javax.swing.JPanel; 38 32 import javax.swing.JPopupMenu; 39 import javax.swing.JScrollPane;40 33 import javax.swing.ListCellRenderer; 41 import javax.swing.event.DocumentEvent;42 import javax.swing.event.DocumentListener;43 34 import javax.swing.event.ListSelectionEvent; 44 35 import javax.swing.event.ListSelectionListener; 45 36 … … import org.openstreetmap.josm.gui.tagging.presets.items.Key; 53 44 import org.openstreetmap.josm.gui.tagging.presets.items.KeyedItem; 54 45 import org.openstreetmap.josm.gui.tagging.presets.items.Roles; 55 46 import org.openstreetmap.josm.gui.tagging.presets.items.Roles.Role; 56 import org.openstreetmap.josm.gui.widgets.JosmTextField;57 47 import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher; 48 import org.openstreetmap.josm.gui.widgets.SearchTextResultListPanel; 58 49 import org.openstreetmap.josm.tools.Predicate; 59 50 import org.openstreetmap.josm.tools.Utils; 60 51 … … import org.openstreetmap.josm.tools.Utils; 62 53 * GUI component to select tagging preset: the list with filter and two checkboxes 63 54 * @since 6068 64 55 */ 65 public class TaggingPresetSelector extends JPanelimplements SelectionChangedListener {56 public class TaggingPresetSelector extends SearchTextResultListPanel<TaggingPreset> implements SelectionChangedListener { 66 57 67 58 private static final int CLASSIFICATION_IN_FAVORITES = 300; 68 59 private static final int CLASSIFICATION_NAME_MATCH = 300; … … public class TaggingPresetSelector extends JPanel implements SelectionChangedLis 72 63 private static final BooleanProperty SEARCH_IN_TAGS = new BooleanProperty("taggingpreset.dialog.search-in-tags", true); 73 64 private static final BooleanProperty ONLY_APPLICABLE = new BooleanProperty("taggingpreset.dialog.only-applicable-to-selection", true); 74 65 75 private final JosmTextField edSearchText;76 private final JList<TaggingPreset> lsResult;77 66 private final JCheckBox ckOnlyApplicable; 78 67 private final JCheckBox ckSearchInTags; 79 68 private final Set<TaggingPresetType> typesInSelection = EnumSet.noneOf(TaggingPresetType.class); 80 69 private boolean typesInSelectionDirty = true; 81 70 private final transient PresetClassifications classifications = new PresetClassifications(); 82 private final ResultListModel lsResultModel = new ResultListModel();83 84 private final transient List<ListSelectionListener> listSelectionListeners = new ArrayList<>();85 86 private transient ActionListener dblClickListener;87 private transient ActionListener clickListener;88 71 89 72 private static class ResultListCellRenderer implements ListCellRenderer<TaggingPreset> { 90 73 private final DefaultListCellRenderer def = new DefaultListCellRenderer(); … … public class TaggingPresetSelector extends JPanel implements SelectionChangedLis 98 81 } 99 82 } 100 83 101 private static class ResultListModel extends AbstractListModel<TaggingPreset> {102 103 private transient List<PresetClassification> presets = new ArrayList<>();104 105 public synchronized void setPresets(List<PresetClassification> presets) {106 this.presets = presets;107 fireContentsChanged(this, 0, Integer.MAX_VALUE);108 }109 110 @Override111 public synchronized TaggingPreset getElementAt(int index) {112 return presets.get(index).preset;113 }114 115 @Override116 public synchronized int getSize() {117 return presets.size();118 }119 120 public synchronized boolean isEmpty() {121 return presets.isEmpty();122 }123 }124 125 84 /** 126 85 * Computes the match ration of a {@link TaggingPreset} wrt. a searchString. 127 86 */ … … public class TaggingPresetSelector extends JPanel implements SelectionChangedLis 216 175 * Constructs a new {@code TaggingPresetSelector}. 217 176 */ 218 177 public TaggingPresetSelector(boolean displayOnlyApplicable, boolean displaySearchInTags) { 219 super(new BorderLayout()); 220 classifications.loadPresets(TaggingPresets.getTaggingPresets()); 221 222 edSearchText = new JosmTextField(); 223 edSearchText.getDocument().addDocumentListener(new DocumentListener() { 224 @Override 225 public void removeUpdate(DocumentEvent e) { 226 filterPresets(); 227 } 228 229 @Override 230 public void insertUpdate(DocumentEvent e) { 231 filterPresets(); 232 } 233 234 @Override 235 public void changedUpdate(DocumentEvent e) { 236 filterPresets(); 237 } 238 }); 239 edSearchText.addKeyListener(new KeyAdapter() { 240 @Override 241 public void keyPressed(KeyEvent e) { 242 switch (e.getKeyCode()) { 243 case KeyEvent.VK_DOWN: 244 selectPreset(lsResult.getSelectedIndex() + 1); 245 break; 246 case KeyEvent.VK_UP: 247 selectPreset(lsResult.getSelectedIndex() - 1); 248 break; 249 case KeyEvent.VK_PAGE_DOWN: 250 selectPreset(lsResult.getSelectedIndex() + 10); 251 break; 252 case KeyEvent.VK_PAGE_UP: 253 selectPreset(lsResult.getSelectedIndex() - 10); 254 break; 255 case KeyEvent.VK_HOME: 256 selectPreset(0); 257 break; 258 case KeyEvent.VK_END: 259 selectPreset(lsResultModel.getSize()); 260 break; 261 } 262 } 263 }); 264 add(edSearchText, BorderLayout.NORTH); 265 266 lsResult = new JList<>(lsResultModel); 178 super(); 267 179 lsResult.setCellRenderer(new ResultListCellRenderer()); 268 lsResult.addMouseListener(new MouseAdapter() { 269 @Override 270 public void mouseClicked(MouseEvent e) { 271 if (e.getClickCount() > 1) { 272 if (dblClickListener != null) 273 dblClickListener.actionPerformed(null); 274 } else { 275 if (clickListener != null) 276 clickListener.actionPerformed(null); 277 } 278 } 279 }); 280 add(new JScrollPane(lsResult), BorderLayout.CENTER); 180 classifications.loadPresets(TaggingPresets.getTaggingPresets()); 281 181 282 182 JPanel pnChecks = new JPanel(); 283 183 pnChecks.setLayout(new BoxLayout(pnChecks, BoxLayout.Y_AXIS)); … … public class TaggingPresetSelector extends JPanel implements SelectionChangedLis 289 189 ckOnlyApplicable.addItemListener(new ItemListener() { 290 190 @Override 291 191 public void itemStateChanged(ItemEvent e) { 292 filter Presets();192 filterItems(); 293 193 } 294 194 }); 295 195 } else { … … public class TaggingPresetSelector extends JPanel implements SelectionChangedLis 303 203 ckSearchInTags.addItemListener(new ItemListener() { 304 204 @Override 305 205 public void itemStateChanged(ItemEvent e) { 306 filter Presets();206 filterItems(); 307 207 } 308 208 }); 309 209 pnChecks.add(ckSearchInTags); … … public class TaggingPresetSelector extends JPanel implements SelectionChangedLis 314 214 add(pnChecks, BorderLayout.SOUTH); 315 215 316 216 setPreferredSize(new Dimension(400, 300)); 317 filter Presets();217 filterItems(); 318 218 JPopupMenu popupMenu = new JPopupMenu(); 319 219 popupMenu.add(new AbstractAction(tr("Add toolbar button")) { 320 220 @Override … … public class TaggingPresetSelector extends JPanel implements SelectionChangedLis 326 226 lsResult.addMouseListener(new PopupMenuLauncher(popupMenu)); 327 227 } 328 228 329 private synchronized void selectPreset(int newIndex) {330 if (newIndex < 0) {331 newIndex = 0;332 }333 if (newIndex > lsResultModel.getSize() - 1) {334 newIndex = lsResultModel.getSize() - 1;335 }336 lsResult.setSelectedIndex(newIndex);337 lsResult.ensureIndexIsVisible(newIndex);338 }339 340 229 /** 341 230 * Search expression can be in form: "group1/group2/name" where names can contain multiple words 342 231 */ 343 private synchronized void filterPresets() { 232 @Override 233 protected synchronized void filterItems() { 344 234 //TODO Save favorites to file 345 235 String text = edSearchText.getText().toLowerCase(Locale.ENGLISH); 346 236 boolean onlyApplicable = ckOnlyApplicable != null && ckOnlyApplicable.isSelected(); … … public class TaggingPresetSelector extends JPanel implements SelectionChangedLis 352 242 text, onlyApplicable, inTags, getTypesInSelection(), selected); 353 243 354 244 TaggingPreset oldPreset = getSelectedPreset(); 355 lsResultModel.setPresets(result); 245 lsResultModel.setItems(Utils.transform(result, new Utils.Function<PresetClassification, TaggingPreset>() { 246 @Override 247 public TaggingPreset apply(PresetClassification x) { 248 return x.preset; 249 } 250 })); 356 251 TaggingPreset newPreset = getSelectedPreset(); 357 252 if (!Objects.equals(oldPreset, newPreset)) { 358 253 int[] indices = lsResult.getSelectedIndices(); … … public class TaggingPresetSelector extends JPanel implements SelectionChangedLis 486 381 typesInSelectionDirty = true; 487 382 } 488 383 384 @Override 489 385 public synchronized void init() { 490 386 if (ckOnlyApplicable != null) { 491 387 ckOnlyApplicable.setEnabled(!getTypesInSelection().isEmpty()); 492 388 ckOnlyApplicable.setSelected(!getTypesInSelection().isEmpty() && ONLY_APPLICABLE.get()); 493 389 } 494 listSelectionListeners.clear(); 495 edSearchText.setText(""); 496 filterPresets(); 390 super.init(); 497 391 } 498 392 499 393 public void init(Collection<TaggingPreset> presets) { … … public class TaggingPresetSelector extends JPanel implements SelectionChangedLis 502 396 init(); 503 397 } 504 398 505 public synchronized void clearSelection() {506 lsResult.getSelectionModel().clearSelection();507 }508 509 399 /** 510 400 * Save checkbox values in preferences for future reuse 511 401 */ … … public class TaggingPresetSelector extends JPanel implements SelectionChangedLis 542 432 public synchronized void setSelectedPreset(TaggingPreset p) { 543 433 lsResult.setSelectedValue(p, true); 544 434 } 545 546 public synchronized int getItemCount() {547 return lsResultModel.getSize();548 }549 550 public void setDblClickListener(ActionListener dblClickListener) {551 this.dblClickListener = dblClickListener;552 }553 554 public void setClickListener(ActionListener clickListener) {555 this.clickListener = clickListener;556 }557 558 /**559 * Adds a selection listener to the presets list.560 * @param selectListener The list selection listener561 * @since 7412562 */563 public synchronized void addSelectionListener(ListSelectionListener selectListener) {564 lsResult.getSelectionModel().addListSelectionListener(selectListener);565 listSelectionListeners.add(selectListener);566 }567 568 /**569 * Removes a selection listener from the presets list.570 * @param selectListener The list selection listener571 * @since 7412572 */573 public synchronized void removeSelectionListener(ListSelectionListener selectListener) {574 listSelectionListeners.remove(selectListener);575 lsResult.getSelectionModel().removeListSelectionListener(selectListener);576 }577 435 } -
new file src/org/openstreetmap/josm/gui/widgets/SearchTextResultListPanel.java
diff --git a/src/org/openstreetmap/josm/gui/widgets/SearchTextResultListPanel.java b/src/org/openstreetmap/josm/gui/widgets/SearchTextResultListPanel.java new file mode 100644 index 0000000..94b5f11
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.widgets; 3 4 import javax.swing.*; 5 import javax.swing.event.DocumentEvent; 6 import javax.swing.event.DocumentListener; 7 import javax.swing.event.ListSelectionListener; 8 import java.awt.*; 9 import java.awt.event.ActionListener; 10 import java.awt.event.KeyAdapter; 11 import java.awt.event.KeyEvent; 12 import java.awt.event.MouseAdapter; 13 import java.awt.event.MouseEvent; 14 import java.util.*; 15 import java.util.List; 16 17 public abstract class SearchTextResultListPanel<T> extends JPanel { 18 19 protected final JosmTextField edSearchText; 20 protected final JList<T> lsResult; 21 protected final ResultListModel<T> lsResultModel = new ResultListModel<>(); 22 23 protected final transient List<ListSelectionListener> listSelectionListeners = new ArrayList<>(); 24 25 private transient ActionListener dblClickListener; 26 private transient ActionListener clickListener; 27 28 protected abstract void filterItems(); 29 30 public SearchTextResultListPanel() { 31 super(new BorderLayout()); 32 33 edSearchText = new JosmTextField(); 34 edSearchText.getDocument().addDocumentListener(new DocumentListener() { 35 @Override 36 public void removeUpdate(DocumentEvent e) { 37 filterItems(); 38 } 39 40 @Override 41 public void insertUpdate(DocumentEvent e) { 42 filterItems(); 43 } 44 45 @Override 46 public void changedUpdate(DocumentEvent e) { 47 filterItems(); 48 } 49 }); 50 edSearchText.addKeyListener(new KeyAdapter() { 51 @Override 52 public void keyPressed(KeyEvent e) { 53 switch (e.getKeyCode()) { 54 case KeyEvent.VK_DOWN: 55 selectItem(lsResult.getSelectedIndex() + 1); 56 break; 57 case KeyEvent.VK_UP: 58 selectItem(lsResult.getSelectedIndex() - 1); 59 break; 60 case KeyEvent.VK_PAGE_DOWN: 61 selectItem(lsResult.getSelectedIndex() + 10); 62 break; 63 case KeyEvent.VK_PAGE_UP: 64 selectItem(lsResult.getSelectedIndex() - 10); 65 break; 66 case KeyEvent.VK_HOME: 67 selectItem(0); 68 break; 69 case KeyEvent.VK_END: 70 selectItem(lsResultModel.getSize()); 71 break; 72 } 73 } 74 }); 75 add(edSearchText, BorderLayout.NORTH); 76 77 lsResult = new JList<>(lsResultModel); 78 lsResult.addMouseListener(new MouseAdapter() { 79 @Override 80 public void mouseClicked(MouseEvent e) { 81 if (e.getClickCount() > 1) { 82 if (dblClickListener != null) 83 dblClickListener.actionPerformed(null); 84 } else { 85 if (clickListener != null) 86 clickListener.actionPerformed(null); 87 } 88 } 89 }); 90 add(new JScrollPane(lsResult), BorderLayout.CENTER); 91 } 92 93 protected static class ResultListModel<T> extends AbstractListModel<T> { 94 95 private transient List<T> items = new ArrayList<>(); 96 97 public synchronized void setItems(List<T> items) { 98 this.items = items; 99 fireContentsChanged(this, 0, Integer.MAX_VALUE); 100 } 101 102 @Override 103 public synchronized T getElementAt(int index) { 104 return items.get(index); 105 } 106 107 @Override 108 public synchronized int getSize() { 109 return items.size(); 110 } 111 112 public synchronized boolean isEmpty() { 113 return items.isEmpty(); 114 } 115 } 116 117 public synchronized void init() { 118 listSelectionListeners.clear(); 119 edSearchText.setText(""); 120 filterItems(); 121 } 122 123 private synchronized void selectItem(int newIndex) { 124 if (newIndex < 0) { 125 newIndex = 0; 126 } 127 if (newIndex > lsResultModel.getSize() - 1) { 128 newIndex = lsResultModel.getSize() - 1; 129 } 130 lsResult.setSelectedIndex(newIndex); 131 lsResult.ensureIndexIsVisible(newIndex); 132 } 133 134 public synchronized void clearSelection() { 135 lsResult.clearSelection(); 136 } 137 138 public synchronized int getItemCount() { 139 return lsResultModel.getSize(); 140 } 141 142 public void setDblClickListener(ActionListener dblClickListener) { 143 this.dblClickListener = dblClickListener; 144 } 145 146 public void setClickListener(ActionListener clickListener) { 147 this.clickListener = clickListener; 148 } 149 150 /** 151 * Adds a selection listener to the presets list. 152 * 153 * @param selectListener The list selection listener 154 * @since 7412 155 */ 156 public synchronized void addSelectionListener(ListSelectionListener selectListener) { 157 lsResult.getSelectionModel().addListSelectionListener(selectListener); 158 listSelectionListeners.add(selectListener); 159 } 160 161 /** 162 * Removes a selection listener from the presets list. 163 * 164 * @param selectListener The list selection listener 165 * @since 7412 166 */ 167 public synchronized void removeSelectionListener(ListSelectionListener selectListener) { 168 listSelectionListeners.remove(selectListener); 169 lsResult.getSelectionModel().removeListSelectionListener(selectListener); 170 } 171 }
