Ticket #12224: 12224-v2.patch
| File 12224-v2.patch, 36.0 KB (added by , 10 years ago) |
|---|
-
src/org/openstreetmap/josm/gui/MainApplication.java
commit 356bbabfc3868e56819051d609caf7195c01388c Author: Simon Legner <Simon.Legner@gmail.com> Date: Tue Jan 5 15:22:01 2016 +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 f051cb3..4e6089e 100644a b public class MainApplication extends Main { 472 472 splash.setVisible(false); 473 473 splash.dispose(); 474 474 mainFrame.setVisible(true); 475 main.gettingStarted.requestFocusInWindow();476 475 } 477 476 }); 478 477 -
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 02939c6..3b417d3 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 { 817 801 } 818 802 }); 819 803 820 helpMenu.add(statusreport); 821 helpMenu.add(reportbug); 804 add(helpMenu, new MenuItemSearchDialog.Action()); 805 helpMenu.addSeparator(); 806 add(helpMenu, statusreport); 807 add(helpMenu, reportbug); 822 808 helpMenu.addSeparator(); 823 809 824 810 // FIXME why is help not a JosmAction? 825 811 helpMenu.add(help).setAccelerator(Shortcut.registerShortcut("system:help", tr("Help"), KeyEvent.VK_F1, 826 812 Shortcut.DIRECT).getKeyStroke()); 827 813 add(helpMenu, about); 828 add(Box.createHorizontalGlue());829 final DisableShortcutsOnFocusGainedTextField searchField = createSearchField();830 add(searchField);831 832 // Do not let search field take the focus automatically833 setFocusTraversalPolicyProvider(true);834 setFocusTraversalPolicy(new DefaultFocusTraversalPolicy() {835 @Override836 protected boolean accept(Component aComponent) {837 return super.accept(aComponent) && !searchField.equals(aComponent);838 }839 });840 814 841 815 windowMenu.addMenuListener(menuSeparatorHandler); 842 816 … … public class MainMenu extends JMenuBar { 855 829 } 856 830 857 831 /** 858 * Create search field.859 * @return the search field860 */861 private DisableShortcutsOnFocusGainedTextField createSearchField() {862 DisableShortcutsOnFocusGainedTextField searchField = new DisableShortcutsOnFocusGainedTextField() {863 @Override864 public Dimension getPreferredSize() {865 // JMenuBar uses a BoxLayout and it doesn't seem possible to specify a size factor,866 // so compute the preferred size dynamically867 return new Dimension(Math.min(200, Math.max(25, getMaximumAvailableWidth())),868 helpMenu.getPreferredSize().height);869 }870 };871 Shortcut searchFieldShortcut = Shortcut.registerShortcut("menu:search-field", tr("Search menu items"), KeyEvent.VK_R, Shortcut.MNEMONIC);872 searchFieldShortcut.setFocusAccelerator(searchField);873 searchField.setEditable(true);874 searchField.setMaximumSize(new Dimension(200, helpMenu.getPreferredSize().height));875 searchField.setHint(tr("Search menu items"));876 searchField.setToolTipText(Main.platform.makeTooltip(tr("Search menu items"), searchFieldShortcut));877 searchField.addKeyListener(new SearchFieldKeyListener());878 searchField.getDocument().addDocumentListener(new SearchFieldTextListener(this, searchField));879 return searchField;880 }881 882 /**883 832 * Search main menu for items with {@code textToFind} in title. 884 833 * @param textToFind The text to find 834 * @param skipPresets whether to skip the {@link #presetsMenu} in the search 885 835 * @return not null list of found menu items. 886 836 */ 887 p rivate List<JMenuItem> findMenuItems(String textToFind) {888 // Explicit ely use default locale in this case, because we're looking for translated strings837 public List<JMenuItem> findMenuItems(String textToFind, boolean skipPresets) { 838 // Explicitly use default locale in this case, because we're looking for translated strings 889 839 textToFind = textToFind.toLowerCase(Locale.getDefault()); 890 840 List<JMenuItem> result = new ArrayList<>(); 891 892 // Iterate over main menus 893 for (MenuElement menuElement : getSubElements()) { 894 if (!(menuElement instanceof JMenu)) continue; 895 896 JMenu mainMenuItem = (JMenu) menuElement; 897 if (mainMenuItem.getAction() != null && mainMenuItem.getText().toLowerCase(Locale.getDefault()).contains(textToFind)) { 898 result.add(mainMenuItem); 841 for (int i = 0; i < getMenuCount(); i++) { 842 if (getMenu(i) != null && (!skipPresets || presetsMenu != getMenu(i))) { 843 findMenuItems(getMenu(i), textToFind, result); 899 844 } 900 901 //Search recursively902 findMenuItems(mainMenuItem, textToFind, result);903 845 } 904 846 return result; 905 847 } … … public class MainMenu extends JMenuBar { 909 851 * contains {@code textToFind} it's appended to result. 910 852 * @param menu menu in which search will be performed 911 853 * @param textToFind The text to find 912 * @param result resulting list of menu items854 * @param result resulting list of menu items 913 855 */ 914 856 private static void findMenuItems(final JMenu menu, final String textToFind, final List<JMenuItem> result) { 915 857 for (int i = 0; i < menu.getItemCount(); i++) { 916 858 JMenuItem menuItem = menu.getItem(i); 917 859 if (menuItem == null) continue; 918 860 919 // Explicit ely use default locale in this case, because we're looking for translated strings861 // Explicitly use default locale in this case, because we're looking for translated strings 920 862 if (menuItem.getAction() != null && menuItem.getText().toLowerCase(Locale.getDefault()).contains(textToFind)) { 921 863 result.add(menuItem); 922 864 } … … public class MainMenu extends JMenuBar { 978 920 } 979 921 } 980 922 981 /**982 * This listener is designed to handle ENTER key pressed in menu search field.983 * When user presses Enter key then selected item of "searchResultsMenu" is triggered.984 */985 private static class SearchFieldKeyListener implements KeyListener {986 987 @Override988 public void keyPressed(KeyEvent e) {989 if (e.getKeyCode() == KeyEvent.VK_ENTER) {990 // On ENTER selected menu item must be triggered991 MenuElement[] selection = MenuSelectionManager.defaultManager().getSelectedPath();992 if (selection.length > 1) {993 MenuElement selectedElement = selection[selection.length-1];994 if (selectedElement instanceof JMenuItem) {995 JMenuItem selectedItem = (JMenuItem) selectedElement;996 Action menuAction = selectedItem.getAction();997 menuAction.actionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, null));998 if (Main.isDebugEnabled()) {999 Main.debug(getClass().getName()+" consuming event "+e);1000 }1001 e.consume();1002 }1003 }1004 }1005 }1006 1007 @Override1008 public void keyTyped(KeyEvent e) {1009 // Not used1010 }1011 1012 @Override1013 public void keyReleased(KeyEvent e) {1014 // Not used1015 }1016 }1017 1018 private class SearchFieldTextListener implements DocumentListener {1019 private final JTextField searchField;1020 private final MainMenu mainMenu;1021 private String currentSearchText;1022 1023 SearchFieldTextListener(MainMenu mainMenu, JTextField searchField) {1024 this.mainMenu = mainMenu;1025 this.searchField = searchField;1026 }1027 1028 @Override1029 public void insertUpdate(DocumentEvent e) {1030 doSearch(searchField.getText());1031 }1032 1033 @Override1034 public void removeUpdate(DocumentEvent e) {1035 doSearch(searchField.getText());1036 }1037 1038 @Override1039 public void changedUpdate(DocumentEvent e) {1040 doSearch(searchField.getText());1041 }1042 1043 //TODO: perform some delay (maybe 200 ms) before actual searching.1044 void doSearch(String searchTerm) {1045 // Explicitely use default locale in this case, because we're looking for translated strings1046 searchTerm = searchTerm.trim().toLowerCase(Locale.getDefault());1047 1048 if (searchTerm.equals(currentSearchText)) {1049 return;1050 }1051 currentSearchText = searchTerm;1052 if (searchTerm.isEmpty()) {1053 // No text to search1054 hideMenu();1055 return;1056 }1057 1058 List<JMenuItem> searchResult = mainMenu.findMenuItems(currentSearchText);1059 if (searchResult.isEmpty()) {1060 // Nothing found1061 hideMenu();1062 return;1063 }1064 1065 if (searchResult.size() > 20) {1066 // Too many items found...1067 searchResult = searchResult.subList(0, 20);1068 }1069 1070 // Update Popup menu1071 searchResultsMenu.removeAll();1072 for (JMenuItem foundItem : searchResult) {1073 searchResultsMenu.add(foundItem.getText()).setAction(foundItem.getAction());1074 }1075 // Put menu right under search field1076 searchResultsMenu.pack();1077 searchResultsMenu.show(mainMenu, searchField.getX(), searchField.getY() + searchField.getHeight());1078 1079 // This is tricky. User still is able to edit search text. While Up and Down keys are handled by Popup Menu.1080 searchField.requestFocusInWindow();1081 }1082 1083 private void hideMenu() {1084 searchResultsMenu.setVisible(false);1085 }1086 }1087 923 } -
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 88d8ff8..840c657 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 218 177 * @param displaySearchInTags if {@code true} display "Search in tags" checkbox 219 178 */ 220 179 public TaggingPresetSelector(boolean displayOnlyApplicable, boolean displaySearchInTags) { 221 super(new BorderLayout()); 222 classifications.loadPresets(TaggingPresets.getTaggingPresets()); 223 224 edSearchText = new JosmTextField(); 225 edSearchText.getDocument().addDocumentListener(new DocumentListener() { 226 @Override 227 public void removeUpdate(DocumentEvent e) { 228 filterPresets(); 229 } 230 231 @Override 232 public void insertUpdate(DocumentEvent e) { 233 filterPresets(); 234 } 235 236 @Override 237 public void changedUpdate(DocumentEvent e) { 238 filterPresets(); 239 } 240 }); 241 edSearchText.addKeyListener(new KeyAdapter() { 242 @Override 243 public void keyPressed(KeyEvent e) { 244 switch (e.getKeyCode()) { 245 case KeyEvent.VK_DOWN: 246 selectPreset(lsResult.getSelectedIndex() + 1); 247 break; 248 case KeyEvent.VK_UP: 249 selectPreset(lsResult.getSelectedIndex() - 1); 250 break; 251 case KeyEvent.VK_PAGE_DOWN: 252 selectPreset(lsResult.getSelectedIndex() + 10); 253 break; 254 case KeyEvent.VK_PAGE_UP: 255 selectPreset(lsResult.getSelectedIndex() - 10); 256 break; 257 case KeyEvent.VK_HOME: 258 selectPreset(0); 259 break; 260 case KeyEvent.VK_END: 261 selectPreset(lsResultModel.getSize()); 262 break; 263 } 264 } 265 }); 266 add(edSearchText, BorderLayout.NORTH); 267 268 lsResult = new JList<>(lsResultModel); 180 super(); 269 181 lsResult.setCellRenderer(new ResultListCellRenderer()); 270 lsResult.addMouseListener(new MouseAdapter() { 271 @Override 272 public void mouseClicked(MouseEvent e) { 273 if (e.getClickCount() > 1) { 274 if (dblClickListener != null) 275 dblClickListener.actionPerformed(null); 276 } else { 277 if (clickListener != null) 278 clickListener.actionPerformed(null); 279 } 280 } 281 }); 282 add(new JScrollPane(lsResult), BorderLayout.CENTER); 182 classifications.loadPresets(TaggingPresets.getTaggingPresets()); 283 183 284 184 JPanel pnChecks = new JPanel(); 285 185 pnChecks.setLayout(new BoxLayout(pnChecks, BoxLayout.Y_AXIS)); … … public class TaggingPresetSelector extends JPanel implements SelectionChangedLis 291 191 ckOnlyApplicable.addItemListener(new ItemListener() { 292 192 @Override 293 193 public void itemStateChanged(ItemEvent e) { 294 filter Presets();194 filterItems(); 295 195 } 296 196 }); 297 197 } else { … … public class TaggingPresetSelector extends JPanel implements SelectionChangedLis 305 205 ckSearchInTags.addItemListener(new ItemListener() { 306 206 @Override 307 207 public void itemStateChanged(ItemEvent e) { 308 filter Presets();208 filterItems(); 309 209 } 310 210 }); 311 211 pnChecks.add(ckSearchInTags); … … public class TaggingPresetSelector extends JPanel implements SelectionChangedLis 316 216 add(pnChecks, BorderLayout.SOUTH); 317 217 318 218 setPreferredSize(new Dimension(400, 300)); 319 filter Presets();219 filterItems(); 320 220 JPopupMenu popupMenu = new JPopupMenu(); 321 221 popupMenu.add(new AbstractAction(tr("Add toolbar button")) { 322 222 @Override … … public class TaggingPresetSelector extends JPanel implements SelectionChangedLis 330 230 lsResult.addMouseListener(new PopupMenuLauncher(popupMenu)); 331 231 } 332 232 333 private synchronized void selectPreset(int newIndex) {334 if (newIndex < 0) {335 newIndex = 0;336 }337 if (newIndex > lsResultModel.getSize() - 1) {338 newIndex = lsResultModel.getSize() - 1;339 }340 lsResult.setSelectedIndex(newIndex);341 lsResult.ensureIndexIsVisible(newIndex);342 }343 344 233 /** 345 234 * Search expression can be in form: "group1/group2/name" where names can contain multiple words 346 235 */ 347 private synchronized void filterPresets() { 236 @Override 237 protected synchronized void filterItems() { 348 238 //TODO Save favorites to file 349 239 String text = edSearchText.getText().toLowerCase(Locale.ENGLISH); 350 240 boolean onlyApplicable = ckOnlyApplicable != null && ckOnlyApplicable.isSelected(); … … public class TaggingPresetSelector extends JPanel implements SelectionChangedLis 356 246 text, onlyApplicable, inTags, getTypesInSelection(), selected); 357 247 358 248 final TaggingPreset oldPreset = lsResult.getSelectedValue(); 359 lsResultModel.setPresets(result); 249 lsResultModel.setItems(Utils.transform(result, new Utils.Function<PresetClassification, TaggingPreset>() { 250 @Override 251 public TaggingPreset apply(PresetClassification x) { 252 return x.preset; 253 } 254 })); 360 255 final TaggingPreset newPreset = lsResult.getSelectedValue(); 361 256 if (!Objects.equals(oldPreset, newPreset)) { 362 257 int[] indices = lsResult.getSelectedIndices(); … … public class TaggingPresetSelector extends JPanel implements SelectionChangedLis 490 385 typesInSelectionDirty = true; 491 386 } 492 387 388 @Override 493 389 public synchronized void init() { 494 390 if (ckOnlyApplicable != null) { 495 391 ckOnlyApplicable.setEnabled(!getTypesInSelection().isEmpty()); 496 392 ckOnlyApplicable.setSelected(!getTypesInSelection().isEmpty() && ONLY_APPLICABLE.get()); 497 393 } 498 listSelectionListeners.clear(); 499 edSearchText.setText(""); 500 filterPresets(); 394 super.init(); 501 395 } 502 396 503 397 public void init(Collection<TaggingPreset> presets) { … … public class TaggingPresetSelector extends JPanel implements SelectionChangedLis 506 400 init(); 507 401 } 508 402 509 public synchronized void clearSelection() {510 lsResult.getSelectionModel().clearSelection();511 }512 513 403 /** 514 404 * Save checkbox values in preferences for future reuse 515 405 */ … … public class TaggingPresetSelector extends JPanel implements SelectionChangedLis 546 436 public synchronized void setSelectedPreset(TaggingPreset p) { 547 437 lsResult.setSelectedValue(p, true); 548 438 } 549 550 public synchronized int getItemCount() {551 return lsResultModel.getSize();552 }553 554 public void setDblClickListener(ActionListener dblClickListener) {555 this.dblClickListener = dblClickListener;556 }557 558 public void setClickListener(ActionListener clickListener) {559 this.clickListener = clickListener;560 }561 562 /**563 * Adds a selection listener to the presets list.564 * @param selectListener The list selection listener565 * @since 7412566 */567 public synchronized void addSelectionListener(ListSelectionListener selectListener) {568 lsResult.getSelectionModel().addListSelectionListener(selectListener);569 listSelectionListeners.add(selectListener);570 }571 572 /**573 * Removes a selection listener from the presets list.574 * @param selectListener The list selection listener575 * @since 7412576 */577 public synchronized void removeSelectionListener(ListSelectionListener selectListener) {578 listSelectionListeners.remove(selectListener);579 lsResult.getSelectionModel().removeListSelectionListener(selectListener);580 }581 439 } -
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 }
