Ticket #20573: 20573-0.3.diff
File 20573-0.3.diff, 127.5 KB (added by , 4 years ago) |
---|
-
src/org/openstreetmap/josm/data/validation/Test.java
### Eclipse Workspace Patch 1.0 #P josm-search
274 274 public boolean ok() { 275 275 enabled = checkEnabled.isSelected(); 276 276 testBeforeUpload = checkBeforeUpload.isSelected(); 277 checkBeforeUpload = null; //prevent memory leak (keeps reference to parent dialog) 277 278 return false; 278 279 } 279 280 -
src/org/openstreetmap/josm/gui/preferences/PreferenceDialog.java
41 41 */ 42 42 public class PreferenceDialog extends JDialog { 43 43 44 private finalPreferenceTabbedPane tpPreferences = new PreferenceTabbedPane();44 private PreferenceTabbedPane tpPreferences = new PreferenceTabbedPane(); 45 45 private final ContextSensitiveHelpAction helpAction = new ContextSensitiveHelpAction(); 46 46 private final WindowEventHandler windowEventHandler = new WindowEventHandler(); 47 47 private boolean canceled; … … 154 154 } else if (previouslySelected != null && previouslySelected.a != null) { 155 155 tpPreferences.selectTabByPref(previouslySelected.a); 156 156 } else { 157 tpPreferences.setSelectedIndex( 0);157 tpPreferences.setSelectedIndex(1); 158 158 } 159 159 } 160 160 … … 232 232 previouslySelected = tpPreferences.getSelectedTab(); 233 233 removeWindowListener(windowEventHandler); 234 234 setVisible(false); // save current geometry 235 //removeAll(); 236 tpPreferences.searchPanel.destroyIndex(); 235 237 super.dispose(); 236 238 } 237 239 } -
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.Color; 5 import java.awt.Component; 6 import java.awt.Rectangle; 7 import java.lang.reflect.InvocationTargetException; 8 import java.util.ArrayList; 9 import java.util.List; 10 import java.util.Objects; 11 import java.util.Optional; 12 13 import javax.swing.ImageIcon; 14 import javax.swing.JComponent; 15 import javax.swing.SwingUtilities; 16 import javax.swing.Timer; 17 18 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane; 19 import org.openstreetmap.josm.gui.util.GuiHelper; 20 import org.openstreetmap.josm.tools.ImageProvider; 21 import org.openstreetmap.josm.tools.ImageProvider.ImageSizes; 22 import org.openstreetmap.josm.tools.Logging; 23 24 /** 25 * Contains searchable items and their components 26 * @author Bjoeni 27 */ 28 public class SearchItem { 29 int level; 30 int inset; 31 private String text; 32 private String tooltip; 33 private String highlight; 34 private List<SearchItem> children = new ArrayList<>(); 35 private List<Component> originalComponents = new ArrayList<>(); 36 37 private int tabIndex; 38 private boolean eol = true; 39 private boolean visible = true; 40 private boolean hasVisibleChildren; 41 private boolean childComponentsSearchable = true; 42 private String iconName; 43 private String textNormalized; 44 private String tooltipNormalized; 45 private PreferenceTabbedPane tabs; 46 private SearchItemComponent comp; 47 48 public SearchItem(String text) { 49 this.setText(text); 50 } 51 52 public SearchItem(Component originalComponent, String text, String tooltip, boolean eol, 53 boolean childComponentsSearchable) { 54 this.originalComponents.add(originalComponent); 55 this.setText(text); 56 this.setTooltip(tooltip); 57 this.setEOL(eol); 58 this.childComponentsSearchable = childComponentsSearchable; 59 } 60 61 public SearchItem addChild(SearchItem item) { 62 synchronized (children) { 63 Optional<SearchItem> match = children.stream() 64 .filter(c -> Objects.equals(c.text, item.text) && c.level == item.level && c.inset == item.inset) 65 .findAny(); 66 if (match.isPresent()) { 67 match.get().merge(item); 68 return match.get(); 69 } else { 70 children.add(item); 71 GuiHelper.runInEDT(() -> { 72 getComponent().addChild(item.getComponent()); 73 }); 74 return item; 75 } 76 } 77 78 } 79 80 public void merge(SearchItem item) { 81 this.originalComponents.addAll(item.originalComponents); 82 this.setEOL(this.isEOL() || item.isEOL()); 83 } 84 85 public boolean filter(SearchFilters filters) { 86 visible = this.matches(filters); 87 hasVisibleChildren = false; 88 for (SearchItem child : children) { 89 if (child.isVisible()) { 90 hasVisibleChildren = child.filter(filters) || hasVisibleChildren; 91 } 92 } 93 visible = visible || hasVisibleChildren; 94 95 if (visible && text != null && textNormalized != null) { 96 highlight = filters.highlightString(text, textNormalized); 97 } else { 98 highlight = "<html><span style=\"color: red;\"><b>ERROR</b></span></html>"; 99 } 100 return visible; 101 } 102 103 public boolean matches(SearchFilters filters) { 104 return filters.getNormalized().stream() 105 .allMatch(str -> textNormalized != null && textNormalized.indexOf(str) != -1 106 || (tooltipNormalized != null && tooltipNormalized.indexOf(str) != -1)); 107 } 108 109 public void showAll() { 110 visible = true; 111 children.forEach(SearchItem::showAll); 112 } 113 114 @Override 115 public String toString() { 116 return text + (tooltip == null ? "" : " (Tooltip: " + tooltip + ")") + " [" + level + "." + inset 117 + (eol ? ":EOL" : "") + "] {" + originalComponents.size() + "}"; 118 } 119 120 public ImageIcon getIcon() { 121 if (iconName == null) 122 return null; 123 124 return iconName == null || iconName.isEmpty() ? null 125 : iconName.contains("/") ? ImageProvider.get(iconName, ImageSizes.SMALLICON) 126 : ImageProvider.get("preferences", iconName, ImageSizes.SMALLICON); 127 } 128 129 public boolean isVisible() { 130 return visible; 131 } 132 133 public boolean isEOL() { 134 return eol; 135 } 136 137 public void setEOL() { 138 setEOL(true); 139 } 140 141 public void setEOL(boolean eol) { 142 this.eol = eol; 143 } 144 145 public void setLevelInset(int level, int inset) { 146 this.level = level; 147 this.inset = inset; 148 } 149 150 public void maximizeInset() { 151 this.inset = Integer.MAX_VALUE; 152 } 153 154 public void setTabIndex(PreferenceTabbedPane tabs, int tabIndex) { 155 this.tabs = tabs; 156 this.tabIndex = tabIndex; 157 } 158 159 public void setIconName(String iconName) { 160 this.iconName = iconName; 161 } 162 163 public void addOriginalComponent(Component c) { 164 this.originalComponents.add(c); 165 } 166 167 public void showOriginalComponent() { 168 if (tabIndex > -1) { 169 tabs.setSelectedIndex(tabIndex); 170 } 171 originalComponents.stream().filter(Objects::nonNull).forEach(comp -> { 172 //Color bg = comp.getBackground(); 173 Color fg = comp.getForeground(); 174 //Logging.debug("BG:" + bg.toString()); 175 Logging.debug("FG:" + fg.toString()); 176 //comp.setBackground(SystemColor.text); 177 comp.setForeground(SearchPanel.DARK_MODE ? Color.yellow : Color.red); 178 comp.requestFocus(); 179 if (comp instanceof JComponent) { 180 JComponent jcomp = (JComponent) comp; 181 Rectangle bounds = new Rectangle(jcomp.getBounds()); 182 jcomp.scrollRectToVisible(bounds); 183 } 184 Timer timer = new Timer(3000, l -> { 185 //comp.setBackground(bg); 186 comp.setForeground(fg); 187 }); 188 timer.setRepeats(false); 189 timer.start(); 190 }); 191 if (originalComponents.stream().filter(Objects::nonNull).noneMatch(Component::isVisible)) { 192 Logging.warn("INVIS!!"); 193 } 194 } 195 196 /** 197 * @return the component 198 */ 199 public SearchItemComponent getComponent() { 200 if (comp == null) { 201 comp = GuiHelper.runInEDTAndWaitAndReturn(() -> new SearchItemComponent(this)); 202 } 203 return comp; 204 } 205 206 public void updateComponents() throws InvocationTargetException, InterruptedException { 207 SwingUtilities.invokeAndWait(() -> { 208 //invokeLater is faster but causes too many calls at once and blocks the UI 209 SearchItemComponent c = getComponent(); 210 c.setVisible(this.isVisible()); 211 if (isVisible()) { 212 c.setToolTipText(toString()); 213 c.setText(highlight); 214 c.setExpanded(true); 215 } 216 }); 217 if (isVisible()) { 218 for (SearchItem child : children) { 219 child.updateComponents(); 220 } 221 } 222 } 223 224 /** 225 * @return the hasVisibleChildren 226 */ 227 public boolean hasVisibleChildren() { 228 return hasVisibleChildren; 229 } 230 231 /** 232 * @return the childComponentsSearchable 233 */ 234 public boolean isChildComponentsSearchable() { 235 return childComponentsSearchable; 236 } 237 238 /** 239 * @return the text2 240 */ 241 public String getText() { 242 return text; 243 } 244 245 /** 246 * @param text the text to set 247 */ 248 public void setText(String text) { 249 this.text = stripHtml(text); 250 this.textNormalized = SearchFilters.normalize(this.text); 251 } 252 253 /** 254 * @return the tooltip 255 */ 256 public String getTooltip() { 257 return tooltip; 258 } 259 260 public void addTooltip(String tooltip) { 261 if (this.getTooltip() == null || this.getTooltip().trim().isEmpty()) { 262 this.setTooltip(tooltip); 263 } 264 } 265 266 /** 267 * @param tooltip the tooltip to set 268 */ 269 public void setTooltip(String tooltip) { 270 this.tooltip = stripHtml(tooltip); 271 this.tooltipNormalized = SearchFilters.normalize(this.tooltip); 272 } 273 274 private static String stripHtml(String str) { 275 if (str == null || str.trim().length() == 0) 276 return null; 277 278 if (str.indexOf('<') > -1) { 279 str = str.replaceAll("(?i)(<br\\s/>\\s*)+", ": ").replaceAll("(<style>.*</style>|<[^<>]*>)", " ").trim() 280 .replaceAll(" +", " "); 281 } 282 283 if (str.indexOf(':') == str.length() - 1) { 284 str = str.substring(0, str.length() - 1); 285 } 286 287 return str; 288 } 289 290 } 291 No newline at end of file -
src/org/openstreetmap/josm/gui/preferences/shortcut/PrefJPanel.java
13 13 import java.awt.event.KeyEvent; 14 14 import java.awt.im.InputContext; 15 15 import java.lang.reflect.Field; 16 import java.util.ArrayList; 16 17 import java.util.LinkedHashMap; 17 18 import java.util.List; 18 19 import java.util.Map; … … 36 37 import javax.swing.table.TableColumnModel; 37 38 38 39 import org.openstreetmap.josm.data.preferences.NamedColorProperty; 40 import org.openstreetmap.josm.gui.preferences.search.SearchItem; 41 import org.openstreetmap.josm.gui.preferences.search.ISearchableComponent; 39 42 import org.openstreetmap.josm.gui.util.GuiHelper; 40 43 import org.openstreetmap.josm.gui.util.TableHelper; 41 44 import org.openstreetmap.josm.gui.widgets.FilterField; … … 48 51 /** 49 52 * This is the keyboard preferences content. 50 53 */ 51 public class PrefJPanel extends JPanel {54 public class PrefJPanel extends JPanel implements ISearchableComponent { 52 55 53 56 // table of shortcuts 54 57 private final AbstractTableModel model; … … 350 353 } 351 354 } 352 355 } 356 357 @Override 358 public boolean isChildrenSearchable() { 359 return false; 360 } 361 362 @Override 363 public List<SearchItem> getSearchItems() { 364 List<SearchItem> list = new ArrayList<>(); 365 for (int row = 0; row < model.getRowCount(); row++) { 366 list.add(new SearchItem(model.getValueAt(row, 0).toString())); 367 } 368 return list; 369 } 353 370 } -
src/org/openstreetmap/josm/gui/preferences/SourceEditor.java
83 83 import org.openstreetmap.josm.gui.HelpAwareOptionPane; 84 84 import org.openstreetmap.josm.gui.MainApplication; 85 85 import org.openstreetmap.josm.gui.PleaseWaitRunnable; 86 import org.openstreetmap.josm.gui.preferences.search.ISearchableComponent; 87 import org.openstreetmap.josm.gui.preferences.search.SearchItem; 86 88 import org.openstreetmap.josm.gui.util.DocumentAdapter; 87 89 import org.openstreetmap.josm.gui.util.FileFilterAllFiles; 88 90 import org.openstreetmap.josm.gui.util.GuiHelper; … … 110 112 * Editor for JOSM extensions source entries. 111 113 * @since 1743 112 114 */ 113 public abstract class SourceEditor extends JPanel {115 public abstract class SourceEditor extends JPanel implements ISearchableComponent { 114 116 115 117 /** the type of source entry **/ 116 118 protected final SourceType sourceType; … … 563 565 sourcesInitiallyLoaded = true; 564 566 } 565 567 568 @Override 569 public List<SearchItem> getSearchItemsAsync() { 570 if (!sourcesInitiallyLoaded && !NetworkManager.isOffline(OnlineResource.CACHE_UPDATES)) { 571 new SourceLoader(availableSourcesUrl, sourceProviders, true).run(); 572 } 573 sourcesInitiallyLoaded = true; 574 return null; 575 } 576 566 577 /** 567 578 * List model of available sources. 568 579 */ … … 1349 1360 private CachedFile cachedFile; 1350 1361 private boolean canceled; 1351 1362 private final List<ExtendedSourceEntry> sources = new ArrayList<>(); 1363 private boolean silent; 1352 1364 1353 1365 SourceLoader(String url, List<SourceProvider> sourceProviders) { 1354 super(tr(getStr(I18nString.LOADING_SOURCES_FROM), url)); 1366 this(url, sourceProviders, false); 1367 } 1368 1369 SourceLoader(String url, List<SourceProvider> sourceProviders, boolean silent) { 1370 super(tr(getStr(I18nString.LOADING_SOURCES_FROM), url), true, silent); 1355 1371 this.url = url; 1356 1372 this.sourceProviders = sourceProviders; 1373 this.silent = silent; 1357 1374 } 1358 1375 1359 1376 @Override … … 1363 1380 } 1364 1381 1365 1382 protected void warn(Exception e) { 1366 String emsg = Utils.escapeReservedCharactersHTML(e.getMessage() != null ? e.getMessage() : e.toString()); 1367 final String msg = tr(getStr(I18nString.FAILED_TO_LOAD_SOURCES_FROM), url, emsg); 1383 if (silent) { 1384 Logging.warn(e); 1385 } else { 1386 String emsg = Utils 1387 .escapeReservedCharactersHTML(e.getMessage() != null ? e.getMessage() : e.toString()); 1388 final String msg = tr(getStr(I18nString.FAILED_TO_LOAD_SOURCES_FROM), url, emsg); 1368 1389 1369 GuiHelper.runInEDT(() -> HelpAwareOptionPane.showOptionDialog( 1370 MainApplication.getMainFrame(), 1371 msg, 1372 tr("Error"), 1373 JOptionPane.ERROR_MESSAGE, 1374 ht(getStr(I18nString.FAILED_TO_LOAD_SOURCES_FROM_HELP_TOPIC)) 1375 )); 1390 GuiHelper.runInEDT(() -> HelpAwareOptionPane.showOptionDialog(MainApplication.getMainFrame(), msg, 1391 tr("Error"), JOptionPane.ERROR_MESSAGE, 1392 ht(getStr(I18nString.FAILED_TO_LOAD_SOURCES_FROM_HELP_TOPIC)))); 1393 } 1376 1394 } 1377 1395 1378 1396 @Override -
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 * 14 * default: true 15 */ 16 default boolean isSearchable() { 17 return true; 18 } 19 20 /** 21 * @return whether the children of this component should be traversed <br><br> 22 * 23 * default: same as {@link #isSearchable()} unless explicitly overridden 24 */ 25 default boolean isChildrenSearchable() { 26 return isSearchable(); 27 } 28 29 /** 30 * Called from the EventDispatchThread (GUI), should not be blocked. 31 * 32 * @return the {@link SearchItem}s that should be used. Ignored if {@link #isSearchable()} returns {@code false}.<br> 33 * Overrides the default {@link SearchTextFinder} if returning not {@code null} (including if an empty list is returned)<br><br> 34 * 35 * default: null 36 */ 37 default List<SearchItem> getSearchItems() { 38 return null; 39 } 40 41 /** 42 * Called from the worker thread, can be blocked as long as necessary. Calls to the GUI must be placed in the EventDispatchThread. 43 * 44 * @return the {@link SearchItem}s that should be used. Ignored if {@link #isSearchable()} returns {@code false}.<br> 45 * Overrides the default {@link SearchTextFinder} if returning not {@code null} (including if an empty list is returned)<br><br> 46 * 47 * default: null 48 */ 49 default List<SearchItem> getSearchItemsAsync() { 50 return null; 51 } 52 53 /** 54 * @return 55 */ 56 default String getIconName() { 57 return null; 58 } 59 60 } -
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/search/SearchPanel.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.GridBagLayout; 7 import java.awt.SystemColor; 8 import java.lang.reflect.InvocationTargetException; 9 10 import javax.swing.BorderFactory; 11 import javax.swing.JLabel; 12 import javax.swing.JPanel; 13 import javax.swing.JScrollPane; 14 import javax.swing.SwingConstants; 15 import javax.swing.SwingUtilities; 16 17 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane; 18 import org.openstreetmap.josm.tools.GBC; 19 import org.openstreetmap.josm.tools.JosmRuntimeException; 20 import org.openstreetmap.josm.tools.Logging; 21 22 /** 23 * Panel displaying search results in the preferences 24 * @author Bjoeni 25 */ 26 public class SearchPanel extends JScrollPane implements ISearchableComponent { 27 28 public static final boolean DARK_MODE = 130 < (Math.sqrt(Math.pow(SystemColor.text.getRed(), 2) * .241 29 + Math.pow(SystemColor.text.getGreen(), 2) * .691 + Math.pow(SystemColor.text.getBlue(), 2) * .068)); 30 31 private PreferenceTabbedPane tabs; 32 private SearchIndex searchIndex; 33 private JLabel lblStatus = new JLabel(tr("Enter something in the text fied to start searching")); 34 private JPanel panel = new JPanel(new GridBagLayout()); 35 private SearchThread thread; 36 37 public SearchPanel(PreferenceTabbedPane tabs) { 38 super(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 39 this.tabs = tabs; 40 searchIndex = new SearchIndex(tabs); 41 GBC gbc = GBC.eol().fill(GBC.HORIZONTAL); 42 gbc.anchor = GBC.NORTHWEST; 43 gbc.weightx = 1; 44 panel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15)); 45 panel.add(searchIndex.getPanel(), gbc); 46 gbc.gridy = 1; 47 lblStatus.setHorizontalAlignment(SwingConstants.CENTER); 48 gbc.fill(); 49 gbc.anchor = GBC.CENTER; 50 panel.add(lblStatus, gbc); 51 //setAlignmentX(LEFT_ALIGNMENT); 52 setViewportView(panel); 53 } 54 55 public void search(String text) { 56 if (thread != null && thread.isAlive()) { 57 Logging.debug("alive"); 58 thread.awaitBuildAndRestart(text); 59 } else { 60 Logging.debug("dead"); 61 thread = new SearchThread(text); 62 thread.start(); 63 } 64 } 65 66 public PreferenceTabbedPane getTabPane() { 67 return tabs; 68 } 69 70 public void destroyIndex() { 71 removeAll(); 72 panel = null; 73 lblStatus = null; 74 setViewport(null); 75 this.searchIndex = null; 76 } 77 78 @Override 79 public boolean isSearchable() { 80 return false; 81 } 82 83 class SearchThread extends Thread { 84 private String text; 85 private StringBuilder searchableSettings; 86 private boolean building; 87 private boolean interrupted; 88 private String nextText; 89 private boolean justBuiltIndex; 90 91 public SearchThread(String text) { 92 this.text = text; 93 } 94 95 public SearchThread(String text, boolean justBuiltIndex) { 96 this.text = text; 97 this.justBuiltIndex = justBuiltIndex; 98 } 99 100 @Override 101 public void run() { 102 try { 103 boolean buildIndex = !searchIndex.isBuilt(); // || "".equals(text); 104 if (buildIndex) { 105 building = true; 106 Logging.info("Building search index..."); 107 setText(tr("Building search index...")); 108 searchIndex.build(); 109 building = false; 110 if (interrupted) { 111 startNext(true); 112 return; 113 } 114 } 115 116 if (buildIndex || justBuiltIndex) { 117 setText(tr("Searching...")); 118 } 119 120 //if (!"".equals(text)) { 121 Logging.debug("searching \"" + text + "\""); 122 boolean found = searchIndex.filter(text); 123 Logging.debug("searched \"" + text + "\""); 124 //} 125 126 Logging.debug("before \"" + text + "\""); 127 searchIndex.updateComponents(); 128 Logging.debug("done \"" + text + "\""); 129 130 String txt = ""; 131 if (!found) { 132 txt += tr("No results found.") + "<br><br>"; 133 } 134 if (false) { //expertmode 135 txt += tr("You might get more results by enabling expert mode."); 136 } 137 setText(txt); 138 139 } catch (InterruptedException e) { 140 Logging.debug("Interrupted search thread \"" + text + "\""); 141 // to be expected 142 } catch (InvocationTargetException e) { 143 throw new JosmRuntimeException(e); 144 } 145 } 146 147 public void awaitBuildAndRestart(String nextText) { 148 this.nextText = nextText; 149 if (building) { 150 interrupted = true; 151 } else { 152 interrupt(); 153 startNext(false); 154 } 155 } 156 157 private void startNext(boolean justBuiltIndex) { 158 thread = new SearchThread(nextText, justBuiltIndex); 159 thread.start(); 160 } 161 162 private void setText(String txt) { 163 SwingUtilities.invokeLater(() -> { 164 lblStatus.setText(txt); 165 }); 166 } 167 } 168 169 } -
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.lang.reflect.InvocationTargetException; 10 import java.util.ArrayList; 11 import java.util.Arrays; 12 import java.util.List; 13 import java.util.Locale; 14 import java.util.Stack; 15 import java.util.stream.Collectors; 16 17 import javax.swing.JComponent; 18 import javax.swing.JPanel; 19 import javax.swing.JSeparator; 20 import javax.swing.SwingUtilities; 21 22 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane; 23 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane.PreferenceTab; 24 import org.openstreetmap.josm.gui.util.GuiHelper; 25 import org.openstreetmap.josm.tools.GBC; 26 import org.openstreetmap.josm.tools.LanguageInfo; 27 28 /** 29 * Contains all {@link SearchItem}s 30 * @author Bjoeni 31 */ 32 public class SearchIndex { 33 public static final Locale CURRENT_LOCALE = LanguageInfo.getLocale(LanguageInfo.getJOSMLocaleCode()); 34 public static final GBC EOL = GBC.eol(); 35 36 Stack<SearchItem> stack = new Stack<>(); 37 List<SearchItem> parents = new ArrayList<>(); 38 PreferenceTabbedPane prefTabs; 39 GridBagConstraints lastConstraint; 40 private boolean built; 41 private String lastFilterStr; 42 private int currentTabIndex = -1; 43 44 private JPanel panel = GuiHelper.runInEDTAndWaitAndReturn(() -> new JPanel(new GridBagLayout())); 45 46 GridBagConstraints gbc_nw; 47 48 public SearchIndex(PreferenceTabbedPane prefTabs) { 49 this.prefTabs = prefTabs; 50 gbc_nw = GBC.eol().fill(GBC.HORIZONTAL); 51 gbc_nw.anchor = GBC.NORTHWEST; 52 } 53 54 void add(SearchItem item) { 55 if (item.getText() == null) 56 return; 57 58 item.setTabIndex(prefTabs, currentTabIndex); 59 if (stack.isEmpty()) { 60 item.setLevelInset(0, 0); 61 parents.add(item); 62 GuiHelper.runInEDT(() -> { 63 getPanel().add(item.getComponent(), gbc_nw); 64 }); 65 stack.push(item); 66 } else { 67 SearchItem lastItem = stack.lastElement(); 68 if (!lastItem.isEOL() && lastItem.level == item.level) { 69 lastItem.setEOL(item.isEOL()); 70 lastItem.addChild(item); 71 } else if (lastItem.level < item.level 72 || (lastItem.level == item.level && lastItem.inset + 15 < item.inset)) { 73 stack.push(lastItem.addChild(item)); 74 } else { 75 stack.pop(); 76 add(item); 77 } 78 } 79 } 80 81 void addAll(List<SearchItem> items, boolean isEOL) { 82 if (items.size() > 0) { 83 items.get(items.size() - 1).setEOL(isEOL); 84 } 85 items.forEach(item -> add(item)); 86 } 87 88 void insertEOL() { 89 if (!stack.isEmpty()) { 90 stack.lastElement().setEOL(); 91 } 92 } 93 94 void insertSeparator() { 95 if (!stack.isEmpty()) { 96 stack.lastElement().maximizeInset(); 97 } 98 } 99 100 void insertNewPage() { 101 stack.clear(); 102 } 103 104 public boolean isBuilt() { 105 return built; 106 } 107 108 /** 109 * @throws InvocationTargetException exception while initializing tab 110 * @throws InterruptedException thread interrupted 111 */ 112 public void build() throws InvocationTargetException, InterruptedException { 113 //setVisible(false); 114 built = false; 115 parents.clear(); 116 getPanel().removeAll(); 117 for (int i = 0; i < prefTabs.getTabCount(); i++) { 118 Component c = prefTabs.getComponentAt(i); 119 currentTabIndex = i; 120 String iconName = null; 121 if (c instanceof PreferenceTab) { 122 iconName = ((PreferenceTab) c).getTabPreferenceSetting().getIconName(); 123 final int index = i; 124 final Component comp = c; 125 SwingUtilities.invokeAndWait(() -> prefTabs.initializeTab(index, (PreferenceTab) comp, true)); 126 c = prefTabs.getComponentAt(i); 127 } 128 insertNewPage(); 129 searchComponent(c, 1, iconName); 130 } 131 132 built = true; 133 //setVisible(true); 134 } 135 136 public boolean filter(String filterStr) { 137 boolean found = false; 138 139 if (lastFilterStr != null && filterStr.indexOf(lastFilterStr) != 0) { 140 parents.forEach(SearchItem::showAll); 141 } 142 143 if (!filterStr.isEmpty()) { 144 SearchFilters filters = new SearchFilters(filterStr); 145 found = parents.stream() 146 .filter(SearchItem::isVisible) 147 .map(item -> item.filter(filters)) 148 .collect(Collectors.toList()) //force all visible elements to be evaluated 149 .contains(true); 150 } 151 152 lastFilterStr = filterStr; 153 return found; 154 } 155 156 /*public DefaultMutableTreeNode getTreeView() { 157 return tree; 158 } 159 160 public synchronized DefaultMutableTreeNode createTreeView(DefaultTreeModel model) { 161 tree.removeAllChildren(); 162 parents.forEach(child -> { 163 MutableTreeNode t = child.createTreeView(model); 164 if (t != null) { 165 model.insertNodeInto(t, tree, tree.getChildCount()); 166 //tree.add(t); 167 } 168 }); 169 return tree; 170 }*/ 171 172 private boolean searchComponent(Component comp, int level, String iconName) 173 throws InvocationTargetException, InterruptedException { 174 175 final Component c = comp; 176 177 boolean isEOL = true; 178 GridBagConstraints currentConstraint = null; 179 180 Container p = c.getParent(); 181 if (p != null) { 182 LayoutManager layout = p.getLayout(); 183 if (layout != null && layout instanceof GridBagLayout) { 184 GridBagLayout grid = (GridBagLayout) layout; 185 186 currentConstraint = grid.getConstraints(c); 187 isEOL = currentConstraint.gridwidth == GridBagConstraints.REMAINDER; 188 } 189 } 190 191 if (lastConstraint != null && currentConstraint != null 192 && (((lastConstraint.fill == GridBagConstraints.HORIZONTAL 193 || lastConstraint.fill == GridBagConstraints.BOTH) 194 && currentConstraint.fill != GridBagConstraints.HORIZONTAL 195 && currentConstraint.fill != GridBagConstraints.BOTH) 196 || lastConstraint.gridy != currentConstraint.gridy)) { 197 insertEOL(); 198 } 199 200 lastConstraint = currentConstraint; 201 boolean isChildrenSearchable = true; 202 ISearchableComponent s = null; 203 if (c instanceof ISearchableComponent) { 204 s = (ISearchableComponent) c; 205 isChildrenSearchable = s.isChildrenSearchable(); 206 } 207 208 if (s == null || s.isSearchable()) { 209 210 List<SearchItem> items = null; 211 if (s != null) { 212 items = s.getSearchItemsAsync(); 213 final ISearchableComponent s2 = s; 214 List<SearchItem> itm2 = GuiHelper.runInEDTAndWaitAndReturn(() -> s2.getSearchItems()); 215 if (itm2 != null) { 216 if (items == null) { 217 items = itm2; 218 } else { 219 items.addAll(itm2); 220 } 221 } 222 223 if (iconName == null) { 224 iconName = s.getIconName(); 225 } 226 227 /*final SwingTransferItem swing = new SwingTransferItem(s); 228 SwingUtilities.invokeAndWait(() -> { 229 swing.list = swing.component.getSearchItems(); 230 }); 231 232 if (swing.list != null) { 233 items = swing.list; 234 }*/ 235 } 236 if (items == null) { 237 final List<SearchItem> itm = new ArrayList<>(); 238 SwingUtilities.invokeAndWait(() -> { 239 SearchTextFinder.DEFAULT_SEARCH_TEXT_FINDERS 240 .forEach(finder -> itm.addAll(finder.getSearchItems(c))); 241 }); 242 243 items = itm; 244 } 245 if (iconName != null) { 246 final String iconName2 = iconName; 247 items.forEach(item -> item.setIconName(iconName2)); 248 } 249 if (items.size() == 0) { 250 if (isEOL) { 251 insertEOL(); 252 } 253 if (c instanceof JSeparator) { 254 insertSeparator(); 255 } 256 257 } else { 258 items.get(0).addOriginalComponent(c); 259 if (c instanceof JComponent) { 260 items.get(0).setTooltip(((JComponent) c).getToolTipText()); 261 } 262 263 final int inset = currentConstraint == null ? 0 : currentConstraint.insets.left; 264 items.forEach(item -> item.setLevelInset(level, inset)); 265 isChildrenSearchable = isChildrenSearchable 266 && items.stream().allMatch(item -> item.isChildComponentsSearchable()); 267 268 addAll(items, isEOL); 269 } 270 } 271 272 if (isChildrenSearchable && c instanceof Container) { 273 Container cont = (Container) c; 274 List<Component> components = Arrays.asList(cont.getComponents()); 275 if (components.size() > 0) { 276 insertEOL(); 277 for (Component component : components) { 278 searchComponent(component, level + 1, iconName); 279 } 280 } 281 } 282 return isEOL; 283 } 284 285 /** 286 * @return the panel 287 */ 288 public JPanel getPanel() { 289 return panel; 290 } 291 292 public void setVisible(boolean visible) { 293 GuiHelper.runInEDT(() -> { 294 panel.setVisible(visible); 295 }); 296 } 297 298 public void updateComponents() throws InvocationTargetException, InterruptedException { 299 for (SearchItem item : parents) { 300 item.updateComponents(); 301 } 302 } 303 304 } 305 No newline at end of file -
src/org/openstreetmap/josm/gui/preferences/plugin/PluginPreference.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.gui.preferences.plugin; 3 3 4 import static java.awt.GridBagConstraints.BOTH; 4 5 import static java.awt.GridBagConstraints.HORIZONTAL; 5 6 import static org.openstreetmap.josm.tools.I18n.tr; 6 7 import static org.openstreetmap.josm.tools.I18n.trc; … … 51 52 import org.openstreetmap.josm.gui.preferences.PreferenceSetting; 52 53 import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory; 53 54 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane; 55 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane.PreferencePanel; 56 import org.openstreetmap.josm.gui.preferences.search.NotSearchablePanel; 54 57 import org.openstreetmap.josm.gui.util.GuiHelper; 55 58 import org.openstreetmap.josm.gui.widgets.FilterField; 56 59 import org.openstreetmap.josm.plugins.PluginDownloadTask; … … 162 165 } 163 166 164 167 private JPanel buildSearchFieldPanel() { 165 JPanel pnl = new JPanel(new GridBagLayout());168 JPanel pnl = new NotSearchablePanel(new GridBagLayout()); 166 169 pnl.add(GBC.glue(0, 0)); 167 170 168 171 ButtonGroup bg = new ButtonGroup(); … … 231 234 232 235 @Override 233 236 public void addGui(final PreferenceTabbedPane gui) { 237 PreferencePanel tab = gui.createPreferenceTab(this); 234 238 JTabbedPane pane = getTabPane(); 239 JPanel pnlPluginList = buildPluginListPanel(); 235 240 pnlPluginUpdatePolicy = new PluginUpdatePolicyPanel(); 236 pane.addTab(tr("Plugins"), buildPluginListPanel());241 pane.addTab(tr("Plugins"), pnlPluginList); 237 242 pane.addTab(tr("Plugin update policy"), pnlPluginUpdatePolicy); 238 super.addGui(gui); 239 readLocalPluginInformation(); 243 tab.add(pane, GBC.eol().fill(BOTH)); 244 gui.addChangeListener(e -> { 245 if (gui.getSelectedComponent() == tab) { 246 readLocalPluginInformation(); 247 } 248 }); 240 249 pluginPreferencesActivated = true; 241 250 } 242 251 -
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/search/SearchItemComponent.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.preferences.search; 3 4 import java.awt.BasicStroke; 5 import java.awt.Color; 6 import java.awt.Cursor; 7 import java.awt.Graphics; 8 import java.awt.Graphics2D; 9 import java.awt.GridBagLayout; 10 import java.awt.RenderingHints; 11 import java.awt.event.MouseAdapter; 12 import java.awt.event.MouseEvent; 13 14 import javax.swing.BorderFactory; 15 import javax.swing.ImageIcon; 16 import javax.swing.JLabel; 17 import javax.swing.JPanel; 18 19 import org.openstreetmap.josm.tools.GBC; 20 import org.openstreetmap.josm.tools.ImageProvider; 21 import org.openstreetmap.josm.tools.ImageProvider.ImageSizes; 22 23 public class SearchItemComponent extends JPanel { 24 25 private final GridBagLayout own_gbl = new GridBagLayout(); 26 private final JLabel lbl = new JLabel(); 27 private final JLabel expander = new JLabel(); 28 private JPanel childPanel; 29 private final SearchItem item; 30 31 private GridBagLayout parent_gbl; 32 private boolean expanded = true; 33 private GBC gbc_nw; 34 private GBC gbc_nw_fill; 35 36 private static final ImageIcon ICON_EXPANDED = ImageProvider.get("dialogs", "down", ImageSizes.SMALLICON); 37 private static final ImageIcon ICON_COLLAPSED = ImageProvider.get("dialogs", "next", ImageSizes.SMALLICON); 38 39 private static final Cursor CURSOR_HAND = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); 40 private static final Cursor CURSOR_DEFAULT = Cursor.getDefaultCursor(); 41 42 public SearchItemComponent(SearchItem item) { 43 super(new GridBagLayout()); 44 setVisible(false); 45 this.item = item; 46 add(expander); 47 48 gbc_nw = GBC.eol(); 49 gbc_nw.anchor = GBC.NORTHWEST; 50 gbc_nw_fill = GBC.eol().fill(GBC.HORIZONTAL); 51 gbc_nw_fill.anchor = GBC.NORTHWEST; 52 add(lbl, gbc_nw_fill); 53 lbl.setCursor(CURSOR_HAND); 54 lbl.setBackground(Color.cyan); 55 //lbl.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 0)); 56 setBorder(BorderFactory.createEmptyBorder(10, item.level == 0 ? 10 : 17, item.level == 0 ? 25 : 0, 10)); 57 58 expander.addMouseListener(new MouseAdapter() { 59 @Override 60 public void mouseClicked(MouseEvent e) { 61 setExpanded(!isExpanded()); 62 } 63 }); 64 65 addMouseListener(new MouseAdapter() { 66 @Override 67 public void mouseClicked(MouseEvent e) { 68 item.showOriginalComponent(); 69 } 70 }); 71 } 72 73 public void setText(String txt) { 74 lbl.setText(txt); 75 } 76 77 @Override 78 protected void paintComponent(Graphics graphics) { 79 super.paintComponent(graphics); 80 81 if (item.level != 0) 82 return; 83 84 Graphics2D g = (Graphics2D) graphics; 85 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 86 87 int width = getWidth(), 88 height = getHeight(), 89 stroke = 1, 90 offset = 5, 91 radius = 15; 92 93 94 g.setColor(new Color(0, 0, 0, 125)); 95 g.fillRoundRect(offset, offset, width - stroke - offset, height - stroke - offset - 10, radius, radius); 96 97 g.setColor(getBackground()); 98 g.fillRoundRect(0, 0, width - 5, height - 15, radius, radius); 99 100 g.setColor(getForeground()); 101 g.setStroke(new BasicStroke(stroke)); 102 g.drawRoundRect(0, 0, width - 5, height - 15, radius, radius); 103 104 g.setStroke(new BasicStroke()); 105 } 106 107 /** 108 * @return the expanded 109 */ 110 public boolean isExpanded() { 111 return expanded; 112 } 113 114 /** 115 * @param expanded the expanded to set 116 */ 117 public void setExpanded(boolean expanded) { 118 if (item.hasVisibleChildren()) { 119 this.expanded = expanded; 120 expander.setIcon(expanded ? ICON_EXPANDED : ICON_COLLAPSED); 121 expander.setCursor(CURSOR_HAND); 122 getChildPanel().setVisible(expanded); 123 } else { 124 expander.setIcon(ICON_COLLAPSED); 125 } 126 } 127 128 /** 129 * @return the childPanel 130 */ 131 public JPanel getChildPanel() { 132 if (childPanel == null) { 133 childPanel = new JPanel(own_gbl); 134 add(childPanel, gbc_nw_fill); 135 } 136 return childPanel; 137 } 138 139 public void addChild(SearchItemComponent comp) { 140 getChildPanel().add(comp, gbc_nw_fill); 141 comp.setParentGBL(own_gbl); 142 } 143 144 /** 145 * @param parent_gbl the parent_gbl to set 146 */ 147 public void setParentGBL(GridBagLayout parent_gbl) { 148 this.parent_gbl = parent_gbl; 149 } 150 151 } -
src/org/openstreetmap/josm/gui/PleaseWaitRunnable.java
18 18 import org.xml.sax.SAXException; 19 19 20 20 /** 21 * Instanced of this thread willdisplay a "Please Wait" message in middle of JOSM21 * Instanced of this thread can display a "Please Wait" message in middle of JOSM 22 22 * to indicate a progress being executed. 23 23 * 24 24 * @author Imi … … 30 30 /** progress monitor */ 31 31 protected final ProgressMonitor progressMonitor; 32 32 33 protected PleaseWaitRunnable(String title, boolean ignoreException, boolean silent) { 34 this.title = title; 35 this.ignoreException = ignoreException; 36 this.progressMonitor = new PleaseWaitProgressMonitor(title, silent); 37 } 38 33 39 /** 34 40 * Create the runnable object with a given message for the user. 35 41 * @param title message for the user -
src/org/openstreetmap/josm/gui/preferences/plugin/PluginListPanel.java
9 9 import java.awt.Rectangle; 10 10 import java.awt.event.MouseAdapter; 11 11 import java.awt.event.MouseEvent; 12 import java.util.Collection; 13 import java.util.HashMap; 12 14 import java.util.HashSet; 13 15 import java.util.List; 16 import java.util.Map; 14 17 import java.util.Set; 18 import java.util.stream.Collectors; 15 19 16 20 import javax.swing.JComponent; 17 21 import javax.swing.JLabel; … … 18 22 import javax.swing.SwingConstants; 19 23 import javax.swing.SwingUtilities; 20 24 25 import org.openstreetmap.josm.data.Preferences; 26 import org.openstreetmap.josm.gui.preferences.search.ISearchableComponent; 27 import org.openstreetmap.josm.gui.preferences.search.SearchItem; 28 import org.openstreetmap.josm.gui.progress.swing.PleaseWaitProgressMonitor; 29 import org.openstreetmap.josm.gui.util.GuiHelper; 21 30 import org.openstreetmap.josm.gui.widgets.HtmlPanel; 22 31 import org.openstreetmap.josm.gui.widgets.VerticallyScrollablePanel; 23 32 import org.openstreetmap.josm.plugins.PluginInformation; 33 import org.openstreetmap.josm.plugins.ReadLocalPluginInformationTask; 34 import org.openstreetmap.josm.plugins.ReadRemotePluginInformationTask; 24 35 25 36 /** 26 37 * A panel displaying the list of known plugins. 27 38 */ 28 public class PluginListPanel extends VerticallyScrollablePanel {39 public class PluginListPanel extends VerticallyScrollablePanel implements ISearchableComponent { 29 40 static final class PluginCheckBoxMouseAdapter extends MouseAdapter { 30 41 private final PluginCheckBox cbPlugin; 31 42 … … 118 129 /** 119 130 * Displays a list of plugins. 120 131 * @param displayedPlugins list of plugins 132 * @return the added labels 121 133 * @since 13799 122 134 */ 123 public void displayPluginList(List<PluginInformation> displayedPlugins) { 135 public Map<PluginInformation, JLabel> displayPluginList(List<PluginInformation> displayedPlugins) { 136 Map<PluginInformation, JLabel> lbls = new HashMap<>(); 137 124 138 GridBagConstraints gbc = new GridBagConstraints(); 125 139 gbc.gridx = 0; 126 140 gbc.anchor = GridBagConstraints.NORTHWEST; … … 143 157 pi.getScaledIcon(), 144 158 SwingConstants.LEADING); 145 159 lblPlugin.addMouseListener(new PluginCheckBoxMouseAdapter(cbPlugin)); 160 lbls.put(pi, lblPlugin); 146 161 147 162 gbc.gridx = 0; 148 163 gbc.gridy = ++row; … … 170 185 add(description, gbc); 171 186 } 172 187 pluginListInitialized = true; 188 return lbls; 173 189 } 174 190 175 191 /** … … 236 252 public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { 237 253 return visibleRect.height; 238 254 } 255 256 @Override 257 public List<SearchItem> getSearchItemsAsync() { 258 259 //if (model.getAvailablePlugins().isEmpty()) { 260 261 final ReadLocalPluginInformationTask task = new ReadLocalPluginInformationTask(new PleaseWaitProgressMonitor(true)); 262 task.run(); 263 model.setAvailablePlugins(task.getAvailablePlugins()); 264 265 if (!task.hasSiteCacheFile()) { 266 Collection<String> pluginSites = Preferences.main().getOnlinePluginSites(); 267 if (!pluginSites.isEmpty()) { 268 final ReadRemotePluginInformationTask remoteTask = new ReadRemotePluginInformationTask( 269 new PleaseWaitProgressMonitor(true), pluginSites, false); 270 remoteTask.run(); 271 model.updateAvailablePlugins(remoteTask.getAvailablePlugins()); 272 } 273 } 274 275 Map<PluginInformation, JLabel> map = GuiHelper.runInEDTAndWaitAndReturn(() -> { 276 removeAll(); 277 displayEmptyPluginListInformation(); 278 return displayPluginList(model.getAvailablePlugins()); 279 }); 280 pluginListInitialized = true; 281 282 return map.entrySet().stream().map((entry) -> { 283 String v = ""; 284 PluginInformation plugin = entry.getKey(); 285 if (plugin.localversion != null && !plugin.localversion.trim().isEmpty()) { 286 v = " (local: " + plugin.localversion + ")"; 287 } 288 return new SearchItem(entry.getValue(), plugin.getName() + v, plugin.description, true, false); 289 }).collect(Collectors.toList()); 290 291 //} 292 /*return model.getAvailablePlugins().stream().map(plugin -> { 293 String v = ""; 294 if (plugin.localversion != null && !plugin.localversion.trim().isEmpty()) { 295 v = " (local: " + plugin.localversion + ")"; 296 } 297 return new SearchItem(null, plugin.getName() + v, plugin.description, true, false); 298 }).collect(Collectors.toList());*/ 299 } 300 239 301 } -
src/org/openstreetmap/josm/gui/preferences/imagery/ImageryProvidersPanel.java
778 778 } 779 779 return false; 780 780 } 781 782 /*@Override 783 public List<SearchItem> getSearchItemsAsync() { 784 //layerInfo.load(true); 785 return null; 786 }*/ 781 787 } -
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/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 = 1; i < tabs.getTabCount(); i++) { 51 width = Math.max(width, tabs.getBoundsAt(i).width); //TODO all width the same, only check one? 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/SearchFilters.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.preferences.search; 3 4 import java.text.Normalizer; 5 import java.util.ArrayList; 6 import java.util.Arrays; 7 import java.util.List; 8 import java.util.regex.Matcher; 9 import java.util.regex.Pattern; 10 import java.util.stream.Collectors; 11 12 public class SearchFilters extends ArrayList<String> { 13 private final List<String> searchStrsNormalized; 14 15 public SearchFilters(String filterStr) { 16 String[] arr = filterStr.split(" "); 17 this.addAll(Arrays.asList(arr)); 18 this.searchStrsNormalized = Arrays.stream(arr).map(SearchFilters::normalize).collect(Collectors.toList()); 19 } 20 21 public List<String> getNormalized() { 22 return searchStrsNormalized; 23 } 24 25 /** 26 * Highlights the search string in the given item text. 27 * Needs to be done manually to deal with accents/umlauts 28 * @param itemStr 29 * @param itemStrNormalized 30 * @return 31 */ 32 public String highlightString(String itemStr, String itemStrNormalized) { 33 Pattern p = Pattern.compile("(?i)(" + String.join("|", searchStrsNormalized) + ")"); 34 Matcher m = p.matcher(itemStrNormalized); 35 StringBuilder sb = null; 36 int i = 0; 37 38 while (m.find()) { 39 if (sb == null) { 40 sb = new StringBuilder("<html>"); 41 } 42 sb.append(itemStr.substring(i, m.start())) 43 .append("<span style=\"color: ") 44 .append(SearchPanel.DARK_MODE ? "yellow" : "red") 45 .append("\">") 46 .append(itemStr.substring(m.start(), m.end())) 47 .append("</span>"); 48 i = m.end(); 49 } 50 51 if (sb == null) { 52 return itemStr; 53 } 54 55 sb.append(itemStr.substring(i)) 56 .append("</html>"); 57 return sb.toString(); 58 } 59 60 public static String normalize(String str) { 61 if (str == null) 62 return null; 63 64 String strLower = str.toLowerCase(SearchIndex.CURRENT_LOCALE); 65 66 String ret = Normalizer.normalize(strLower, Normalizer.Form.NFD) 67 .replaceAll("\\p{InCombiningDiacriticalMarks}+", ""); 68 69 if (ret.length() != str.length()) { 70 return strLower; 71 } 72 73 return ret; 74 } 75 } 76 No newline at end of file -
src/org/openstreetmap/josm/gui/progress/swing/PleaseWaitProgressMonitor.java
96 96 97 97 private boolean cancelable; 98 98 99 private boolean silent; 100 99 101 /** 100 102 * Returns the progress monitor being currently displayed. 101 103 * @return the progress monitor being currently displayed … … 197 199 this.windowTitle = windowTitle; 198 200 } 199 201 202 public PleaseWaitProgressMonitor(boolean silent) { 203 this(); 204 this.silent = silent; 205 } 206 207 public PleaseWaitProgressMonitor(String title, boolean silent) { 208 this(title); 209 this.silent = silent; 210 } 211 200 212 private final ActionListener cancelListener = e -> cancel(); 201 213 202 214 private final ActionListener inBackgroundListener = e -> { … … 234 246 public void doBeginTask() { 235 247 doInEDT(() -> { 236 248 currentProgressMonitor = this; 237 if ( GraphicsEnvironment.isHeadless()) {249 if (silent || GraphicsEnvironment.isHeadless()) { 238 250 return; 239 251 } 240 252 if (dialogParent != null && dialog == null) { -
src/org/openstreetmap/josm/gui/preferences/advanced/AdvancedPreference.java
49 49 import org.openstreetmap.josm.gui.preferences.PreferenceSetting; 50 50 import org.openstreetmap.josm.gui.preferences.PreferenceSettingFactory; 51 51 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane; 52 import org.openstreetmap.josm.gui.preferences.search.NotSearchablePanel; 52 53 import org.openstreetmap.josm.gui.util.DocumentAdapter; 53 54 import org.openstreetmap.josm.gui.util.GuiHelper; 54 55 import org.openstreetmap.josm.gui.widgets.AbstractFileChooser; … … 163 164 164 165 @Override 165 166 public void addGui(final PreferenceTabbedPane gui) { 166 JPanel p = gui.createPreferenceTab(this);167 JPanel panel = gui.createPreferenceTab(this); 167 168 169 NotSearchablePanel p = new NotSearchablePanel(new GridBagLayout()); 170 panel.add(p, GBC.std().fill()); 171 168 172 final JPanel txtFilterPanel = new JPanel(new GridBagLayout()); 169 173 p.add(txtFilterPanel, GBC.eol().fill(GBC.HORIZONTAL)); 170 174 txtFilter = new FilterField(); -
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/plugins/ReadLocalPluginInformationTask.java
39 39 public class ReadLocalPluginInformationTask extends PleaseWaitRunnable { 40 40 private final Map<String, PluginInformation> availablePlugins; 41 41 private boolean canceled; 42 private boolean hasSiteCacheFile; 42 43 43 44 /** 44 45 * Constructs a new {@code ReadLocalPluginInformationTask}. … … 96 97 monitor.setCustomText(tr("Processing file ''{0}''", fname)); 97 98 try { 98 99 processLocalPluginInformationFile(f); 100 hasSiteCacheFile = true; 99 101 } catch (PluginListParseException e) { 100 102 Logging.warn(tr("Failed to scan file ''{0}'' for plugin information. Skipping.", fname)); 101 103 Logging.error(e); … … 219 221 } 220 222 221 223 /** 224 * @return true if a local cache file was parsed 225 */ 226 public boolean hasSiteCacheFile() { 227 return hasSiteCacheFile; 228 } 229 230 /** 222 231 * Replies true if the task was canceled by the user 223 232 * 224 233 * @return true if the task was canceled by the user -
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(); … … 280 284 private static final PreferenceSettingFactory ADVANCED_PREFERENCE_FACTORY = new AdvancedPreference.Factory(); 281 285 private final transient List<PreferenceSetting> settings = new ArrayList<>(); 282 286 287 public final SearchPanel searchPanel = new SearchPanel(this); 288 private final SearchTextField searchTextField = new SearchTextField(searchPanel); 289 283 290 // distinct list of tabs that have been initialized (we do not initialize tabs until they are displayed to speed up dialog startup) 284 291 private final transient List<PreferenceSetting> settingsInitialized = new ArrayList<>(); 285 292 … … 464 471 */ 465 472 public PreferenceTabbedPane() { 466 473 super(SwingConstants.LEFT, JTabbedPane.SCROLL_TAB_LAYOUT); 467 super.addMouseWheelListener(new WheelListener(this)); 474 MouseAndWheelListener l = new MouseAndWheelListener(this); 475 super.addMouseWheelListener(l); 476 super.addMouseListener(l); 468 477 ExpertToggleAction.addExpertModeChangeListener(this); 469 478 } 470 479 … … 525 534 private void addGUITabs(boolean clear) { 526 535 boolean expert = ExpertToggleAction.isExpert(); 527 536 if (clear) { 537 /*for (int i = 1; i < getTabCount(); i++) { 538 remove(i); 539 }*/ 528 540 removeAll(); 529 541 } 542 543 addTab(null, searchPanel); 544 setTabComponentAt(0, searchTextField); 530 545 // Compute max tab length in pixels 531 546 int maxWidth = computeMaxTabWidth(); 532 547 // Inspect each tab setting … … 558 573 Logging.debug("{0}: hiding empty {1}", getClass().getSimpleName(), tps); 559 574 }); 560 575 } 561 setSelectedIndex(-1); 576 searchTextField.adjustWidth(); 577 //setSelectedIndex(2); 562 578 } 563 579 564 580 private int computeMaxTabWidth() { … … 627 643 * This mouse wheel listener reacts when a scroll is carried out over the 628 644 * tab strip and scrolls one tab/down or up, selecting it immediately. 629 645 */ 630 static final class WheelListener implements MouseWheelListener {646 static final class MouseAndWheelListener implements MouseWheelListener, MouseListener { 631 647 632 648 final JTabbedPane tabbedPane; 633 649 634 WheelListener(JTabbedPane tabbedPane) {650 MouseAndWheelListener(JTabbedPane tabbedPane) { 635 651 this.tabbedPane = tabbedPane; 636 652 } 637 653 … … 647 663 648 664 tabbedPane.setSelectedIndex(newTab); 649 665 } 666 667 @Override 668 public void mouseClicked(MouseEvent e) { 669 tabbedPane.requestFocus(); 670 } 671 672 @Override 673 public void mouseEntered(MouseEvent e) { 674 } 675 676 @Override 677 public void mouseExited(MouseEvent e) { 678 } 679 680 @Override 681 public void mousePressed(MouseEvent e) { 682 } 683 684 @Override 685 public void mouseReleased(MouseEvent e) { 686 } 650 687 } 651 688 652 689 @Override … … 654 691 int index = getSelectedIndex(); 655 692 Component sel = getSelectedComponent(); 656 693 if (index > -1 && sel instanceof PreferenceTab) { 657 PreferenceTab tab = (PreferenceTab) sel; 658 TabPreferenceSetting preferenceSettings = tab.getTabPreferenceSetting(); 659 if (!settingsInitialized.contains(preferenceSettings)) { 660 try { 661 getModel().removeChangeListener(this); 662 preferenceSettings.addGui(this); 663 // Add GUI for sub preferences 664 for (PreferenceSetting setting : settings) { 665 if (setting instanceof SubPreferenceSetting) { 666 addSubPreferenceSetting(preferenceSettings, (SubPreferenceSetting) setting); 667 } 694 initializeTab(index, (PreferenceTab) sel, false); 695 } 696 } 697 698 public void initializeTab(int index, PreferenceTab tab, boolean silent) { 699 TabPreferenceSetting preferenceSettings = tab.getTabPreferenceSetting(); 700 if (!settingsInitialized.contains(preferenceSettings)) { 701 try { 702 getModel().removeChangeListener(this); 703 preferenceSettings.addGui(this); 704 // Add GUI for sub preferences 705 for (PreferenceSetting setting : settings) { 706 if (setting instanceof SubPreferenceSetting) { 707 addSubPreferenceSetting(preferenceSettings, (SubPreferenceSetting) setting); 668 708 } 669 Icon icon = getIconAt(index);670 remove(index);671 if (index <= insertGUITabsForSetting(icon, preferenceSettings, index, computeMaxTabWidth())) {672 setSelectedIndex(index);673 }674 } catch (SecurityException ex) {675 Logging.error(ex);676 } catch (RuntimeException ex) { // NOPMD677 // allow to change most settings even if e.g. a plugin fails678 BugReportExceptionHandler.handleException(ex);679 } finally {680 settingsInitialized.add(preferenceSettings);681 getModel().addChangeListener(this);682 709 } 710 Icon icon = getIconAt(index); 711 remove(index); 712 if (index <= insertGUITabsForSetting(icon, preferenceSettings, index, computeMaxTabWidth()) && !silent) { 713 setSelectedIndex(index); 714 } 715 } catch (SecurityException ex) { 716 Logging.error(ex); 717 } catch (RuntimeException ex) { // NOPMD 718 // allow to change most settings even if e.g. a plugin fails 719 BugReportExceptionHandler.handleException(ex); 720 } finally { 721 settingsInitialized.add(preferenceSettings); 722 getModel().addChangeListener(this); 683 723 } 684 Container ancestor = getTopLevelAncestor();685 if (ancestor instanceof PreferenceDialog) {686 ((PreferenceDialog) ancestor).setHelpContext(preferenceSettings.getHelpContext());687 }688 724 } 725 Container ancestor = getTopLevelAncestor(); 726 if (ancestor instanceof PreferenceDialog) { 727 ((PreferenceDialog) ancestor).setHelpContext(preferenceSettings.getHelpContext()); 728 } 689 729 } 690 730 691 731 private void addSubPreferenceSetting(TabPreferenceSetting preferenceSettings, SubPreferenceSetting sps) { -
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 * 14 * default: true 15 */ 16 default boolean isSearchable() { 17 return true; 18 } 19 20 /** 21 * @return whether the children of this component should be traversed <br><br> 22 * 23 * default: same as {@link #isSearchable()} unless explicitly overridden 24 */ 25 default boolean isChildrenSearchable() { 26 return isSearchable(); 27 } 28 29 /** 30 * Called from the EventDispatchThread (GUI), should not be blocked. 31 * 32 * @return the {@link SearchItem}s that should be used. Ignored if {@link #isSearchable()} returns {@code false}.<br> 33 * Overrides the default {@link SearchTextFinder} if returning not {@code null} (including if an empty list is returned)<br><br> 34 * 35 * default: null 36 */ 37 default List<SearchItem> getSearchItems() { 38 return null; 39 } 40 41 /** 42 * Called from the worker thread, can be blocked as long as necessary. Calls to the GUI must be placed in the EventDispatchThread. 43 * 44 * @return the {@link SearchItem}s that should be used. Ignored if {@link #isSearchable()} returns {@code false}.<br> 45 * Overrides the default {@link SearchTextFinder} if returning not {@code null} (including if an empty list is returned)<br><br> 46 * 47 * default: null 48 */ 49 default List<SearchItem> getSearchItemsAsync() { 50 return null; 51 } 52 53 /** 54 * @return 55 */ 56 default String getIconName() { 57 return null; 58 } 59 60 } -
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/SearchFilters.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.preferences.search; 3 4 import java.text.Normalizer; 5 import java.util.ArrayList; 6 import java.util.Arrays; 7 import java.util.List; 8 import java.util.regex.Matcher; 9 import java.util.regex.Pattern; 10 import java.util.stream.Collectors; 11 12 public class SearchFilters extends ArrayList<String> { 13 private final List<String> searchStrsNormalized; 14 15 public SearchFilters(String filterStr) { 16 String[] arr = filterStr.split(" "); 17 this.addAll(Arrays.asList(arr)); 18 this.searchStrsNormalized = Arrays.stream(arr).map(SearchFilters::normalize).collect(Collectors.toList()); 19 } 20 21 public List<String> getNormalized() { 22 return searchStrsNormalized; 23 } 24 25 /** 26 * Highlights the search string in the given item text. 27 * Needs to be done manually to deal with accents/umlauts 28 * @param itemStr 29 * @param itemStrNormalized 30 * @return 31 */ 32 public String highlightString(String itemStr, String itemStrNormalized) { 33 Pattern p = Pattern.compile("(?i)(" + String.join("|", searchStrsNormalized) + ")"); 34 Matcher m = p.matcher(itemStrNormalized); 35 StringBuilder sb = null; 36 int i = 0; 37 38 while (m.find()) { 39 if (sb == null) { 40 sb = new StringBuilder("<html>"); 41 } 42 sb.append(itemStr.substring(i, m.start())) 43 .append("<span style=\"color: ") 44 .append(SearchPanel.DARK_MODE ? "yellow" : "red") 45 .append("\">") 46 .append(itemStr.substring(m.start(), m.end())) 47 .append("</span>"); 48 i = m.end(); 49 } 50 51 if (sb == null) { 52 return itemStr; 53 } 54 55 sb.append(itemStr.substring(i)) 56 .append("</html>"); 57 return sb.toString(); 58 } 59 60 public static String normalize(String str) { 61 if (str == null) 62 return null; 63 64 String strLower = str.toLowerCase(SearchIndex.CURRENT_LOCALE); 65 66 String ret = Normalizer.normalize(strLower, Normalizer.Form.NFD) 67 .replaceAll("\\p{InCombiningDiacriticalMarks}+", ""); 68 69 if (ret.length() != str.length()) { 70 return strLower; 71 } 72 73 return ret; 74 } 75 } 76 No newline at end of file -
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.lang.reflect.InvocationTargetException; 10 import java.util.ArrayList; 11 import java.util.Arrays; 12 import java.util.List; 13 import java.util.Locale; 14 import java.util.Stack; 15 import java.util.stream.Collectors; 16 17 import javax.swing.JComponent; 18 import javax.swing.JPanel; 19 import javax.swing.JSeparator; 20 import javax.swing.SwingUtilities; 21 22 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane; 23 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane.PreferenceTab; 24 import org.openstreetmap.josm.gui.util.GuiHelper; 25 import org.openstreetmap.josm.tools.GBC; 26 import org.openstreetmap.josm.tools.LanguageInfo; 27 28 /** 29 * Contains all {@link SearchItem}s 30 * @author Bjoeni 31 */ 32 public class SearchIndex { 33 public static final Locale CURRENT_LOCALE = LanguageInfo.getLocale(LanguageInfo.getJOSMLocaleCode()); 34 public static final GBC EOL = GBC.eol(); 35 36 Stack<SearchItem> stack = new Stack<>(); 37 List<SearchItem> parents = new ArrayList<>(); 38 PreferenceTabbedPane prefTabs; 39 GridBagConstraints lastConstraint; 40 private boolean built; 41 private String lastFilterStr; 42 private int currentTabIndex = -1; 43 44 private JPanel panel = GuiHelper.runInEDTAndWaitAndReturn(() -> new JPanel(new GridBagLayout())); 45 46 GridBagConstraints gbc_nw; 47 48 public SearchIndex(PreferenceTabbedPane prefTabs) { 49 this.prefTabs = prefTabs; 50 gbc_nw = GBC.eol().fill(GBC.HORIZONTAL); 51 gbc_nw.anchor = GBC.NORTHWEST; 52 } 53 54 void add(SearchItem item) { 55 if (item.getText() == null) 56 return; 57 58 item.setTabIndex(prefTabs, currentTabIndex); 59 if (stack.isEmpty()) { 60 item.setLevelInset(0, 0); 61 parents.add(item); 62 GuiHelper.runInEDT(() -> { 63 getPanel().add(item.getComponent(), gbc_nw); 64 }); 65 stack.push(item); 66 } else { 67 SearchItem lastItem = stack.lastElement(); 68 if (!lastItem.isEOL() && lastItem.level == item.level) { 69 lastItem.setEOL(item.isEOL()); 70 lastItem.addChild(item); 71 } else if (lastItem.level < item.level 72 || (lastItem.level == item.level && lastItem.inset + 15 < item.inset)) { 73 stack.push(lastItem.addChild(item)); 74 } else { 75 stack.pop(); 76 add(item); 77 } 78 } 79 } 80 81 void addAll(List<SearchItem> items, boolean isEOL) { 82 if (items.size() > 0) { 83 items.get(items.size() - 1).setEOL(isEOL); 84 } 85 items.forEach(item -> add(item)); 86 } 87 88 void insertEOL() { 89 if (!stack.isEmpty()) { 90 stack.lastElement().setEOL(); 91 } 92 } 93 94 void insertSeparator() { 95 if (!stack.isEmpty()) { 96 stack.lastElement().maximizeInset(); 97 } 98 } 99 100 void insertNewPage() { 101 stack.clear(); 102 } 103 104 public boolean isBuilt() { 105 return built; 106 } 107 108 /** 109 * @throws InvocationTargetException exception while initializing tab 110 * @throws InterruptedException thread interrupted 111 */ 112 public void build() throws InvocationTargetException, InterruptedException { 113 //setVisible(false); 114 built = false; 115 parents.clear(); 116 getPanel().removeAll(); 117 for (int i = 0; i < prefTabs.getTabCount(); i++) { 118 Component c = prefTabs.getComponentAt(i); 119 currentTabIndex = i; 120 String iconName = null; 121 if (c instanceof PreferenceTab) { 122 iconName = ((PreferenceTab) c).getTabPreferenceSetting().getIconName(); 123 final int index = i; 124 final Component comp = c; 125 SwingUtilities.invokeAndWait(() -> prefTabs.initializeTab(index, (PreferenceTab) comp, true)); 126 c = prefTabs.getComponentAt(i); 127 } 128 insertNewPage(); 129 searchComponent(c, 1, iconName); 130 } 131 132 built = true; 133 //setVisible(true); 134 } 135 136 public boolean filter(String filterStr) { 137 boolean found = false; 138 139 if (lastFilterStr != null && filterStr.indexOf(lastFilterStr) != 0) { 140 parents.forEach(SearchItem::showAll); 141 } 142 143 if (!filterStr.isEmpty()) { 144 SearchFilters filters = new SearchFilters(filterStr); 145 found = parents.stream() 146 .filter(SearchItem::isVisible) 147 .map(item -> item.filter(filters)) 148 .collect(Collectors.toList()) //force all visible elements to be evaluated 149 .contains(true); 150 } 151 152 lastFilterStr = filterStr; 153 return found; 154 } 155 156 /*public DefaultMutableTreeNode getTreeView() { 157 return tree; 158 } 159 160 public synchronized DefaultMutableTreeNode createTreeView(DefaultTreeModel model) { 161 tree.removeAllChildren(); 162 parents.forEach(child -> { 163 MutableTreeNode t = child.createTreeView(model); 164 if (t != null) { 165 model.insertNodeInto(t, tree, tree.getChildCount()); 166 //tree.add(t); 167 } 168 }); 169 return tree; 170 }*/ 171 172 private boolean searchComponent(Component comp, int level, String iconName) 173 throws InvocationTargetException, InterruptedException { 174 175 final Component c = comp; 176 177 boolean isEOL = true; 178 GridBagConstraints currentConstraint = null; 179 180 Container p = c.getParent(); 181 if (p != null) { 182 LayoutManager layout = p.getLayout(); 183 if (layout != null && layout instanceof GridBagLayout) { 184 GridBagLayout grid = (GridBagLayout) layout; 185 186 currentConstraint = grid.getConstraints(c); 187 isEOL = currentConstraint.gridwidth == GridBagConstraints.REMAINDER; 188 } 189 } 190 191 if (lastConstraint != null && currentConstraint != null 192 && (((lastConstraint.fill == GridBagConstraints.HORIZONTAL 193 || lastConstraint.fill == GridBagConstraints.BOTH) 194 && currentConstraint.fill != GridBagConstraints.HORIZONTAL 195 && currentConstraint.fill != GridBagConstraints.BOTH) 196 || lastConstraint.gridy != currentConstraint.gridy)) { 197 insertEOL(); 198 } 199 200 lastConstraint = currentConstraint; 201 boolean isChildrenSearchable = true; 202 ISearchableComponent s = null; 203 if (c instanceof ISearchableComponent) { 204 s = (ISearchableComponent) c; 205 isChildrenSearchable = s.isChildrenSearchable(); 206 } 207 208 if (s == null || s.isSearchable()) { 209 210 List<SearchItem> items = null; 211 if (s != null) { 212 items = s.getSearchItemsAsync(); 213 final ISearchableComponent s2 = s; 214 List<SearchItem> itm2 = GuiHelper.runInEDTAndWaitAndReturn(() -> s2.getSearchItems()); 215 if (itm2 != null) { 216 if (items == null) { 217 items = itm2; 218 } else { 219 items.addAll(itm2); 220 } 221 } 222 223 if (iconName == null) { 224 iconName = s.getIconName(); 225 } 226 227 /*final SwingTransferItem swing = new SwingTransferItem(s); 228 SwingUtilities.invokeAndWait(() -> { 229 swing.list = swing.component.getSearchItems(); 230 }); 231 232 if (swing.list != null) { 233 items = swing.list; 234 }*/ 235 } 236 if (items == null) { 237 final List<SearchItem> itm = new ArrayList<>(); 238 SwingUtilities.invokeAndWait(() -> { 239 SearchTextFinder.DEFAULT_SEARCH_TEXT_FINDERS 240 .forEach(finder -> itm.addAll(finder.getSearchItems(c))); 241 }); 242 243 items = itm; 244 } 245 if (iconName != null) { 246 final String iconName2 = iconName; 247 items.forEach(item -> item.setIconName(iconName2)); 248 } 249 if (items.size() == 0) { 250 if (isEOL) { 251 insertEOL(); 252 } 253 if (c instanceof JSeparator) { 254 insertSeparator(); 255 } 256 257 } else { 258 items.get(0).addOriginalComponent(c); 259 if (c instanceof JComponent) { 260 items.get(0).setTooltip(((JComponent) c).getToolTipText()); 261 } 262 263 final int inset = currentConstraint == null ? 0 : currentConstraint.insets.left; 264 items.forEach(item -> item.setLevelInset(level, inset)); 265 isChildrenSearchable = isChildrenSearchable 266 && items.stream().allMatch(item -> item.isChildComponentsSearchable()); 267 268 addAll(items, isEOL); 269 } 270 } 271 272 if (isChildrenSearchable && c instanceof Container) { 273 Container cont = (Container) c; 274 List<Component> components = Arrays.asList(cont.getComponents()); 275 if (components.size() > 0) { 276 insertEOL(); 277 for (Component component : components) { 278 searchComponent(component, level + 1, iconName); 279 } 280 } 281 } 282 return isEOL; 283 } 284 285 /** 286 * @return the panel 287 */ 288 public JPanel getPanel() { 289 return panel; 290 } 291 292 public void setVisible(boolean visible) { 293 GuiHelper.runInEDT(() -> { 294 panel.setVisible(visible); 295 }); 296 } 297 298 public void updateComponents() throws InvocationTargetException, InterruptedException { 299 for (SearchItem item : parents) { 300 item.updateComponents(); 301 } 302 } 303 304 } 305 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.Color; 5 import java.awt.Component; 6 import java.awt.Rectangle; 7 import java.lang.reflect.InvocationTargetException; 8 import java.util.ArrayList; 9 import java.util.List; 10 import java.util.Objects; 11 import java.util.Optional; 12 13 import javax.swing.ImageIcon; 14 import javax.swing.JComponent; 15 import javax.swing.SwingUtilities; 16 import javax.swing.Timer; 17 18 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane; 19 import org.openstreetmap.josm.gui.util.GuiHelper; 20 import org.openstreetmap.josm.tools.ImageProvider; 21 import org.openstreetmap.josm.tools.ImageProvider.ImageSizes; 22 import org.openstreetmap.josm.tools.Logging; 23 24 /** 25 * Contains searchable items and their components 26 * @author Bjoeni 27 */ 28 public class SearchItem { 29 int level; 30 int inset; 31 private String text; 32 private String tooltip; 33 private String highlight; 34 private List<SearchItem> children = new ArrayList<>(); 35 private List<Component> originalComponents = new ArrayList<>(); 36 37 private int tabIndex; 38 private boolean eol = true; 39 private boolean visible = true; 40 private boolean hasVisibleChildren; 41 private boolean childComponentsSearchable = true; 42 private String iconName; 43 private String textNormalized; 44 private String tooltipNormalized; 45 private PreferenceTabbedPane tabs; 46 private SearchItemComponent comp; 47 48 public SearchItem(String text) { 49 this.setText(text); 50 } 51 52 public SearchItem(Component originalComponent, String text, String tooltip, boolean eol, 53 boolean childComponentsSearchable) { 54 this.originalComponents.add(originalComponent); 55 this.setText(text); 56 this.setTooltip(tooltip); 57 this.setEOL(eol); 58 this.childComponentsSearchable = childComponentsSearchable; 59 } 60 61 public SearchItem addChild(SearchItem item) { 62 synchronized (children) { 63 Optional<SearchItem> match = children.stream() 64 .filter(c -> Objects.equals(c.text, item.text) && c.level == item.level && c.inset == item.inset) 65 .findAny(); 66 if (match.isPresent()) { 67 match.get().merge(item); 68 return match.get(); 69 } else { 70 children.add(item); 71 GuiHelper.runInEDT(() -> { 72 getComponent().addChild(item.getComponent()); 73 }); 74 return item; 75 } 76 } 77 78 } 79 80 public void merge(SearchItem item) { 81 this.originalComponents.addAll(item.originalComponents); 82 this.setEOL(this.isEOL() || item.isEOL()); 83 } 84 85 public boolean filter(SearchFilters filters) { 86 visible = this.matches(filters); 87 hasVisibleChildren = false; 88 for (SearchItem child : children) { 89 if (child.isVisible()) { 90 hasVisibleChildren = child.filter(filters) || hasVisibleChildren; 91 } 92 } 93 visible = visible || hasVisibleChildren; 94 95 if (visible && text != null && textNormalized != null) { 96 highlight = filters.highlightString(text, textNormalized); 97 } else { 98 highlight = "<html><span style=\"color: red;\"><b>ERROR</b></span></html>"; 99 } 100 return visible; 101 } 102 103 public boolean matches(SearchFilters filters) { 104 return filters.getNormalized().stream() 105 .allMatch(str -> textNormalized != null && textNormalized.indexOf(str) != -1 106 || (tooltipNormalized != null && tooltipNormalized.indexOf(str) != -1)); 107 } 108 109 public void showAll() { 110 visible = true; 111 children.forEach(SearchItem::showAll); 112 } 113 114 @Override 115 public String toString() { 116 return text + (tooltip == null ? "" : " (Tooltip: " + tooltip + ")") + " [" + level + "." + inset 117 + (eol ? ":EOL" : "") + "] {" + originalComponents.size() + "}"; 118 } 119 120 public ImageIcon getIcon() { 121 if (iconName == null) 122 return null; 123 124 return iconName == null || iconName.isEmpty() ? null 125 : iconName.contains("/") ? ImageProvider.get(iconName, ImageSizes.SMALLICON) 126 : ImageProvider.get("preferences", iconName, ImageSizes.SMALLICON); 127 } 128 129 public boolean isVisible() { 130 return visible; 131 } 132 133 public boolean isEOL() { 134 return eol; 135 } 136 137 public void setEOL() { 138 setEOL(true); 139 } 140 141 public void setEOL(boolean eol) { 142 this.eol = eol; 143 } 144 145 public void setLevelInset(int level, int inset) { 146 this.level = level; 147 this.inset = inset; 148 } 149 150 public void maximizeInset() { 151 this.inset = Integer.MAX_VALUE; 152 } 153 154 public void setTabIndex(PreferenceTabbedPane tabs, int tabIndex) { 155 this.tabs = tabs; 156 this.tabIndex = tabIndex; 157 } 158 159 public void setIconName(String iconName) { 160 this.iconName = iconName; 161 } 162 163 public void addOriginalComponent(Component c) { 164 this.originalComponents.add(c); 165 } 166 167 public void showOriginalComponent() { 168 if (tabIndex > -1) { 169 tabs.setSelectedIndex(tabIndex); 170 } 171 originalComponents.stream().filter(Objects::nonNull).forEach(comp -> { 172 //Color bg = comp.getBackground(); 173 Color fg = comp.getForeground(); 174 //Logging.debug("BG:" + bg.toString()); 175 Logging.debug("FG:" + fg.toString()); 176 //comp.setBackground(SystemColor.text); 177 comp.setForeground(SearchPanel.DARK_MODE ? Color.yellow : Color.red); 178 comp.requestFocus(); 179 if (comp instanceof JComponent) { 180 JComponent jcomp = (JComponent) comp; 181 Rectangle bounds = new Rectangle(jcomp.getBounds()); 182 jcomp.scrollRectToVisible(bounds); 183 } 184 Timer timer = new Timer(3000, l -> { 185 //comp.setBackground(bg); 186 comp.setForeground(fg); 187 }); 188 timer.setRepeats(false); 189 timer.start(); 190 }); 191 if (originalComponents.stream().filter(Objects::nonNull).noneMatch(Component::isVisible)) { 192 Logging.warn("INVIS!!"); 193 } 194 } 195 196 /** 197 * @return the component 198 */ 199 public SearchItemComponent getComponent() { 200 if (comp == null) { 201 comp = GuiHelper.runInEDTAndWaitAndReturn(() -> new SearchItemComponent(this)); 202 } 203 return comp; 204 } 205 206 public void updateComponents() throws InvocationTargetException, InterruptedException { 207 SwingUtilities.invokeAndWait(() -> { 208 //invokeLater is faster but causes too many calls at once and blocks the UI 209 SearchItemComponent c = getComponent(); 210 c.setVisible(this.isVisible()); 211 if (isVisible()) { 212 c.setToolTipText(toString()); 213 c.setText(highlight); 214 c.setExpanded(true); 215 } 216 }); 217 if (isVisible()) { 218 for (SearchItem child : children) { 219 child.updateComponents(); 220 } 221 } 222 } 223 224 /** 225 * @return the hasVisibleChildren 226 */ 227 public boolean hasVisibleChildren() { 228 return hasVisibleChildren; 229 } 230 231 /** 232 * @return the childComponentsSearchable 233 */ 234 public boolean isChildComponentsSearchable() { 235 return childComponentsSearchable; 236 } 237 238 /** 239 * @return the text2 240 */ 241 public String getText() { 242 return text; 243 } 244 245 /** 246 * @param text the text to set 247 */ 248 public void setText(String text) { 249 this.text = stripHtml(text); 250 this.textNormalized = SearchFilters.normalize(this.text); 251 } 252 253 /** 254 * @return the tooltip 255 */ 256 public String getTooltip() { 257 return tooltip; 258 } 259 260 public void addTooltip(String tooltip) { 261 if (this.getTooltip() == null || this.getTooltip().trim().isEmpty()) { 262 this.setTooltip(tooltip); 263 } 264 } 265 266 /** 267 * @param tooltip the tooltip to set 268 */ 269 public void setTooltip(String tooltip) { 270 this.tooltip = stripHtml(tooltip); 271 this.tooltipNormalized = SearchFilters.normalize(this.tooltip); 272 } 273 274 private static String stripHtml(String str) { 275 if (str == null || str.trim().length() == 0) 276 return null; 277 278 if (str.indexOf('<') > -1) { 279 str = str.replaceAll("(?i)(<br\\s/>\\s*)+", ": ").replaceAll("(<style>.*</style>|<[^<>]*>)", " ").trim() 280 .replaceAll(" +", " "); 281 } 282 283 if (str.indexOf(':') == str.length() - 1) { 284 str = str.substring(0, str.length() - 1); 285 } 286 287 return str; 288 } 289 290 } 291 No newline at end of file -
src/org/openstreetmap/josm/gui/preferences/search/SearchItemComponent.java
1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.gui.preferences.search; 3 4 import java.awt.BasicStroke; 5 import java.awt.Color; 6 import java.awt.Cursor; 7 import java.awt.Graphics; 8 import java.awt.Graphics2D; 9 import java.awt.GridBagLayout; 10 import java.awt.RenderingHints; 11 import java.awt.event.MouseAdapter; 12 import java.awt.event.MouseEvent; 13 14 import javax.swing.BorderFactory; 15 import javax.swing.ImageIcon; 16 import javax.swing.JLabel; 17 import javax.swing.JPanel; 18 19 import org.openstreetmap.josm.tools.GBC; 20 import org.openstreetmap.josm.tools.ImageProvider; 21 import org.openstreetmap.josm.tools.ImageProvider.ImageSizes; 22 23 public class SearchItemComponent extends JPanel { 24 25 private final GridBagLayout own_gbl = new GridBagLayout(); 26 private final JLabel lbl = new JLabel(); 27 private final JLabel expander = new JLabel(); 28 private JPanel childPanel; 29 private final SearchItem item; 30 31 private GridBagLayout parent_gbl; 32 private boolean expanded = true; 33 private GBC gbc_nw; 34 private GBC gbc_nw_fill; 35 36 private static final ImageIcon ICON_EXPANDED = ImageProvider.get("dialogs", "down", ImageSizes.SMALLICON); 37 private static final ImageIcon ICON_COLLAPSED = ImageProvider.get("dialogs", "next", ImageSizes.SMALLICON); 38 39 private static final Cursor CURSOR_HAND = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); 40 private static final Cursor CURSOR_DEFAULT = Cursor.getDefaultCursor(); 41 42 public SearchItemComponent(SearchItem item) { 43 super(new GridBagLayout()); 44 setVisible(false); 45 this.item = item; 46 add(expander); 47 48 gbc_nw = GBC.eol(); 49 gbc_nw.anchor = GBC.NORTHWEST; 50 gbc_nw_fill = GBC.eol().fill(GBC.HORIZONTAL); 51 gbc_nw_fill.anchor = GBC.NORTHWEST; 52 add(lbl, gbc_nw_fill); 53 lbl.setCursor(CURSOR_HAND); 54 lbl.setBackground(Color.cyan); 55 //lbl.setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 0)); 56 setBorder(BorderFactory.createEmptyBorder(10, item.level == 0 ? 10 : 17, item.level == 0 ? 25 : 0, 10)); 57 58 expander.addMouseListener(new MouseAdapter() { 59 @Override 60 public void mouseClicked(MouseEvent e) { 61 setExpanded(!isExpanded()); 62 } 63 }); 64 65 addMouseListener(new MouseAdapter() { 66 @Override 67 public void mouseClicked(MouseEvent e) { 68 item.showOriginalComponent(); 69 } 70 }); 71 } 72 73 public void setText(String txt) { 74 lbl.setText(txt); 75 } 76 77 @Override 78 protected void paintComponent(Graphics graphics) { 79 super.paintComponent(graphics); 80 81 if (item.level != 0) 82 return; 83 84 Graphics2D g = (Graphics2D) graphics; 85 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 86 87 int width = getWidth(), 88 height = getHeight(), 89 stroke = 1, 90 offset = 5, 91 radius = 15; 92 93 94 g.setColor(new Color(0, 0, 0, 125)); 95 g.fillRoundRect(offset, offset, width - stroke - offset, height - stroke - offset - 10, radius, radius); 96 97 g.setColor(getBackground()); 98 g.fillRoundRect(0, 0, width - 5, height - 15, radius, radius); 99 100 g.setColor(getForeground()); 101 g.setStroke(new BasicStroke(stroke)); 102 g.drawRoundRect(0, 0, width - 5, height - 15, radius, radius); 103 104 g.setStroke(new BasicStroke()); 105 } 106 107 /** 108 * @return the expanded 109 */ 110 public boolean isExpanded() { 111 return expanded; 112 } 113 114 /** 115 * @param expanded the expanded to set 116 */ 117 public void setExpanded(boolean expanded) { 118 if (item.hasVisibleChildren()) { 119 this.expanded = expanded; 120 expander.setIcon(expanded ? ICON_EXPANDED : ICON_COLLAPSED); 121 expander.setCursor(CURSOR_HAND); 122 getChildPanel().setVisible(expanded); 123 } else { 124 expander.setIcon(ICON_COLLAPSED); 125 } 126 } 127 128 /** 129 * @return the childPanel 130 */ 131 public JPanel getChildPanel() { 132 if (childPanel == null) { 133 childPanel = new JPanel(own_gbl); 134 add(childPanel, gbc_nw_fill); 135 } 136 return childPanel; 137 } 138 139 public void addChild(SearchItemComponent comp) { 140 getChildPanel().add(comp, gbc_nw_fill); 141 comp.setParentGBL(own_gbl); 142 } 143 144 /** 145 * @param parent_gbl the parent_gbl to set 146 */ 147 public void setParentGBL(GridBagLayout parent_gbl) { 148 this.parent_gbl = parent_gbl; 149 } 150 151 } -
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 static org.openstreetmap.josm.tools.I18n.tr; 5 6 import java.awt.GridBagLayout; 7 import java.awt.SystemColor; 8 import java.lang.reflect.InvocationTargetException; 9 10 import javax.swing.BorderFactory; 11 import javax.swing.JLabel; 12 import javax.swing.JPanel; 13 import javax.swing.JScrollPane; 14 import javax.swing.SwingConstants; 15 import javax.swing.SwingUtilities; 16 17 import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane; 18 import org.openstreetmap.josm.tools.GBC; 19 import org.openstreetmap.josm.tools.JosmRuntimeException; 20 import org.openstreetmap.josm.tools.Logging; 21 22 /** 23 * Panel displaying search results in the preferences 24 * @author Bjoeni 25 */ 26 public class SearchPanel extends JScrollPane implements ISearchableComponent { 27 28 public static final boolean DARK_MODE = 130 < (Math.sqrt(Math.pow(SystemColor.text.getRed(), 2) * .241 29 + Math.pow(SystemColor.text.getGreen(), 2) * .691 + Math.pow(SystemColor.text.getBlue(), 2) * .068)); 30 31 private PreferenceTabbedPane tabs; 32 private SearchIndex searchIndex; 33 private JLabel lblStatus = new JLabel(tr("Enter something in the text fied to start searching")); 34 private JPanel panel = new JPanel(new GridBagLayout()); 35 private SearchThread thread; 36 37 public SearchPanel(PreferenceTabbedPane tabs) { 38 super(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 39 this.tabs = tabs; 40 searchIndex = new SearchIndex(tabs); 41 GBC gbc = GBC.eol().fill(GBC.HORIZONTAL); 42 gbc.anchor = GBC.NORTHWEST; 43 gbc.weightx = 1; 44 panel.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15)); 45 panel.add(searchIndex.getPanel(), gbc); 46 gbc.gridy = 1; 47 lblStatus.setHorizontalAlignment(SwingConstants.CENTER); 48 gbc.fill(); 49 gbc.anchor = GBC.CENTER; 50 panel.add(lblStatus, gbc); 51 //setAlignmentX(LEFT_ALIGNMENT); 52 setViewportView(panel); 53 } 54 55 public void search(String text) { 56 if (thread != null && thread.isAlive()) { 57 Logging.debug("alive"); 58 thread.awaitBuildAndRestart(text); 59 } else { 60 Logging.debug("dead"); 61 thread = new SearchThread(text); 62 thread.start(); 63 } 64 } 65 66 public PreferenceTabbedPane getTabPane() { 67 return tabs; 68 } 69 70 public void destroyIndex() { 71 removeAll(); 72 panel = null; 73 lblStatus = null; 74 setViewport(null); 75 this.searchIndex = null; 76 } 77 78 @Override 79 public boolean isSearchable() { 80 return false; 81 } 82 83 class SearchThread extends Thread { 84 private String text; 85 private StringBuilder searchableSettings; 86 private boolean building; 87 private boolean interrupted; 88 private String nextText; 89 private boolean justBuiltIndex; 90 91 public SearchThread(String text) { 92 this.text = text; 93 } 94 95 public SearchThread(String text, boolean justBuiltIndex) { 96 this.text = text; 97 this.justBuiltIndex = justBuiltIndex; 98 } 99 100 @Override 101 public void run() { 102 try { 103 boolean buildIndex = !searchIndex.isBuilt(); // || "".equals(text); 104 if (buildIndex) { 105 building = true; 106 Logging.info("Building search index..."); 107 setText(tr("Building search index...")); 108 searchIndex.build(); 109 building = false; 110 if (interrupted) { 111 startNext(true); 112 return; 113 } 114 } 115 116 if (buildIndex || justBuiltIndex) { 117 setText(tr("Searching...")); 118 } 119 120 //if (!"".equals(text)) { 121 Logging.debug("searching \"" + text + "\""); 122 boolean found = searchIndex.filter(text); 123 Logging.debug("searched \"" + text + "\""); 124 //} 125 126 Logging.debug("before \"" + text + "\""); 127 searchIndex.updateComponents(); 128 Logging.debug("done \"" + text + "\""); 129 130 String txt = ""; 131 if (!found) { 132 txt += tr("No results found.") + "<br><br>"; 133 } 134 if (false) { //expertmode 135 txt += tr("You might get more results by enabling expert mode."); 136 } 137 setText(txt); 138 139 } catch (InterruptedException e) { 140 Logging.debug("Interrupted search thread \"" + text + "\""); 141 // to be expected 142 } catch (InvocationTargetException e) { 143 throw new JosmRuntimeException(e); 144 } 145 } 146 147 public void awaitBuildAndRestart(String nextText) { 148 this.nextText = nextText; 149 if (building) { 150 interrupted = true; 151 } else { 152 interrupt(); 153 startNext(false); 154 } 155 } 156 157 private void startNext(boolean justBuiltIndex) { 158 thread = new SearchThread(nextText, justBuiltIndex); 159 thread.start(); 160 } 161 162 private void setText(String txt) { 163 SwingUtilities.invokeLater(() -> { 164 lblStatus.setText(txt); 165 }); 166 } 167 } 168 169 } -
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 = 1; i < tabs.getTabCount(); i++) { 51 width = Math.max(width, tabs.getBoundsAt(i).width); //TODO all width the same, only check one? 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 ret.addAll(items); 136 /*items.forEach(item -> { 137 if (item.getText() != null && item.getText().trim().length() > 0) { 138 String txt = item.getText(); 139 if (txt.indexOf('<') != -1) { 140 txt = stripHtml(txt); 141 } 142 if (txt.indexOf(':') == txt.length() - 1) { 143 txt = txt.substring(0, item.getText().length() - 1); 144 } 145 item.setText(txt); 146 if (item.getTooltip() != null && item.getTooltip().indexOf('<') != -1) { 147 item.setTooltip(stripHtml(item.getTooltip())); 148 } 149 ret.add(item); 150 } 151 });*/ 152 }); 153 } 154 155 } catch (ClassCastException ex) { 156 // can't use 'instanceof' here, because the type information of T will already be stripped away at runtime 157 } 158 return ret; 159 } 160 161 public final static List<SearchTextFinder<?>> DEFAULT_SEARCH_TEXT_FINDERS = Collections.unmodifiableList(Arrays.asList( 162 163 new SearchTextFinder<>(JLabel::getText), 164 new SearchTextFinder<>(JRadioButton::getText), 165 new SearchTextFinder<>(JCheckBox::getText), 166 167 new SearchTextFinder<JTable>().addItem(table -> { 168 List<SearchItem> lst = new ArrayList<>(); 169 TableModel tm = table.getModel(); 170 for (int row = 0; row < tm.getRowCount(); row++) { 171 for (int col = 0; col < tm.getColumnCount(); col++) { 172 Object obj = tm.getValueAt(row, col); 173 Component renderer = table.getCellRenderer(row, col) 174 .getTableCellRendererComponent(table, obj, false, false, row, col); 175 if (renderer instanceof JLabel) { 176 JLabel label = (JLabel) renderer; 177 boolean isLast = col == tm.getColumnCount() - 1; 178 lst.add(new SearchItem(label, label.getText(), label.getToolTipText(), isLast, false)); 179 } 180 } 181 } 182 return lst.toArray(new SearchItem[0]); 183 }), 184 185 new SearchTextFinder<JComboBox<Object>>().addItem(combobox -> { 186 List<SearchItem> lst = new ArrayList<>(); 187 int size = combobox.getItemCount(); 188 JList<Object> listStub = new JList<>(); 189 for (int i = 0; i < size; i++) { 190 Component renderer = combobox.getRenderer().getListCellRendererComponent(listStub, 191 combobox.getItemAt(i), i, false, false); 192 if (renderer instanceof JLabel) { 193 JLabel label = (JLabel) renderer; 194 lst.add(new SearchItem(renderer, label.getText(), label.getToolTipText(), false, false)); 195 } 196 } 197 return lst.toArray(new SearchItem[0]); 198 }))); 199 200 } 201 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/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 ret.addAll(items); 136 /*items.forEach(item -> { 137 if (item.getText() != null && item.getText().trim().length() > 0) { 138 String txt = item.getText(); 139 if (txt.indexOf('<') != -1) { 140 txt = stripHtml(txt); 141 } 142 if (txt.indexOf(':') == txt.length() - 1) { 143 txt = txt.substring(0, item.getText().length() - 1); 144 } 145 item.setText(txt); 146 if (item.getTooltip() != null && item.getTooltip().indexOf('<') != -1) { 147 item.setTooltip(stripHtml(item.getTooltip())); 148 } 149 ret.add(item); 150 } 151 });*/ 152 }); 153 } 154 155 } catch (ClassCastException ex) { 156 // can't use 'instanceof' here, because the type information of T will already be stripped away at runtime 157 } 158 return ret; 159 } 160 161 public final static List<SearchTextFinder<?>> DEFAULT_SEARCH_TEXT_FINDERS = Collections.unmodifiableList(Arrays.asList( 162 163 new SearchTextFinder<>(JLabel::getText), 164 new SearchTextFinder<>(JRadioButton::getText), 165 new SearchTextFinder<>(JCheckBox::getText), 166 167 new SearchTextFinder<JTable>().addItem(table -> { 168 List<SearchItem> lst = new ArrayList<>(); 169 TableModel tm = table.getModel(); 170 for (int row = 0; row < tm.getRowCount(); row++) { 171 for (int col = 0; col < tm.getColumnCount(); col++) { 172 Object obj = tm.getValueAt(row, col); 173 Component renderer = table.getCellRenderer(row, col) 174 .getTableCellRendererComponent(table, obj, false, false, row, col); 175 if (renderer instanceof JLabel) { 176 JLabel label = (JLabel) renderer; 177 boolean isLast = col == tm.getColumnCount() - 1; 178 lst.add(new SearchItem(label, label.getText(), label.getToolTipText(), isLast, false)); 179 } 180 } 181 } 182 return lst.toArray(new SearchItem[0]); 183 }), 184 185 new SearchTextFinder<JComboBox<Object>>().addItem(combobox -> { 186 List<SearchItem> lst = new ArrayList<>(); 187 int size = combobox.getItemCount(); 188 JList<Object> listStub = new JList<>(); 189 for (int i = 0; i < size; i++) { 190 Component renderer = combobox.getRenderer().getListCellRendererComponent(listStub, 191 combobox.getItemAt(i), i, false, false); 192 if (renderer instanceof JLabel) { 193 JLabel label = (JLabel) renderer; 194 lst.add(new SearchItem(renderer, label.getText(), label.getToolTipText(), false, false)); 195 } 196 } 197 return lst.toArray(new SearchItem[0]); 198 }))); 199 200 } 201 No newline at end of file