Ticket #20573: 20573-0.1.diff
File 20573-0.1.diff, 74.5 KB (added by , 4 years ago) |
---|
-
src/org/openstreetmap/josm/data/preferences/sources/ExtendedSourceEntry.java
49 49 } 50 50 51 51 private static void appendRow(StringBuilder s, String th, String td) { 52 s.append("<tr><th>").append(th).append("</th><td>").append(Utils.escapeReservedCharactersHTML(td)).append("</td </tr>");52 s.append("<tr><th>").append(th).append("</th><td>").append(Utils.escapeReservedCharactersHTML(td)).append("</td></tr>"); 53 53 } 54 54 55 55 /** -
src/org/openstreetmap/josm/gui/preferences/DefaultTabPreferenceSetting.java
48 48 this.tabpane = tabpane; 49 49 this.subSettingMap = tabpane != null ? new HashMap<>() : null; 50 50 if (tabpane != null) { 51 tabpane.addMouseWheelListener(new PreferenceTabbedPane. WheelListener(tabpane));51 tabpane.addMouseWheelListener(new PreferenceTabbedPane.MouseAndWheelListener(tabpane)); 52 52 } 53 53 } 54 54 -
src/org/openstreetmap/josm/gui/preferences/PreferenceTabbedPane.java
10 10 import java.awt.FontMetrics; 11 11 import java.awt.GridBagConstraints; 12 12 import java.awt.GridBagLayout; 13 import java.awt.event.MouseEvent; 14 import java.awt.event.MouseListener; 13 15 import java.awt.event.MouseWheelEvent; 14 16 import java.awt.event.MouseWheelListener; 15 17 import java.util.ArrayList; … … 60 62 import org.openstreetmap.josm.gui.preferences.plugin.PluginPreference; 61 63 import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference; 62 64 import org.openstreetmap.josm.gui.preferences.remotecontrol.RemoteControlPreference; 65 import org.openstreetmap.josm.gui.preferences.search.SearchPanel; 66 import org.openstreetmap.josm.gui.preferences.search.SearchTextField; 63 67 import org.openstreetmap.josm.gui.preferences.server.ProxyPreference; 64 68 import org.openstreetmap.josm.gui.preferences.server.ServerAccessPreference; 65 69 import org.openstreetmap.josm.gui.preferences.shortcut.ShortcutPreference; … … 204 208 boolean validatePreferences(); 205 209 } 206 210 207 p rivateinterface PreferenceTab {211 public interface PreferenceTab { 208 212 TabPreferenceSetting getTabPreferenceSetting(); 209 213 210 214 Component getComponent(); … … 273 277 } 274 278 } 275 279 280 private final SearchPanel searchPanel = new SearchPanel(this); 281 private final SearchTextField searchTextField = new SearchTextField(searchPanel); 282 276 283 // all created tabs 277 284 private final transient List<PreferenceTab> tabs = new ArrayList<>(); 278 285 private static final Collection<PreferenceSettingFactory> SETTINGS_FACTORIES = new LinkedList<>(); … … 463 470 */ 464 471 public PreferenceTabbedPane() { 465 472 super(SwingConstants.LEFT, JTabbedPane.SCROLL_TAB_LAYOUT); 466 super.addMouseWheelListener(new WheelListener(this)); 473 MouseAndWheelListener l = new MouseAndWheelListener(this); 474 super.addMouseWheelListener(l); 475 super.addMouseListener(l); 467 476 ExpertToggleAction.addExpertModeChangeListener(this); 468 477 } 469 478 … … 526 535 if (clear) { 527 536 removeAll(); 528 537 } 538 539 addTab(null, searchPanel); 540 setTabComponentAt(0, searchTextField); 529 541 // Compute max tab length in pixels 530 542 int maxWidth = computeMaxTabWidth(); 531 543 // Inspect each tab setting … … 557 569 Logging.debug("{0}: hiding empty {1}", getClass().getSimpleName(), tps); 558 570 }); 559 571 } 572 searchTextField.adjustWidth(); 560 573 setSelectedIndex(-1); 561 574 } 562 575 … … 626 639 * This mouse wheel listener reacts when a scroll is carried out over the 627 640 * tab strip and scrolls one tab/down or up, selecting it immediately. 628 641 */ 629 static final class WheelListener implements MouseWheelListener {642 static final class MouseAndWheelListener implements MouseWheelListener, MouseListener { 630 643 631 644 final JTabbedPane tabbedPane; 632 645 633 WheelListener(JTabbedPane tabbedPane) {646 MouseAndWheelListener(JTabbedPane tabbedPane) { 634 647 this.tabbedPane = tabbedPane; 635 648 } 636 649 … … 646 659 647 660 tabbedPane.setSelectedIndex(newTab); 648 661 } 662 663 @Override 664 public void mouseClicked(MouseEvent e) { 665 tabbedPane.requestFocus(); 666 } 667 668 @Override 669 public void mouseEntered(MouseEvent e) { 670 } 671 672 @Override 673 public void mouseExited(MouseEvent e) { 674 } 675 676 @Override 677 public void mousePressed(MouseEvent e) { 678 } 679 680 @Override 681 public void mouseReleased(MouseEvent e) { 682 } 649 683 } 650 684 651 685 @Override … … 653 687 int index = getSelectedIndex(); 654 688 Component sel = getSelectedComponent(); 655 689 if (index > -1 && sel instanceof PreferenceTab) { 656 PreferenceTab tab = (PreferenceTab) sel; 657 TabPreferenceSetting preferenceSettings = tab.getTabPreferenceSetting(); 658 if (!settingsInitialized.contains(preferenceSettings)) { 659 try { 660 getModel().removeChangeListener(this); 661 preferenceSettings.addGui(this); 662 // Add GUI for sub preferences 663 for (PreferenceSetting setting : settings) { 664 if (setting instanceof SubPreferenceSetting) { 665 addSubPreferenceSetting(preferenceSettings, (SubPreferenceSetting) setting); 666 } 690 initializeTab(index, (PreferenceTab) sel, false); 691 } 692 } 693 694 public void initializeTab(int index, PreferenceTab tab, boolean silent) { 695 TabPreferenceSetting preferenceSettings = tab.getTabPreferenceSetting(); 696 if (!settingsInitialized.contains(preferenceSettings)) { 697 try { 698 getModel().removeChangeListener(this); 699 preferenceSettings.addGui(this); 700 // Add GUI for sub preferences 701 for (PreferenceSetting setting : settings) { 702 if (setting instanceof SubPreferenceSetting) { 703 addSubPreferenceSetting(preferenceSettings, (SubPreferenceSetting) setting); 667 704 } 668 Icon icon = getIconAt(index);669 remove(index);670 if (index <= insertGUITabsForSetting(icon, preferenceSettings, index, computeMaxTabWidth())) {671 setSelectedIndex(index);672 }673 } catch (SecurityException ex) {674 Logging.error(ex);675 } catch (RuntimeException ex) { // NOPMD676 // allow to change most settings even if e.g. a plugin fails677 BugReportExceptionHandler.handleException(ex);678 } finally {679 settingsInitialized.add(preferenceSettings);680 getModel().addChangeListener(this);681 705 } 706 Icon icon = getIconAt(index); 707 remove(index); 708 if (index <= insertGUITabsForSetting(icon, preferenceSettings, index, computeMaxTabWidth()) && !silent) { 709 setSelectedIndex(index); 710 } 711 } catch (SecurityException ex) { 712 Logging.error(ex); 713 } catch (RuntimeException ex) { // NOPMD 714 // allow to change most settings even if e.g. a plugin fails 715 BugReportExceptionHandler.handleException(ex); 716 } finally { 717 settingsInitialized.add(preferenceSettings); 718 getModel().addChangeListener(this); 682 719 } 683 Container ancestor = getTopLevelAncestor();684 if (ancestor instanceof PreferenceDialog) {685 ((PreferenceDialog) ancestor).setHelpContext(preferenceSettings.getHelpContext());686 }687 720 } 721 Container ancestor = getTopLevelAncestor(); 722 if (ancestor instanceof PreferenceDialog) { 723 ((PreferenceDialog) ancestor).setHelpContext(preferenceSettings.getHelpContext()); 724 } 688 725 } 689 726 690 727 private void addSubPreferenceSetting(TabPreferenceSetting preferenceSettings, SubPreferenceSetting sps) { -
src/org/openstreetmap/josm/gui/preferences/advanced/AdvancedPreference.java
52 52 import org.openstreetmap.josm.gui.preferences.PreferenceSetting; 53 53 import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory; 54 54 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane; 55 import org.openstreetmap.josm.gui.preferences.search.NotSearchablePanel; 55 56 import org.openstreetmap.josm.gui.util.GuiHelper; 56 57 import org.openstreetmap.josm.gui.widgets.AbstractFileChooser; 57 58 import org.openstreetmap.josm.gui.widgets.JosmTextField; … … 164 165 165 166 @Override 166 167 public void addGui(final PreferenceTabbedPane gui) { 167 JPanel p = gui.createPreferenceTab(this);168 JPanel panel = gui.createPreferenceTab(this); 168 169 170 NotSearchablePanel p = new NotSearchablePanel(new GridBagLayout()); 171 panel.add(p, GBC.std().fill()); 172 169 173 final JPanel txtFilterPanel = new JPanel(new GridBagLayout()); 170 174 p.add(txtFilterPanel, GBC.eol().fill(GBC.HORIZONTAL)); 171 175 txtFilter = new JosmTextField(); -
src/org/openstreetmap/josm/gui/preferences/search/ISearchableComponent.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.preferences.search; 3 4 import java.util.List; 5 6 /** 7 * Interface allowing components in the preferences to determine if and how they can be searched 8 * @author Bjoeni 9 */ 10 public interface ISearchableComponent { 11 /** 12 * @return whether the component can be searched<br><br> 13 * default: true 14 */ 15 default boolean isSearchable() { 16 return true; 17 } 18 19 /** 20 * @return whether the children of this component should be traversed <br><br> 21 * 22 * default: same as {@link #isSearchable()} unless explicitly overridden 23 */ 24 default boolean isChildrenSearchable() { 25 return isSearchable(); 26 } 27 28 /** 29 * @return the {@link SearchItem}s that should be used. Ignored if {@link #isSearchable()} returns {@code false}.<br> 30 * Overrides the default {@link SearchTextFinder} if returning not {@code null} (including if an empty list is returned)<br><br> 31 * default: null 32 */ 33 default List<SearchItem> getSearchItems() { 34 return null; 35 } 36 } -
src/org/openstreetmap/josm/gui/preferences/search/NotSearchablePanel.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.preferences.search; 3 4 import java.awt.LayoutManager; 5 6 import javax.swing.JPanel; 7 8 /** 9 * {@link JPanel} that disallows searching its contents 10 * @see ISearchableComponent 11 * @author Bjoeni 12 */ 13 public class NotSearchablePanel extends JPanel implements ISearchableComponent { 14 15 /** 16 * Create {@link JPanel} that disallows searching its contents 17 */ 18 public NotSearchablePanel() { 19 super(); 20 } 21 22 /** 23 * Create {@link JPanel} that disallows searching its contents 24 * @param layout 25 */ 26 public NotSearchablePanel(LayoutManager layout) { 27 super(layout); 28 } 29 30 /** 31 * Create {@link JPanel} that disallows searching its contents 32 * @param isDoubleBuffered 33 */ 34 public NotSearchablePanel(boolean isDoubleBuffered) { 35 super(isDoubleBuffered); 36 } 37 38 /** 39 * Create {@link JPanel} that disallows searching its contents 40 * @param layout 41 * @param isDoubleBuffered 42 */ 43 public NotSearchablePanel(LayoutManager layout, boolean isDoubleBuffered) { 44 super(layout, isDoubleBuffered); 45 } 46 47 /** 48 * The component can not be searched 49 * @return false 50 */ 51 @Override 52 public boolean isSearchable() { 53 return false; 54 } 55 56 } -
src/org/openstreetmap/josm/gui/preferences/search/SearchIndex.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.preferences.search; 3 4 import java.awt.Component; 5 import java.awt.Container; 6 import java.awt.GridBagConstraints; 7 import java.awt.GridBagLayout; 8 import java.awt.LayoutManager; 9 import java.util.ArrayList; 10 import java.util.Arrays; 11 import java.util.List; 12 import java.util.Stack; 13 14 import javax.swing.JComponent; 15 import javax.swing.JSeparator; 16 17 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane; 18 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane.PreferenceTab; 19 20 /** 21 * Contains all {@link SearchItem}s 22 * @author Bjoeni 23 */ 24 public class SearchIndex { 25 Stack<SearchItem> stack = new Stack<>(); 26 public List<SearchItem> parents = new ArrayList<>(); 27 PreferenceTabbedPane prefTabs; 28 GridBagConstraints lastConstraint; 29 30 public SearchIndex(PreferenceTabbedPane prefTabs) { 31 this.prefTabs = prefTabs; 32 } 33 34 void add(SearchItem item) { 35 if (stack.isEmpty()) { 36 item.level = 0; 37 parents.add(item); 38 stack.push(item); 39 } else { 40 SearchItem lastItem = stack.lastElement(); 41 if (!lastItem.eol && lastItem.level == item.level) { 42 lastItem.eol = item.eol; 43 lastItem.addChild(item); 44 } else if (lastItem.level < item.level || (lastItem.level == item.level && lastItem.inset < item.inset)) { 45 stack.push(lastItem.addChild(item)); 46 } else { 47 stack.pop(); 48 add(item); 49 } 50 } 51 } 52 53 void addAll(List<SearchItem> items, boolean isEOL) { 54 if (items.size() > 0) { 55 items.get(items.size() - 1).eol = isEOL; 56 } 57 items.forEach(item -> add(item)); 58 } 59 60 void insertEOL() { 61 if (!stack.isEmpty()) { 62 stack.lastElement().eol = true; 63 } 64 } 65 66 void insertSeparator() { 67 if (!stack.isEmpty()) { 68 stack.lastElement().inset = Integer.MAX_VALUE; 69 } 70 } 71 72 void insertNewPage() { 73 stack.clear(); 74 } 75 76 public void build() { 77 for (int i = 0; i < prefTabs.getTabCount(); i++) { 78 Component c = prefTabs.getComponentAt(i); 79 if (c instanceof PreferenceTab) { 80 prefTabs.initializeTab(i, (PreferenceTab) c, true); 81 c = prefTabs.getComponentAt(i); 82 } 83 insertNewPage(); 84 searchComponent(c, 1); 85 } 86 } 87 88 private boolean searchComponent(Component comp, int level) { 89 90 final Component c = comp; 91 92 boolean isEOL = true; 93 GridBagConstraints currentConstraint = null; 94 95 Container p = c.getParent(); 96 if (p != null) { 97 LayoutManager layout = p.getLayout(); 98 if (layout != null && layout instanceof GridBagLayout) { 99 GridBagLayout grid = (GridBagLayout) layout; 100 101 currentConstraint = grid.getConstraints(c); 102 isEOL = currentConstraint.gridwidth == GridBagConstraints.REMAINDER; 103 } 104 } 105 106 if (lastConstraint != null && currentConstraint != null 107 && (((lastConstraint.fill == GridBagConstraints.HORIZONTAL 108 || lastConstraint.fill == GridBagConstraints.BOTH) 109 && currentConstraint.fill != GridBagConstraints.HORIZONTAL 110 && currentConstraint.fill != GridBagConstraints.BOTH) 111 || lastConstraint.gridy != currentConstraint.gridy)) { 112 insertEOL(); 113 } 114 115 lastConstraint = currentConstraint; 116 ISearchableComponent s = null; 117 if (c instanceof ISearchableComponent) { 118 s = (ISearchableComponent) c; 119 } 120 121 if (s == null || s.isSearchable()) { 122 123 List<SearchItem> items = null; 124 if (s != null) { 125 items = s.getSearchItems(); 126 } 127 if (items == null) { 128 List<SearchItem> itm = new ArrayList<>(); 129 SearchTextFinder.DEFAULT_SEARCH_TEXT_FINDERS.forEach(finder -> itm.addAll(finder.getSearchItems(c))); 130 items = itm; 131 } 132 if (items.size() == 0) { 133 if (isEOL) { 134 insertEOL(); 135 } 136 if (c instanceof JSeparator) { 137 insertSeparator(); 138 } 139 140 } else { 141 items.get(0).components.add(c); 142 if (c instanceof JComponent) { 143 items.get(0).tooltip = ((JComponent) c).getToolTipText(); 144 } 145 146 final int inset = currentConstraint == null ? 0 : currentConstraint.insets.left; 147 items.forEach(item -> { 148 item.level = level; 149 item.inset = inset; 150 }); 151 152 addAll(items, isEOL); 153 } 154 } 155 156 if ((s == null || s.isChildrenSearchable()) && c instanceof Container) { 157 Container cont = (Container) c; 158 List<Component> components = Arrays.asList(cont.getComponents()); 159 if (components.size() > 0) { 160 insertEOL(); 161 for (Component component : components) { 162 searchComponent(component, level + 1); 163 } 164 } 165 } 166 return isEOL; 167 } 168 169 } 170 No newline at end of file -
src/org/openstreetmap/josm/gui/preferences/search/SearchItem.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.preferences.search; 3 4 import java.awt.Component; 5 import java.util.ArrayList; 6 import java.util.List; 7 import java.util.Objects; 8 import java.util.Optional; 9 10 /** 11 * Contains searchable items and their components 12 * @author Bjoeni 13 */ 14 public class SearchItem { 15 List<Component> components = new ArrayList<>(); 16 String text; 17 String tooltip; 18 int level; 19 int inset = 0; 20 public boolean eol = true; 21 public List<SearchItem> children = new ArrayList<>(); 22 23 public SearchItem() { 24 } 25 26 public SearchItem(String text) { 27 this.text = text; 28 } 29 30 public SearchItem(Component component, String text, String tooltip, boolean eol) { 31 this.components.add(component); 32 this.text = text; 33 this.tooltip = tooltip; 34 this.eol = eol; 35 } 36 37 public SearchItem(Component component, String text, String tooltip, int level, int inset) { 38 this.components.add(component); 39 this.text = text; 40 this.tooltip = tooltip; 41 this.level = level; 42 this.inset = inset; 43 } 44 45 public void merge(SearchItem item) { 46 this.components.addAll(item.components); 47 this.eol = this.eol || item.eol; 48 } 49 50 @Override 51 public String toString() { 52 return text + (tooltip == null ? "" : " (Tooltip: " + tooltip + ")") + " [" + level + "." + inset + "]"; 53 } 54 55 public SearchItem addChild(SearchItem item) { 56 Optional<SearchItem> match = children.stream() 57 .filter(c -> Objects.equals(c.text, item.text) && c.level == item.level && c.inset == item.inset) 58 .findAny(); 59 60 if (match.isPresent()) { 61 match.get().merge(item); 62 return match.get(); 63 } else { 64 children.add(item); 65 return item; 66 } 67 } 68 } 69 No newline at end of file -
src/org/openstreetmap/josm/gui/preferences/search/SearchPanel.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.preferences.search; 3 4 import java.io.File; 5 import java.io.IOException; 6 import java.util.List; 7 8 import javax.swing.JLabel; 9 import javax.swing.JScrollPane; 10 import javax.swing.filechooser.FileSystemView; 11 12 import org.apache.commons.io.FileUtils; 13 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane; 14 import org.openstreetmap.josm.tools.Logging; 15 16 /** 17 * Panel displaying search results in the preferences 18 * @author Bjoeni 19 */ 20 public class SearchPanel extends JScrollPane implements ISearchableComponent { 21 22 private PreferenceTabbedPane tabs; 23 private SearchIndex searchIndex; 24 private JLabel lbl = new JLabel("search results here"); 25 26 27 public SearchPanel(PreferenceTabbedPane tabs) { 28 this.tabs = tabs; 29 add(lbl); 30 } 31 32 public PreferenceTabbedPane getTabPane() { 33 return tabs; 34 } 35 36 public void search(String text) { 37 searchIndex = new SearchIndex(tabs); 38 39 searchIndex.build(); 40 Logging.info("searched"); 41 searchableSettings = new StringBuilder(); 42 printSearchItems(searchIndex.parents, ""); 43 try { 44 FileUtils.writeStringToFile(new File(FileSystemView.getFileSystemView().getHomeDirectory().getAbsolutePath() 45 + "/searchableSettings.txt"), searchableSettings.toString()); 46 } catch (IOException e) { 47 e.printStackTrace(); 48 } 49 lbl.setText(searchableSettings.toString()); 50 } 51 52 StringBuilder searchableSettings; 53 54 private void printSearchItems(List<SearchItem> items, String indent) { 55 items.forEach(item -> { 56 searchableSettings.append("\n" + indent + item.toString().replaceAll("\n", "<br>")); 57 if (item.eol) { 58 searchableSettings.append("\n" + indent + "=="); 59 } 60 if (item.children.size() > 0) { 61 searchableSettings.append("\n" + indent + ">>"); 62 printSearchItems(item.children, indent + " "); 63 searchableSettings.append("\n" + indent + "<<"); 64 } 65 }); 66 } 67 68 @Override 69 public boolean isSearchable() { 70 return false; 71 } 72 73 } -
src/org/openstreetmap/josm/gui/preferences/search/SearchTextField.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.preferences.search; 3 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 6 import java.awt.Dimension; 7 8 import javax.swing.event.DocumentEvent; 9 import javax.swing.event.DocumentListener; 10 11 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane; 12 import org.openstreetmap.josm.gui.widgets.JosmTextField; 13 14 /** 15 * TextField for searching the preferences 16 * @author Bjoeni 17 */ 18 public class SearchTextField extends JosmTextField { 19 private SearchPanel panel; 20 private PreferenceTabbedPane tabs; 21 22 public SearchTextField(SearchPanel panel) { 23 24 this.panel = panel; 25 tabs = panel.getTabPane(); 26 setHint(tr("Search...")); 27 getDocument().addDocumentListener(new DocumentListener() { 28 29 @Override 30 public void removeUpdate(DocumentEvent e) { 31 panel.getTabPane().setSelectedIndex(0); 32 panel.search(getTextContent()); 33 } 34 35 @Override 36 public void insertUpdate(DocumentEvent e) { 37 panel.getTabPane().setSelectedIndex(0); 38 panel.search(getTextContent()); 39 40 } 41 42 @Override 43 public void changedUpdate(DocumentEvent e) { 44 } 45 }); 46 } 47 48 public void adjustWidth() { 49 int width = getPreferredSize().width; 50 for (int i = 0; i < tabs.getTabCount(); i++) { 51 width = Math.max(width, tabs.getBoundsAt(i).width); 52 } 53 setPreferredSize(new Dimension(width, getPreferredSize().height)); 54 } 55 56 @Override 57 public String getText() { 58 return tr("Search"); 59 } 60 61 public String getTextContent() { 62 return super.getText(); 63 } 64 65 } -
src/org/openstreetmap/josm/gui/preferences/search/SearchTextFinder.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.preferences.search; 3 4 import java.awt.Component; 5 import java.util.ArrayList; 6 import java.util.Arrays; 7 import java.util.Collections; 8 import java.util.List; 9 import java.util.stream.Collectors; 10 11 import javax.swing.JCheckBox; 12 import javax.swing.JComboBox; 13 import javax.swing.JLabel; 14 import javax.swing.JList; 15 import javax.swing.JRadioButton; 16 import javax.swing.JTable; 17 import javax.swing.table.TableModel; 18 19 /** 20 * Class for obtaining the searchable properties of arbitrary components 21 * @param <T> type of the component 22 * @author Bjoeni 23 */ 24 public class SearchTextFinder<T extends Component> { 25 List<IComponentProperties<T>> properties = new ArrayList<>(); 26 27 @FunctionalInterface 28 private interface IComponentProperties<U extends Component> { 29 Object getFrom(U component); 30 31 default List<SearchItem> getSearchItems(U component) { 32 Object obj = getFrom(component); 33 34 if (obj == null) { 35 return new ArrayList<>(); 36 } 37 38 if (obj instanceof String) { 39 return Arrays.asList(new SearchItem((String) obj)); 40 } 41 42 if (obj instanceof String[]) { 43 return Arrays.asList((String[]) obj).stream().map(SearchItem::new).collect(Collectors.toList()); 44 } 45 46 if (obj instanceof SearchItem[]) { 47 return Arrays.asList((SearchItem[]) obj); 48 } 49 50 throw new IllegalArgumentException(); 51 } 52 } 53 54 @FunctionalInterface 55 interface ComponentProperty<U extends Component> extends IComponentProperties<U> { 56 @Override 57 String getFrom(U component); 58 } 59 60 @FunctionalInterface 61 interface ComponentPropertyArray<U extends Component> extends IComponentProperties<U> { 62 @Override 63 String[] getFrom(U component); 64 } 65 66 @FunctionalInterface 67 interface ComponentPropertyItem<U extends Component> extends IComponentProperties<U> { 68 @Override 69 SearchItem[] getFrom(U component); 70 } 71 72 /** 73 * Instantiates {@link SearchTextFinder} 74 */ 75 public SearchTextFinder() { 76 } 77 78 /** 79 * Instantiates {@link SearchTextFinder} 80 * @param prop Expression returning a searchable string for the given component, e.g. {@code JLabel::getText}. 81 * @see #add(ComponentProperty) 82 * @see #addArray(ComponentPropertyArray) 83 */ 84 public SearchTextFinder(ComponentProperty<T> prop) { 85 properties.add(prop); 86 } 87 88 /** 89 * Add another property. 90 * The compiler can't deal with generic vararg parameters properly, so just use this function instead 91 * @param prop Expression returning a searchable string for the given component, e.g. {@code JLabel::getText}. 92 * @return this (for chaining) 93 */ 94 public SearchTextFinder<T> add(ComponentProperty<T> prop) { 95 properties.add(prop); 96 return this; 97 } 98 99 /** 100 * Add a property returning a string array. 101 * The compiler can't deal with generic vararg parameters properly, so just use this function instead 102 * @param prop Expression returning an array of searchable strings for the given component 103 * @return this (for chaining) 104 */ 105 public SearchTextFinder<T> addArray(ComponentPropertyArray<T> prop) { 106 properties.add(prop); 107 return this; 108 } 109 110 /** 111 * Add a property returning a string array. 112 * The compiler can't deal with generic vararg parameters properly, so just use this function instead 113 * @param prop Expression returning an array of searchable strings for the given component 114 * @return this (for chaining) 115 */ 116 public SearchTextFinder<T> addItem(ComponentPropertyItem<T> prop) { 117 properties.add(prop); 118 return this; 119 } 120 121 /** 122 * Get the searchable texts for the given component 123 * @param c component 124 * @return {@code List<String>} or an empty list if it's not the right type. 125 */ 126 public List<SearchItem> getSearchItems(Component c) { 127 List<SearchItem> ret = new ArrayList<>(); 128 129 try { 130 @SuppressWarnings("unchecked") 131 T component = (T) c; 132 if (component != null) { 133 properties.forEach(property -> { 134 List<SearchItem> items = property.getSearchItems(component); 135 items.forEach(item -> { 136 if (item.text != null && item.text.trim().length() > 0) { 137 if (item.text.indexOf('<') != -1) { 138 item.text = stripHtml(item.text); 139 } 140 if (item.text.indexOf(':') == item.text.length() - 1) { 141 item.text = item.text.substring(0, item.text.length() - 1); 142 } 143 if (item.tooltip != null && item.tooltip.indexOf('<') != -1) { 144 item.tooltip = stripHtml(item.tooltip); 145 } 146 ret.add(item); 147 } 148 }); 149 }); 150 } 151 152 } catch (ClassCastException ex) { 153 // can't use 'instanceof' here, because the type information of T will already be stripped away at runtime 154 } 155 return ret; 156 } 157 158 private String stripHtml(String text) { 159 return text 160 .replaceAll("<br\s*/?>", "\n") 161 .replaceAll("(<style>.*</style>|<[^<>]*>)", " ") 162 .trim() 163 .replaceAll(" +", " "); 164 } 165 166 public final static List<SearchTextFinder<?>> DEFAULT_SEARCH_TEXT_FINDERS = Collections.unmodifiableList(Arrays.asList( 167 168 new SearchTextFinder<>(JLabel::getText), 169 new SearchTextFinder<>(JRadioButton::getText), 170 new SearchTextFinder<>(JCheckBox::getText), 171 172 new SearchTextFinder<JTable>().addItem(table -> { 173 List<SearchItem> lst = new ArrayList<>(); 174 TableModel tm = table.getModel(); 175 for (int row = 0; row < tm.getRowCount(); row++) { 176 for (int col = 0; col < tm.getColumnCount(); col++) { 177 Object obj = tm.getValueAt(row, col); 178 Component renderer = table.getCellRenderer(row, col) 179 .getTableCellRendererComponent(table, obj, false, false, row, col); 180 if (renderer instanceof JLabel) { 181 JLabel label = (JLabel) renderer; 182 boolean isLast = col == tm.getColumnCount() - 1; 183 lst.add(new SearchItem(label, label.getText(), label.getToolTipText(), isLast)); 184 } 185 } 186 } 187 return lst.toArray(new SearchItem[0]); 188 }), 189 190 new SearchTextFinder<JComboBox<Object>>().addItem(combobox -> { 191 List<SearchItem> lst = new ArrayList<>(); 192 int size = combobox.getItemCount(); 193 JList<Object> listStub = new JList<>(); 194 for (int i = 0; i < size; i++) { 195 Component renderer = combobox.getRenderer().getListCellRendererComponent(listStub, 196 combobox.getItemAt(i), i, false, false); 197 if (renderer instanceof JLabel) { 198 JLabel label = (JLabel) renderer; 199 lst.add(new SearchItem(renderer, label.getText(), label.getToolTipText(), false)); 200 } 201 } 202 return lst.toArray(new SearchItem[0]); 203 }))); 204 205 } 206 No newline at end of file -
src/org/openstreetmap/josm/gui/preferences/search/package-info.java
1 package org.openstreetmap.josm.gui.preferences.search; 2 No newline at end of file -
src/org/openstreetmap/josm/gui/preferences/shortcut/PrefJPanel.java
15 15 import java.awt.event.KeyEvent; 16 16 import java.awt.im.InputContext; 17 17 import java.lang.reflect.Field; 18 import java.util.ArrayList; 18 19 import java.util.LinkedHashMap; 19 20 import java.util.List; 20 21 import java.util.Map; … … 39 40 import javax.swing.table.TableColumnModel; 40 41 41 42 import org.openstreetmap.josm.data.preferences.NamedColorProperty; 43 import org.openstreetmap.josm.gui.preferences.search.SearchItem; 44 import org.openstreetmap.josm.gui.preferences.search.ISearchableComponent; 42 45 import org.openstreetmap.josm.gui.util.GuiHelper; 43 46 import org.openstreetmap.josm.gui.util.TableHelper; 44 47 import org.openstreetmap.josm.gui.widgets.FilterField; … … 50 53 /** 51 54 * This is the keyboard preferences content. 52 55 */ 53 public class PrefJPanel extends JPanel {56 public class PrefJPanel extends JPanel implements ISearchableComponent { 54 57 55 58 // table of shortcuts 56 59 private final AbstractTableModel model; … … 363 366 } 364 367 } 365 368 } 369 370 @Override 371 public boolean isChildrenSearchable() { 372 return false; 373 } 374 375 @Override 376 public List<SearchItem> getSearchItems() { 377 List<SearchItem> list = new ArrayList<>(); 378 for (int row = 0; row < model.getRowCount(); row++) { 379 list.add(new SearchItem(model.getValueAt(row, 0).toString())); 380 } 381 return list; 382 } 366 383 }