source: josm/trunk/src/org/openstreetmap/josm/actions/search/SearchAction.java@ 2264

Last change on this file since 2264 was 2264, checked in by stoecker, 15 years ago

applied #3676 - patch by Dave Hansen - cleanup selection handling interface

  • Property svn:eol-style set to native
File size: 13.5 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.actions.search;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Font;
7import java.awt.GridBagLayout;
8import java.awt.event.ActionEvent;
9import java.awt.event.KeyEvent;
10import java.util.Collection;
11import java.util.LinkedList;
12
13import javax.swing.ButtonGroup;
14import javax.swing.JCheckBox;
15import javax.swing.JLabel;
16import javax.swing.JOptionPane;
17import javax.swing.JPanel;
18import javax.swing.JRadioButton;
19import javax.swing.JTextField;
20
21import org.openstreetmap.josm.Main;
22import org.openstreetmap.josm.actions.JosmAction;
23import org.openstreetmap.josm.data.osm.OsmPrimitive;
24import org.openstreetmap.josm.gui.ExtendedDialog;
25import org.openstreetmap.josm.tools.GBC;
26import org.openstreetmap.josm.tools.Shortcut;
27import org.openstreetmap.josm.data.osm.Filter;
28import org.openstreetmap.josm.data.osm.DataSet;
29
30public class SearchAction extends JosmAction{
31
32 public static final int DEFAULT_SEARCH_HISTORY_SIZE = 10;
33
34
35 public static enum SearchMode {
36 replace, add, remove, in_selection
37 }
38
39 public static final LinkedList<SearchSetting> searchHistory = new LinkedList<SearchSetting>();
40
41 private static SearchSetting lastSearch = null;
42
43 public SearchAction() {
44 super(tr("Search..."), "dialogs/search", tr("Search for objects."),
45 Shortcut.registerShortcut("system:find", tr("Search..."), KeyEvent.VK_F, Shortcut.GROUP_HOTKEY), true);
46 }
47
48 public void actionPerformed(ActionEvent e) {
49 if (!isEnabled())
50 return;
51 if (Main.map == null) {
52 JOptionPane.showMessageDialog(
53 Main.parent,
54 tr("Can't search because there is no loaded data."),
55 tr("Warning"),
56 JOptionPane.WARNING_MESSAGE
57 );
58 return;
59 }
60 SearchSetting s = lastSearch;
61 if (s == null) {
62 s = new SearchSetting("", SearchMode.replace, false, false);
63 }
64 SearchSetting se = showSearchDialog(s);
65 if(se != null) {
66 searchWithHistory(se);
67 }
68 }
69
70 public static SearchSetting showSearchDialog(SearchSetting initialValues) {
71 JLabel label = new JLabel( initialValues instanceof Filter ? tr("Please enter a filter string.") : tr("Please enter a search string."));
72 final JTextField input = new JTextField(initialValues.text);
73 input.selectAll();
74 input.requestFocusInWindow();
75 JRadioButton replace = new JRadioButton(tr("replace selection"), initialValues.mode == SearchMode.replace);
76 JRadioButton add = new JRadioButton(tr("add to selection"), initialValues.mode == SearchMode.add);
77 JRadioButton remove = new JRadioButton(tr("remove from selection"), initialValues.mode == SearchMode.remove);
78 JRadioButton in_selection = new JRadioButton(tr("find in selection"), initialValues.mode == SearchMode.in_selection);
79 ButtonGroup bg = new ButtonGroup();
80 bg.add(replace);
81 bg.add(add);
82 bg.add(remove);
83 bg.add(in_selection);
84
85 JCheckBox caseSensitive = new JCheckBox(tr("case sensitive"), initialValues.caseSensitive);
86 JCheckBox regexSearch = new JCheckBox(tr("regular expression"), initialValues.regexSearch);
87
88 JPanel left = new JPanel(new GridBagLayout());
89 left.add(label, GBC.eop());
90 left.add(input, GBC.eop().fill(GBC.HORIZONTAL));
91 left.add(replace, GBC.eol());
92 DataSet ds = Main.main.getCurrentDataSet();
93 left.add(add, GBC.eol());
94 left.add(remove, GBC.eol());
95 left.add(in_selection, GBC.eop());
96 left.add(caseSensitive, GBC.eol());
97 left.add(regexSearch, GBC.eol());
98
99 JPanel right = new JPanel();
100 JLabel description =
101 new JLabel("<html><ul>"
102 + "<li>"+tr("<b>Baker Street</b> - 'Baker' and 'Street' in any key or name.")+"</li>"
103 + "<li>"+tr("<b>\"Baker Street\"</b> - 'Baker Street' in any key or name.")+"</li>"
104 + "<li>"+tr("<b>name:Bak</b> - 'Bak' anywhere in the name.")+"</li>"
105 + "<li>"+tr("<b>type=route</b> - key 'type' with value exactly 'route'.") + "</li>"
106 + "<li>"+tr("<b>type=*</b> - key 'type' with any value. Try also <b>*=value</b>, <b>type=</b>, <b>*=*</b>, <b>*=</b>") + "</li>"
107 + "<li>"+tr("<b>-name:Bak</b> - not 'Bak' in the name.")+"</li>"
108 + "<li>"+tr("<b>foot:</b> - key=foot set to any value.")+"</li>"
109 + "<li>"+tr("<u>Special targets:</u>")+"</li>"
110 + "<li>"+tr("<b>type:</b> - type of the object (<b>node</b>, <b>way</b>, <b>relation</b>)")+"</li>"
111 + "<li>"+tr("<b>user:</b>... - all objects changed by user")+"</li>"
112 + "<li>"+tr("<b>id:</b>... - object with given ID (0 for new objects)")+"</li>"
113 + "<li>"+tr("<b>nodes:</b>... - object with given number of nodes")+"</li>"
114 + "<li>"+tr("<b>modified</b> - all changed objects")+"</li>"
115 + "<li>"+tr("<b>selected</b> - all selected objects")+"</li>"
116 + "<li>"+tr("<b>incomplete</b> - all incomplete objects")+"</li>"
117 + "<li>"+tr("<b>untagged</b> - all untagged objects")+"</li>"
118 + "<li>"+tr("<b>child <i>expr</i></b> - all children of objects matching the expression")+"</li>"
119 + "<li>"+tr("<b>parent <i>expr</i></b> - all parents of objects matching the expression")+"</li>"
120 + "<li>"+tr("Use <b>|</b> or <b>OR</b> to combine with logical or")+"</li>"
121 + "<li>"+tr("Use <b>\"</b> to quote operators (e.g. if key contains :)")+"</li>"
122 + "<li>"+tr("Use <b>(</b> and <b>)</b> to group expressions")+"</li>"
123 + "</ul></html>");
124 description.setFont(description.getFont().deriveFont(Font.PLAIN));
125 right.add(description);
126
127 final JPanel p = new JPanel();
128 p.add(left);
129 p.add(right);
130 ExtendedDialog dialog = new ExtendedDialog(
131 Main.parent,
132 initialValues instanceof Filter ? tr("Filter") : tr("Search"),
133 new String[] {
134 initialValues instanceof Filter ? tr("Submit filter") : tr("Start Search"),
135 tr("Cancel")}
136 );
137 dialog.setButtonIcons(new String[] {"dialogs/search.png", "cancel.png"});
138 dialog.setContent(p);
139 dialog.showDialog();
140 int result = dialog.getValue();
141
142 if(result != 1) return null;
143
144 // User pressed OK - let's perform the search
145 SearchMode mode = replace.isSelected() ? SearchAction.SearchMode.replace
146 : (add.isSelected() ? SearchAction.SearchMode.add
147 : (remove.isSelected() ? SearchAction.SearchMode.remove : SearchAction.SearchMode.in_selection));
148 initialValues.text = input.getText();
149 initialValues.mode = mode;
150 initialValues.caseSensitive = caseSensitive.isSelected();
151 initialValues.regexSearch = regexSearch.isSelected();
152 return initialValues;
153 }
154
155 /**
156 * Adds the search specified by the settings in <code>s</code> to the
157 * search history and performs the search.
158 *
159 * @param s
160 */
161 public static void searchWithHistory(SearchSetting s) {
162 if(searchHistory.isEmpty() || !s.equals(searchHistory.getFirst())) {
163 searchHistory.addFirst(new SearchSetting(s));
164 }
165 while (searchHistory.size() > Main.pref.getInteger("search.history-size", DEFAULT_SEARCH_HISTORY_SIZE)) {
166 searchHistory.removeLast();
167 }
168 lastSearch = new SearchSetting(s);
169 search(s);
170 }
171
172 public static void searchWithoutHistory(SearchSetting s) {
173 lastSearch = new SearchSetting(s);
174 search(s);
175 }
176
177 public interface Function{
178 public Boolean isSomething(OsmPrimitive o);
179 }
180
181 public static Integer getSelection(SearchSetting s, Collection<OsmPrimitive> sel, Function f) {
182 Integer foundMatches = 0;
183 try {
184 String searchText = s.text;
185 if(s instanceof Filter){
186 searchText = "(" + s.text + ")" + (((Filter)s).applyForChildren ? ("| child (" + s.text + ")"): "");
187 searchText = (((Filter)s).inverted ? "-" : "") + "(" + searchText + ")";
188 }
189 /*System.out.println(searchText);*/
190 SearchCompiler.Match matcher = SearchCompiler.compile(searchText, s.caseSensitive, s.regexSearch);
191 foundMatches = 0;
192 for (OsmPrimitive osm : Main.main.getCurrentDataSet().allNonDeletedCompletePrimitives()) {
193 if (s.mode == SearchMode.replace) {
194 if (matcher.match(osm)) {
195 sel.add(osm);
196 ++foundMatches;
197 } else {
198 sel.remove(osm);
199 }
200 } else if (s.mode == SearchMode.add && !f.isSomething(osm) && matcher.match(osm)) {
201 sel.add(osm);
202 ++foundMatches;
203 } else if (s.mode == SearchMode.remove && f.isSomething(osm) && matcher.match(osm)) {
204 sel.remove(osm);
205 ++foundMatches;
206 } else if (s.mode == SearchMode.in_selection && f.isSomething(osm)&& !matcher.match(osm)) {
207 sel.remove(osm);
208 ++foundMatches;
209 }
210 }
211 } catch (SearchCompiler.ParseError e) {
212 JOptionPane.showMessageDialog(
213 Main.parent,
214 e.getMessage(),
215 tr("Error"),
216 JOptionPane.ERROR_MESSAGE
217
218 );
219 }
220 return foundMatches;
221 }
222
223 public static void search(String search, SearchMode mode, boolean caseSensitive, boolean regexSearch) {
224 search(new SearchSetting(search, mode, caseSensitive, regexSearch));
225 }
226
227 public static void search(SearchSetting s) {
228 // FIXME: This is confusing. The GUI says nothing about loading primitives from an URL. We'd like to *search*
229 // for URLs in the current data set.
230 // Disabling until a better solution is in place
231 //
232 // if (search.startsWith("http://") || search.startsWith("ftp://") || search.startsWith("https://")
233 // || search.startsWith("file:/")) {
234 // SelectionWebsiteLoader loader = new SelectionWebsiteLoader(search, mode);
235 // if (loader.url != null && loader.url.getHost() != null) {
236 // Main.worker.execute(loader);
237 // return;
238 // }
239 // }
240
241 final DataSet ds = Main.main.getCurrentDataSet();
242 Collection<OsmPrimitive> sel = ds.getSelected();
243 int foundMatches = getSelection(s, sel, new Function(){
244 public Boolean isSomething(OsmPrimitive o){
245 return ds.isSelected(o);
246 }
247 });
248 ds.setSelected(sel);
249 if (foundMatches == 0) {
250 String msg = null;
251 if (s.mode == SearchMode.replace) {
252 msg = tr("No match found for ''{0}''", s.text);
253 } else if (s.mode == SearchMode.add) {
254 msg = tr("Nothing added to selection by searching for ''{0}''", s.text);
255 } else if (s.mode == SearchMode.remove) {
256 msg = tr("Nothing removed from selection by searching for ''{0}''", s.text);
257 } else if (s.mode == SearchMode.in_selection) {
258 msg = tr("Nothing found in selection by searching for ''{0}''", s.text);
259 }
260 Main.map.statusLine.setHelpText(msg);
261 JOptionPane.showMessageDialog(
262 Main.parent,
263 msg,
264 tr("Warning"),
265 JOptionPane.WARNING_MESSAGE
266 );
267 } else {
268 Main.map.statusLine.setHelpText(tr("Found {0} matches", foundMatches));
269 }
270 }
271
272 public static class SearchSetting {
273 public String text;
274 public SearchMode mode;
275 public boolean caseSensitive;
276 public boolean regexSearch;
277
278 public SearchSetting(String text, SearchMode mode, boolean caseSensitive, boolean regexSearch) {
279 super();
280 this.caseSensitive = caseSensitive;
281 this.regexSearch = regexSearch;
282 this.mode = mode;
283 this.text = text;
284 }
285
286 public SearchSetting(SearchSetting original) {
287 super();
288 this.caseSensitive = original.caseSensitive;
289 this.regexSearch = original.regexSearch;
290 this.mode = original.mode;
291 this.text = original.text;
292 }
293
294 @Override
295 public String toString() {
296 String cs = caseSensitive ? tr("CS") : tr("CI");
297 String rx = regexSearch ? (", " + tr("RX")) : "";
298 return "\"" + text + "\" (" + cs + rx + ", " + mode + ")";
299 }
300
301 @Override
302 public boolean equals(Object other) {
303 if(!(other instanceof SearchSetting))
304 return false;
305 SearchSetting o = (SearchSetting) other;
306 return (o.caseSensitive == this.caseSensitive
307 && o.regexSearch == this.regexSearch
308 && o.mode.equals(this.mode)
309 && o.text.equals(this.text));
310 }
311 }
312
313 /**
314 * Refreshes the enabled state
315 *
316 */
317 @Override
318 protected void updateEnabledState() {
319 setEnabled(getEditLayer() != null);
320 }
321}
Note: See TracBrowser for help on using the repository browser.