source: josm/trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPresetSearchDialog.java@ 3388

Last change on this file since 3388 was 3388, checked in by jttt, 14 years ago

Add tagging preset search dialog

  • Property svn:mime-type set to text/plain
File size: 14.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.tagging;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.BorderLayout;
7import java.awt.Component;
8import java.awt.Dimension;
9import java.awt.event.ActionEvent;
10import java.awt.event.ItemEvent;
11import java.awt.event.ItemListener;
12import java.awt.event.KeyAdapter;
13import java.awt.event.KeyEvent;
14import java.util.ArrayList;
15import java.util.Collection;
16import java.util.Collections;
17import java.util.EnumSet;
18import java.util.HashSet;
19import java.util.List;
20
21import javax.swing.AbstractListModel;
22import javax.swing.Action;
23import javax.swing.BoxLayout;
24import javax.swing.DefaultListCellRenderer;
25import javax.swing.Icon;
26import javax.swing.JCheckBox;
27import javax.swing.JLabel;
28import javax.swing.JList;
29import javax.swing.JPanel;
30import javax.swing.JScrollPane;
31import javax.swing.JTextField;
32import javax.swing.event.DocumentEvent;
33import javax.swing.event.DocumentListener;
34
35import org.openstreetmap.josm.Main;
36import org.openstreetmap.josm.data.osm.Node;
37import org.openstreetmap.josm.data.osm.OsmPrimitive;
38import org.openstreetmap.josm.data.osm.Relation;
39import org.openstreetmap.josm.data.osm.Way;
40import org.openstreetmap.josm.gui.ExtendedDialog;
41import org.openstreetmap.josm.gui.preferences.TaggingPresetPreference;
42import org.openstreetmap.josm.gui.tagging.TaggingPreset.Check;
43import org.openstreetmap.josm.gui.tagging.TaggingPreset.Combo;
44import org.openstreetmap.josm.gui.tagging.TaggingPreset.Item;
45import org.openstreetmap.josm.gui.tagging.TaggingPreset.Key;
46import org.openstreetmap.josm.gui.tagging.TaggingPreset.PresetType;
47import org.openstreetmap.josm.gui.tagging.TaggingPreset.Role;
48import org.openstreetmap.josm.gui.tagging.TaggingPreset.Roles;
49import org.openstreetmap.josm.gui.tagging.TaggingPreset.Text;
50
51public class TaggingPresetSearchDialog extends ExtendedDialog {
52
53 private int CLASSIFICATION_NAME_MATCH = 300;
54 private int CLASSIFICATION_GROUP_MATCH = 200;
55 private int CLASSIFICATION_TAGS_MATCH = 100;
56
57 private static class ResultListCellRenderer extends DefaultListCellRenderer {
58 @Override
59 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
60 boolean cellHasFocus) {
61 JLabel result = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
62 TaggingPreset tp = (TaggingPreset)value;
63 result.setText(tp.getName());
64 result.setIcon((Icon) tp.getValue(Action.SMALL_ICON));
65 return result;
66 }
67 }
68
69 private static class ResultListModel extends AbstractListModel {
70
71 private List<PresetClasification> presets = new ArrayList<PresetClasification>();
72
73 public void setPresets(List<PresetClasification> presets) {
74 this.presets = presets;
75 fireContentsChanged(this, 0, Integer.MAX_VALUE);
76 }
77
78 public List<PresetClasification> getPresets() {
79 return presets;
80 }
81
82 @Override
83 public Object getElementAt(int index) {
84 return presets.get(index).preset;
85 }
86
87 @Override
88 public int getSize() {
89 return presets.size();
90 }
91
92 }
93
94 private static class PresetClasification implements Comparable<PresetClasification> {
95 public final TaggingPreset preset;
96 public int classification;
97 private final Collection<String> groups = new HashSet<String>();
98 private final Collection<String> names = new HashSet<String>();
99 private final Collection<String> tags = new HashSet<String>();
100
101 PresetClasification(TaggingPreset preset) {
102 this.preset = preset;
103 TaggingPreset group = preset.group;
104 while (group != null) {
105 for (String word: group.getLocaleName().toLowerCase().split("\\s")) {
106 groups.add(word);
107 }
108 group = group.group;
109 }
110 for (String word: preset.getLocaleName().toLowerCase().split("\\s")) {
111 names.add(word);
112 }
113 for (Item item: preset.data) {
114 if (item instanceof Check) {
115 tags.add(((Check)item).key.toLowerCase());
116 } else if (item instanceof Combo) {
117 // Should combo values also be added?
118 tags.add(((Combo)item).key);
119 } else if (item instanceof Key) {
120 tags.add(((Key) item).key);
121 tags.add(((Key) item).value);
122 } else if (item instanceof Text) {
123 tags.add(((Text) item).key);
124 } else if (item instanceof Roles) {
125 for (Role role: ((Roles) item).roles) {
126 tags.add(role.key);
127 }
128 }
129 }
130 }
131
132 private int isMatching(Collection<String> values, String[] searchString) {
133 int sum = 0;
134 for (String word: searchString) {
135 boolean found = false;
136 boolean foundFirst = false;
137 for (String value: values) {
138 int index = value.indexOf(word);
139 if (index == 0) {
140 foundFirst = true;
141 break;
142 } else if (index > 0) {
143 found = true;
144 }
145 }
146 if (foundFirst) {
147 sum += 2;
148 } else if (found) {
149 sum += 1;
150 } else
151 return 0;
152 }
153 return sum;
154 }
155
156 int isMatchingGroup(String[] words) {
157 return isMatching(groups, words);
158 }
159
160 int isMatchingName(String[] words) {
161 return isMatching(names, words);
162 }
163
164 int isMatchingTags(String[] words) {
165 return isMatching(tags, words);
166 }
167
168 @Override
169 public int compareTo(PresetClasification o) {
170 int result = o.classification - classification;
171 if (result == 0)
172 return preset.getName().compareTo(o.preset.getName());
173 else
174 return result;
175 }
176 }
177
178
179 private JTextField edSearchText;
180 private JList lsResult;
181 private JCheckBox ckOnlyApplicable;
182 private JCheckBox ckSearchInTags;
183 private final EnumSet<PresetType> typesInSelection = EnumSet.noneOf(PresetType.class);
184 private final List<PresetClasification> classifications = new ArrayList<PresetClasification>();
185 private ResultListModel lsResultModel = new ResultListModel();
186
187 public TaggingPresetSearchDialog(Component parent) {
188 super(parent, tr("Presets"), new String[] {tr("Select"), tr("Cancel")});
189 getTypesInSelection();
190
191 for (TaggingPreset preset: TaggingPresetPreference.taggingPresets) {
192 if (preset instanceof TaggingPresetSeparator || preset instanceof TaggingPresetMenu) {
193 continue;
194 }
195
196 classifications.add(new PresetClasification(preset));
197 }
198
199 build();
200 filterPresets("");
201 }
202
203 private void build() {
204 JPanel content = new JPanel();
205 content.setLayout(new BorderLayout());
206
207 edSearchText = new JTextField();
208 edSearchText.getDocument().addDocumentListener(new DocumentListener() {
209
210 @Override
211 public void removeUpdate(DocumentEvent e) {
212 filterPresets(edSearchText.getText());
213 }
214
215 @Override
216 public void insertUpdate(DocumentEvent e) {
217 filterPresets(edSearchText.getText());
218
219 }
220
221 @Override
222 public void changedUpdate(DocumentEvent e) {
223 filterPresets(edSearchText.getText());
224
225 }
226 });
227 edSearchText.addKeyListener(new KeyAdapter() {
228 @Override
229 public void keyPressed(KeyEvent e) {
230 switch (e.getKeyCode()) {
231 case KeyEvent.VK_DOWN:
232 selectPreset(lsResult.getSelectedIndex() + 1);
233 break;
234 case KeyEvent.VK_UP:
235 selectPreset(lsResult.getSelectedIndex() - 1);
236 break;
237 case KeyEvent.VK_PAGE_DOWN:
238 selectPreset(lsResult.getSelectedIndex() + 10);
239 break;
240 case KeyEvent.VK_PAGE_UP:
241 selectPreset(lsResult.getSelectedIndex() - 10);
242 break;
243 case KeyEvent.VK_HOME:
244 selectPreset(0);
245 break;
246 case KeyEvent.VK_END:
247 selectPreset(lsResultModel.getSize());
248 break;
249 }
250 }
251 });
252 content.add(edSearchText, BorderLayout.NORTH);
253
254 lsResult = new JList();
255 lsResult.setModel(lsResultModel);
256 lsResult.setCellRenderer(new ResultListCellRenderer());
257 content.add(new JScrollPane(lsResult), BorderLayout.CENTER);
258
259 JPanel pnChecks = new JPanel();
260 pnChecks.setLayout(new BoxLayout(pnChecks, BoxLayout.Y_AXIS));
261
262 ckOnlyApplicable = new JCheckBox();
263 ckOnlyApplicable.setText(tr("Show only applicable to selection"));
264 pnChecks.add(ckOnlyApplicable);
265
266 if (typesInSelection.isEmpty()) {
267 ckOnlyApplicable.setSelected(false);
268 ckOnlyApplicable.setEnabled(false);
269 } else {
270 ckOnlyApplicable.setSelected(true);
271 ckOnlyApplicable.addItemListener(new ItemListener() {
272 @Override
273 public void itemStateChanged(ItemEvent e) {
274 filterPresets(edSearchText.getText());
275 }
276 });
277 }
278
279 ckSearchInTags = new JCheckBox();
280 ckSearchInTags.setText(tr("Search in tags"));
281 ckSearchInTags.addItemListener(new ItemListener() {
282 @Override
283 public void itemStateChanged(ItemEvent e) {
284 filterPresets(edSearchText.getText());
285 }
286 });
287 pnChecks.add(ckSearchInTags);
288
289 content.add(pnChecks, BorderLayout.SOUTH);
290
291 content.setPreferredSize(new Dimension(400, 300));
292 setContent(content);
293 }
294
295 private void selectPreset(int newIndex) {
296 if (newIndex < 0) {
297 newIndex = 0;
298 }
299 if (newIndex > lsResultModel.getSize() - 1) {
300 newIndex = lsResultModel.getSize() - 1;
301 }
302 lsResult.setSelectedIndex(newIndex);
303 lsResult.ensureIndexIsVisible(newIndex);
304 }
305
306 /**
307 * Search expression can be in form: "group1/group2/name" where names can contain multiple words
308 *
309 * When groups are given,
310 *
311 *
312 * @param text
313 */
314 private void filterPresets(String text) {
315 //TODO search also in keys/values
316 //TODO Favorites
317 text = text.toLowerCase();
318
319 String[] groupWords;
320 String[] nameWords;
321
322 if (text.contains("/")) {
323 groupWords = text.substring(0, text.lastIndexOf('/')).split("[\\s/]");
324 nameWords = text.substring(text.indexOf('/') + 1).split("\\s");
325 } else {
326 groupWords = null;
327 nameWords = text.split("\\s");
328 }
329
330 boolean onlyApplicable = ckOnlyApplicable.isSelected();
331 boolean inTags = ckSearchInTags.isSelected();
332
333 List<PresetClasification> result = new ArrayList<PresetClasification>();
334 PRESET_LOOP:
335 for (PresetClasification presetClasification: classifications) {
336 TaggingPreset preset = presetClasification.preset;
337 presetClasification.classification = 0;
338
339 if (onlyApplicable && preset.types != null) {
340 boolean found = false;
341 for (PresetType type: preset.types) {
342 if (typesInSelection.contains(type)) {
343 found = true;
344 break;
345 }
346 }
347 if (!found) {
348 continue;
349 }
350 }
351
352
353
354 if (groupWords != null && presetClasification.isMatchingGroup(groupWords) == 0) {
355 continue PRESET_LOOP;
356 }
357
358 int matchName = presetClasification.isMatchingName(nameWords);
359
360 if (matchName == 0) {
361 if (groupWords == null) {
362 int groupMatch = presetClasification.isMatchingGroup(nameWords);
363 if (groupMatch > 0) {
364 presetClasification.classification = CLASSIFICATION_GROUP_MATCH + groupMatch;
365 }
366 }
367 if (presetClasification.classification == 0 && inTags) {
368 int tagsMatch = presetClasification.isMatchingTags(nameWords);
369 if (tagsMatch > 0) {
370 presetClasification.classification = CLASSIFICATION_TAGS_MATCH + tagsMatch;
371 }
372 }
373 } else {
374 presetClasification.classification = CLASSIFICATION_NAME_MATCH + matchName;
375 }
376
377 if (presetClasification.classification > 0) {
378 result.add(presetClasification);
379 }
380 }
381
382 Collections.sort(result);
383 lsResultModel.setPresets(result);
384 if (!buttons.isEmpty()) {
385 buttons.get(0).setEnabled(!result.isEmpty());
386 }
387 }
388
389
390 private void getTypesInSelection() {
391 for (OsmPrimitive primitive: Main.main.getCurrentDataSet().getSelected()) {
392 if (primitive instanceof Node) {
393 typesInSelection.add(PresetType.NODE);
394 } else if (primitive instanceof Way) {
395 typesInSelection.add(PresetType.WAY);
396 if (((Way)primitive).isClosed()) {
397 typesInSelection.add(PresetType.CLOSEDWAY);
398 }
399 } else if (primitive instanceof Relation) {
400 typesInSelection.add(PresetType.RELATION);
401 }
402 }
403 }
404
405 @Override
406 protected void buttonAction(int buttonIndex, ActionEvent evt) {
407 super.buttonAction(buttonIndex, evt);
408 if (buttonIndex == 0) {
409 int selectPreset = lsResult.getSelectedIndex();
410 if (selectPreset == -1) {
411 selectPreset = 0;
412 }
413 lsResultModel.getPresets().get(selectPreset).preset.actionPerformed(null);
414 }
415 }
416
417}
Note: See TracBrowser for help on using the repository browser.