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

Last change on this file since 4377 was 4377, checked in by stoecker, 13 years ago

use lower case search expressions only

  • Property svn:eol-style set to native
File size: 25.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.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.tr;
6import static org.openstreetmap.josm.tools.I18n.trc;
7
8import java.awt.Dimension;
9import java.awt.Font;
10import java.awt.GridBagLayout;
11import java.awt.event.ActionEvent;
12import java.awt.event.KeyEvent;
13import java.util.ArrayList;
14import java.util.Collection;
15import java.util.Collections;
16import java.util.HashSet;
17import java.util.LinkedList;
18import java.util.List;
19import java.util.Map;
20
21import javax.swing.ButtonGroup;
22import javax.swing.JCheckBox;
23import javax.swing.JLabel;
24import javax.swing.JOptionPane;
25import javax.swing.JPanel;
26import javax.swing.JRadioButton;
27
28import org.openstreetmap.josm.Main;
29import org.openstreetmap.josm.actions.ActionParameter;
30import org.openstreetmap.josm.actions.ActionParameter.SearchSettingsActionParameter;
31import org.openstreetmap.josm.actions.JosmAction;
32import org.openstreetmap.josm.actions.ParameterizedAction;
33import org.openstreetmap.josm.actions.search.SearchCompiler.ParseError;
34import org.openstreetmap.josm.data.osm.DataSet;
35import org.openstreetmap.josm.data.osm.Filter;
36import org.openstreetmap.josm.data.osm.OsmPrimitive;
37import org.openstreetmap.josm.gui.ExtendedDialog;
38import org.openstreetmap.josm.gui.widgets.HistoryComboBox;
39import org.openstreetmap.josm.tools.GBC;
40import org.openstreetmap.josm.tools.Property;
41import org.openstreetmap.josm.tools.Shortcut;
42
43public class SearchAction extends JosmAction implements ParameterizedAction {
44
45 public static final int DEFAULT_SEARCH_HISTORY_SIZE = 15;
46
47 private static final String SEARCH_EXPRESSION = "searchExpression";
48
49 public static enum SearchMode {
50 replace('R'), add('A'), remove('D'), in_selection('S');
51
52 private final char code;
53
54 SearchMode(char code) {
55 this.code = code;
56 }
57
58 public char getCode() {
59 return code;
60 }
61
62 public static SearchMode fromCode(char code) {
63 for (SearchMode mode: values()) {
64 if (mode.getCode() == code)
65 return mode;
66 }
67 return null;
68 }
69 }
70
71 private static LinkedList<SearchSetting> searchHistory = null;
72
73 public static Collection<SearchSetting> getSearchHistory() {
74 if (searchHistory == null) {
75 searchHistory = new LinkedList<SearchSetting>();
76 for (String s: Main.pref.getCollection("search.history", Collections.<String>emptyList())) {
77 SearchSetting ss = SearchSetting.readFromString(s);
78 if (ss != null) {
79 searchHistory.add(ss);
80 }
81 }
82 }
83
84 return searchHistory;
85 }
86
87 public static void saveToHistory(SearchSetting s) {
88 if(searchHistory.isEmpty() || !s.equals(searchHistory.getFirst())) {
89 searchHistory.addFirst(new SearchSetting(s));
90 }
91 int maxsize = Main.pref.getInteger("search.history-size", DEFAULT_SEARCH_HISTORY_SIZE);
92 while (searchHistory.size() > maxsize) {
93 searchHistory.removeLast();
94 }
95 List<String> savedHistory = new ArrayList<String>();
96 for (SearchSetting item: searchHistory) {
97 savedHistory.add(item.writeToString());
98 }
99 Main.pref.putCollection("search.history", savedHistory);
100 }
101
102 public static List<String> getSearchExpressionHistory() {
103 ArrayList<String> ret = new ArrayList<String>(getSearchHistory().size());
104 for (SearchSetting ss: getSearchHistory()) {
105 ret.add(ss.text);
106 }
107 return ret;
108 }
109
110 private static SearchSetting lastSearch = null;
111
112 public SearchAction() {
113 super(tr("Search..."), "dialogs/search", tr("Search for objects."),
114 Shortcut.registerShortcut("system:find", tr("Search..."), KeyEvent.VK_F, Shortcut.GROUP_HOTKEY), true);
115 putValue("help", ht("/Action/Search"));
116 }
117
118 public void actionPerformed(ActionEvent e) {
119 if (!isEnabled())
120 return;
121 search();
122 }
123
124 public void actionPerformed(ActionEvent e, Map<String, Object> parameters) {
125 if (parameters.get(SEARCH_EXPRESSION) == null) {
126 actionPerformed(e);
127 } else {
128 searchWithoutHistory((SearchSetting) parameters.get(SEARCH_EXPRESSION));
129 }
130 }
131
132 private static class DescriptionTextBuilder {
133
134 StringBuilder s = new StringBuilder(4096);
135
136 public StringBuilder append(String string) {
137 return s.append(string);
138 }
139
140 StringBuilder appendItem(String item) {
141 return append("<li>").append(item).append("</li>\n");
142 }
143
144 StringBuilder appendItemHeader(String itemHeader) {
145 return append("<li class=\"header\">").append(itemHeader).append("</li>\n");
146 }
147
148 @Override
149 public String toString() {
150 return s.toString();
151 }
152 }
153
154 public static SearchSetting showSearchDialog(SearchSetting initialValues) {
155 if (initialValues == null) {
156 initialValues = new SearchSetting();
157 }
158 // -- prepare the combo box with the search expressions
159 //
160 JLabel label = new JLabel( initialValues instanceof Filter ? tr("Filter string:") : tr("Search string:"));
161 final HistoryComboBox hcbSearchString = new HistoryComboBox();
162 hcbSearchString.setText(initialValues.text);
163 hcbSearchString.getEditor().selectAll();
164 hcbSearchString.getEditor().getEditorComponent().requestFocusInWindow();
165 hcbSearchString.setToolTipText(tr("Enter the search expression"));
166 // we have to reverse the history, because ComboBoxHistory will reverse it again
167 // in addElement()
168 //
169 List<String> searchExpressionHistory = getSearchExpressionHistory();
170 Collections.reverse(searchExpressionHistory);
171 hcbSearchString.setPossibleItems(searchExpressionHistory);
172 hcbSearchString.setPreferredSize(new Dimension(40, hcbSearchString.getPreferredSize().height));
173
174 JRadioButton replace = new JRadioButton(tr("replace selection"), initialValues.mode == SearchMode.replace);
175 JRadioButton add = new JRadioButton(tr("add to selection"), initialValues.mode == SearchMode.add);
176 JRadioButton remove = new JRadioButton(tr("remove from selection"), initialValues.mode == SearchMode.remove);
177 JRadioButton in_selection = new JRadioButton(tr("find in selection"), initialValues.mode == SearchMode.in_selection);
178 ButtonGroup bg = new ButtonGroup();
179 bg.add(replace);
180 bg.add(add);
181 bg.add(remove);
182 bg.add(in_selection);
183
184 final JCheckBox caseSensitive = new JCheckBox(tr("case sensitive"), initialValues.caseSensitive);
185 JCheckBox allElements = new JCheckBox(tr("all objects"), initialValues.allElements);
186 allElements.setToolTipText(tr("Also include incomplete and deleted objects in search."));
187 final JCheckBox regexSearch = new JCheckBox(tr("regular expression"), initialValues.regexSearch);
188
189 JPanel top = new JPanel(new GridBagLayout());
190 top.add(label, GBC.std().insets(0, 0, 5, 0));
191 top.add(hcbSearchString, GBC.eol().fill(GBC.HORIZONTAL));
192 JPanel left = new JPanel(new GridBagLayout());
193 left.add(replace, GBC.eol());
194 left.add(add, GBC.eol());
195 left.add(remove, GBC.eol());
196 left.add(in_selection, GBC.eop());
197 left.add(caseSensitive, GBC.eol());
198 left.add(allElements, GBC.eol());
199 left.add(regexSearch, GBC.eol());
200
201 JPanel right = new JPanel();
202 DescriptionTextBuilder descriptionText = new DescriptionTextBuilder();
203 descriptionText.append("<html><style>li.header{font-size:110%; list-style-type:none; margin-top:5px;}</style><ul>");
204 descriptionText.appendItem(tr("<b>Baker Street</b> - ''Baker'' and ''Street'' in any key or name."));
205 descriptionText.appendItem(tr("<b>\"Baker Street\"</b> - ''Baker Street'' in any key or name."));
206 descriptionText.appendItem(tr("<b>name:Bak</b> - ''Bak'' anywhere in the name."));
207 descriptionText.appendItem(tr("<b>type=route</b> - key ''type'' with value exactly ''route''.") + "</li>");
208 descriptionText.appendItem(tr("<b>type=*</b> - key ''type'' with any value. Try also <b>*=value</b>, <b>type=</b>, <b>*=*</b>, <b>*=</b>") + "</li>");
209 descriptionText.appendItem(tr("<b>-name:Bak</b> - not ''Bak'' in the name."));
210 descriptionText.appendItem(tr("<b>oneway?</b> - oneway=yes, true, 1 or on"));
211 descriptionText.appendItem(tr("<b>foot:</b> - key=foot set to any value."));
212 descriptionText.appendItemHeader(tr("Special targets"));
213 /* I18n: don't translate the bold text keyword */ descriptionText.appendItem(tr("<b>type:</b> - objects with corresponding type (<b>node</b>, <b>way</b>, <b>relation</b>)"));
214 /* I18n: don't translate the bold text keyword */ descriptionText.appendItem(tr("<b>user:</b>... - objects changed by user"));
215 /* I18n: don't translate the bold text keyword */ descriptionText.appendItem(tr("<b>user:anonymous</b> - objects changed by anonymous users"));
216 /* I18n: don't translate the bold text keyword */ descriptionText.appendItem(tr("<b>id:</b>... - objects with given ID (0 for new objects)"));
217 /* I18n: don't translate the bold text keyword */ descriptionText.appendItem(tr("<b>version:</b>... - objects with given version (0 objects without an assigned version)"));
218 /* I18n: don't translate the bold text keyword */ descriptionText.appendItem(tr("<b>changeset:</b>... - objects with given changeset id (0 objects without assigned changeset)"));
219 /* I18n: don't translate the bold text keyword */ descriptionText.appendItem(tr("<b>nodes:</b>... - objects with given number of nodes (nodes:count, nodes:min-max, nodes:min- or nodes:-max)"));
220 /* I18n: don't translate the bold text keyword */ descriptionText.appendItem(tr("<b>tags:</b>... - objects with given number of tags (nodes:count, nodes:min-max, nodes:min- or nodes:-max)"));
221 /* I18n: don't translate the bold text keyword */ descriptionText.appendItem(tr("<b>role:</b>... - objects with given role in a relation"));
222 /* I18n: don't translate the bold text keyword */ descriptionText.appendItem(tr("<b>timestamp:</b>... - objects with this timestamp (<b>2009-11-12T14:51:09Z</b>, <b>2009-11-12</b> or <b>T14:51</b> ...)"));
223 /* I18n: don't translate the bold text keyword */ descriptionText.appendItem(tr("<b>areasize:</b>... - closed ways with given area in m\u00b2 (areasize:min-max or areasize:max)"));
224 /* I18n: don't translate the bold text keyword */ descriptionText.appendItem(tr("<b>modified</b> - all changed objects"));
225 /* I18n: don't translate the bold text keyword */ descriptionText.appendItem(tr("<b>selected</b> - all selected objects"));
226 /* I18n: don't translate the bold text keyword */ descriptionText.appendItem(tr("<b>incomplete</b> - all incomplete objects"));
227 /* I18n: don't translate the bold text keyword */ descriptionText.appendItem(tr("<b>untagged</b> - all untagged objects"));
228 /* I18n: don't translate the bold text keyword */ descriptionText.appendItem(tr("<b>closed</b> - all closed ways (a node is not considered closed)"));
229 /* I18n: don't translate the bold text keyword */ descriptionText.appendItem(tr("<b>child <i>expr</i></b> - all children of objects matching the expression"));
230 /* I18n: don't translate the bold text keyword */ descriptionText.appendItem(tr("<b>parent <i>expr</i></b> - all parents of objects matching the expression"));
231 /* I18n: don't translate the bold text keyword */ descriptionText.appendItem(tr("<b>(all)indownloadedarea</b> - objects (and all its way nodes / relation members) in downloaded area"));
232 /* I18n: don't translate the bold text keyword */ descriptionText.appendItem(tr("<b>(all)inview</b> - objects (and all its way nodes / relation members) in current view"));
233 /* I18n: don't translate the bold text keyword */ descriptionText.appendItem(tr("Use <b>|</b> or <b>OR</b> to combine with logical or"));
234 descriptionText.appendItem(tr("Use <b>\"</b> to quote operators (e.g. if key contains <b>:</b>)")
235 + "<br/>"
236 + tr("Within quoted strings the <b>\"</b> and <b>\\</b> characters need to be escaped by a preceding <b>\\</b> (e.g. <b>\\\"</b> and <b>\\\\</b>)."));
237 descriptionText.appendItem(tr("Use <b>(</b> and <b>)</b> to group expressions"));
238 descriptionText.append("</ul></html>");
239 JLabel description = new JLabel(descriptionText.toString());
240 description.setFont(description.getFont().deriveFont(Font.PLAIN));
241 right.add(description);
242
243 final JPanel p = new JPanel(new GridBagLayout());
244 p.add(top, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 5, 5, 0));
245 p.add(left, GBC.std().anchor(GBC.NORTH).insets(5, 10, 10, 0));
246 p.add(right, GBC.eol());
247 ExtendedDialog dialog = new ExtendedDialog(
248 Main.parent,
249 initialValues instanceof Filter ? tr("Filter") : tr("Search"),
250 new String[] {
251 initialValues instanceof Filter ? tr("Submit filter") : tr("Start Search"),
252 tr("Cancel")}
253 ) {
254 @Override
255 protected void buttonAction(int buttonIndex, ActionEvent evt) {
256 if (buttonIndex == 0) {
257 try {
258 SearchCompiler.compile(hcbSearchString.getText(), caseSensitive.isSelected(), regexSearch.isSelected());
259 super.buttonAction(buttonIndex, evt);
260 } catch (ParseError e) {
261 JOptionPane.showMessageDialog(
262 Main.parent,
263 tr("Search expression is not valid: \n\n {0}", e.getMessage()),
264 tr("Invalid search expression"),
265 JOptionPane.ERROR_MESSAGE);
266 }
267 } else {
268 super.buttonAction(buttonIndex, evt);
269 }
270 }
271 };
272 dialog.setButtonIcons(new String[] {"dialogs/search.png", "cancel.png"});
273 dialog.configureContextsensitiveHelp("/Action/Search", true /* show help button */);
274 dialog.setContent(p);
275 dialog.showDialog();
276 int result = dialog.getValue();
277
278 if(result != 1) return null;
279
280 // User pressed OK - let's perform the search
281 SearchMode mode = replace.isSelected() ? SearchAction.SearchMode.replace
282 : (add.isSelected() ? SearchAction.SearchMode.add
283 : (remove.isSelected() ? SearchAction.SearchMode.remove : SearchAction.SearchMode.in_selection));
284 initialValues.text = hcbSearchString.getText();
285 initialValues.mode = mode;
286 initialValues.caseSensitive = caseSensitive.isSelected();
287 initialValues.allElements = allElements.isSelected();
288 initialValues.regexSearch = regexSearch.isSelected();
289 return initialValues;
290 }
291
292 /**
293 * Launches the dialog for specifying search criteria and runs
294 * a search
295 */
296 public static void search() {
297 SearchSetting se = showSearchDialog(lastSearch);
298 if(se != null) {
299 searchWithHistory(se);
300 }
301 }
302
303 /**
304 * Adds the search specified by the settings in <code>s</code> to the
305 * search history and performs the search.
306 *
307 * @param s
308 */
309 public static void searchWithHistory(SearchSetting s) {
310 saveToHistory(s);
311 lastSearch = new SearchSetting(s);
312 search(s);
313 }
314
315 public static void searchWithoutHistory(SearchSetting s) {
316 lastSearch = new SearchSetting(s);
317 search(s);
318 }
319
320 public interface Function{
321 public Boolean isSomething(OsmPrimitive o);
322 }
323
324 public static int getSelection(SearchSetting s, Collection<OsmPrimitive> sel, Function f) {
325 int foundMatches = 0;
326 try {
327 String searchText = s.text;
328 SearchCompiler.Match matcher = SearchCompiler.compile(searchText, s.caseSensitive, s.regexSearch);
329
330 if (s.mode == SearchMode.replace) {
331 sel.clear();
332 }
333
334 Collection<OsmPrimitive> all;
335 if(s.allElements) {
336 all = Main.main.getCurrentDataSet().allPrimitives();
337 } else {
338 all = Main.main.getCurrentDataSet().allNonDeletedCompletePrimitives();
339 }
340 for (OsmPrimitive osm : all) {
341 if (s.mode == SearchMode.replace) {
342 if (matcher.match(osm)) {
343 sel.add(osm);
344 ++foundMatches;
345 }
346 } else if (s.mode == SearchMode.add && !f.isSomething(osm) && matcher.match(osm)) {
347 sel.add(osm);
348 ++foundMatches;
349 } else if (s.mode == SearchMode.remove && f.isSomething(osm) && matcher.match(osm)) {
350 sel.remove(osm);
351 ++foundMatches;
352 } else if (s.mode == SearchMode.in_selection && f.isSomething(osm)&& !matcher.match(osm)) {
353 sel.remove(osm);
354 ++foundMatches;
355 }
356 }
357 } catch (SearchCompiler.ParseError e) {
358 JOptionPane.showMessageDialog(
359 Main.parent,
360 e.getMessage(),
361 tr("Error"),
362 JOptionPane.ERROR_MESSAGE
363
364 );
365 }
366 return foundMatches;
367 }
368
369 /**
370 * Version of getSelection that is customized for filter, but should
371 * also work in other context.
372 *
373 * @param s the search settings
374 * @param all the collection of all the primitives that should be considered
375 * @param p the property that should be set/unset if something is found
376 */
377 public static void getSelection(SearchSetting s, Collection<OsmPrimitive> all, Property<OsmPrimitive, Boolean> p) {
378 try {
379 String searchText = s.text;
380 if (s instanceof Filter && ((Filter)s).inverted) {
381 searchText = String.format("-(%s)", searchText);
382 }
383 SearchCompiler.Match matcher = SearchCompiler.compile(searchText, s.caseSensitive, s.regexSearch);
384
385 for (OsmPrimitive osm : all) {
386 if (s.mode == SearchMode.replace) {
387 if (matcher.match(osm)) {
388 p.set(osm, true);
389 } else {
390 p.set(osm, false);
391 }
392 } else if (s.mode == SearchMode.add && !p.get(osm) && matcher.match(osm)) {
393 p.set(osm, true);
394 } else if (s.mode == SearchMode.remove && p.get(osm) && matcher.match(osm)) {
395 p.set(osm, false);
396 } else if (s.mode == SearchMode.in_selection && p.get(osm) && !matcher.match(osm)) {
397 p.set(osm, false);
398 }
399 }
400 } catch (SearchCompiler.ParseError e) {
401 JOptionPane.showMessageDialog(
402 Main.parent,
403 e.getMessage(),
404 tr("Error"),
405 JOptionPane.ERROR_MESSAGE
406
407 );
408 }
409 }
410
411 public static void search(String search, SearchMode mode) {
412 search(new SearchSetting(search, mode, false, false, false));
413 }
414
415 public static void search(SearchSetting s) {
416 // FIXME: This is confusing. The GUI says nothing about loading primitives from an URL. We'd like to *search*
417 // for URLs in the current data set.
418 // Disabling until a better solution is in place
419 //
420 // if (search.startsWith("http://") || search.startsWith("ftp://") || search.startsWith("https://")
421 // || search.startsWith("file:/")) {
422 // SelectionWebsiteLoader loader = new SelectionWebsiteLoader(search, mode);
423 // if (loader.url != null && loader.url.getHost() != null) {
424 // Main.worker.execute(loader);
425 // return;
426 // }
427 // }
428
429 final DataSet ds = Main.main.getCurrentDataSet();
430 Collection<OsmPrimitive> sel = new HashSet<OsmPrimitive>(ds.getSelected());
431 int foundMatches = getSelection(s, sel, new Function(){
432 public Boolean isSomething(OsmPrimitive o){
433 return ds.isSelected(o);
434 }
435 });
436 ds.setSelected(sel);
437 if (foundMatches == 0) {
438 String msg = null;
439 if (s.mode == SearchMode.replace) {
440 msg = tr("No match found for ''{0}''", s.text);
441 } else if (s.mode == SearchMode.add) {
442 msg = tr("Nothing added to selection by searching for ''{0}''", s.text);
443 } else if (s.mode == SearchMode.remove) {
444 msg = tr("Nothing removed from selection by searching for ''{0}''", s.text);
445 } else if (s.mode == SearchMode.in_selection) {
446 msg = tr("Nothing found in selection by searching for ''{0}''", s.text);
447 }
448 Main.map.statusLine.setHelpText(msg);
449 JOptionPane.showMessageDialog(
450 Main.parent,
451 msg,
452 tr("Warning"),
453 JOptionPane.WARNING_MESSAGE
454 );
455 } else {
456 Main.map.statusLine.setHelpText(tr("Found {0} matches", foundMatches));
457 }
458 }
459
460 public static class SearchSetting {
461 public String text;
462 public SearchMode mode;
463 public boolean caseSensitive;
464 public boolean regexSearch;
465 public boolean allElements;
466
467 public SearchSetting() {
468 this("", SearchMode.replace, false /* case insensitive */,
469 false /* no regexp */, false /* only useful primitives */);
470 }
471
472 public SearchSetting(String text, SearchMode mode, boolean caseSensitive,
473 boolean regexSearch, boolean allElements) {
474 this.caseSensitive = caseSensitive;
475 this.regexSearch = regexSearch;
476 this.allElements = allElements;
477 this.mode = mode;
478 this.text = text;
479 }
480
481 public SearchSetting(SearchSetting original) {
482 this(original.text, original.mode, original.caseSensitive,
483 original.regexSearch, original.allElements);
484 }
485
486 @Override
487 public String toString() {
488 String cs = caseSensitive ?
489 /*case sensitive*/ trc("search", "CS") :
490 /*case insensitive*/ trc("search", "CI");
491 String rx = regexSearch ? (", " +
492 /*regex search*/ trc("search", "RX")) : "";
493 String all = allElements ? (", " +
494 /*all elements*/ trc("search", "A")) : "";
495 return "\"" + text + "\" (" + cs + rx + all + ", " + mode + ")";
496 }
497
498 @Override
499 public boolean equals(Object other) {
500 if(!(other instanceof SearchSetting))
501 return false;
502 SearchSetting o = (SearchSetting) other;
503 return (o.caseSensitive == this.caseSensitive
504 && o.regexSearch == this.regexSearch
505 && o.allElements == this.allElements
506 && o.mode.equals(this.mode)
507 && o.text.equals(this.text));
508 }
509
510 @Override
511 public int hashCode() {
512 return text.hashCode();
513 }
514
515 public static SearchSetting readFromString(String s) {
516 if (s.length() == 0)
517 return null;
518
519 SearchSetting result = new SearchSetting();
520
521 int index = 1;
522
523 result.mode = SearchMode.fromCode(s.charAt(0));
524 if (result.mode == null) {
525 result.mode = SearchMode.replace;
526 index = 0;
527 }
528
529 while (index < s.length()) {
530 if (s.charAt(index) == 'C') {
531 result.caseSensitive = true;
532 } else if (s.charAt(index) == 'R') {
533 result.regexSearch = true;
534 } else if (s.charAt(index) == 'A') {
535 result.allElements = true;
536 } else if (s.charAt(index) == ' ') {
537 break;
538 } else {
539 System.out.println("Unknown char in SearchSettings: " + s);
540 break;
541 }
542 index++;
543 }
544
545 if (index < s.length() && s.charAt(index) == ' ') {
546 index++;
547 }
548
549 result.text = s.substring(index);
550
551 return result;
552 }
553
554 public String writeToString() {
555 if (text == null || text.length() == 0)
556 return "";
557
558 StringBuilder result = new StringBuilder();
559 result.append(mode.getCode());
560 if (caseSensitive) {
561 result.append('C');
562 }
563 if (regexSearch) {
564 result.append('R');
565 }
566 if (allElements) {
567 result.append('A');
568 }
569 result.append(' ');
570 result.append(text);
571 return result.toString();
572 }
573 }
574
575 /**
576 * Refreshes the enabled state
577 *
578 */
579 @Override
580 protected void updateEnabledState() {
581 setEnabled(getEditLayer() != null);
582 }
583
584 public List<ActionParameter<?>> getActionParameters() {
585 return Collections.<ActionParameter<?>>singletonList(new SearchSettingsActionParameter(SEARCH_EXPRESSION));
586 }
587}
Note: See TracBrowser for help on using the repository browser.